Skip to content

Backend Design

Framework used: Express JS

This project uses RESTful API's for communication between client and server. Additionally, developers working on the backend are required to follow strict variable naming convention rules.

Backend Objects Documentation

The backend keeps data organized by requiring all variables ending with "Object" to match one of the following JSON structures.

Allowed JSON Structures

Any JSON object with one of the listed names must match the structure underneath

foodGroupObject

{
   foodGroupId: number, //primary key
   foodGroupName: string
}

ingredientObject

{
   foodId: number, //primary key
   foodDescription: string,
   portion?: {
      measureId: number,
      measureDescription?: string,
      amount: number
   },
   nutrition?: {
      calories: number,
      fat: number,
      cholesterol: number,
      sodium: number,
      potassium: number,
      carbohydrates: number,
      fibre: number,
      sugar: number,
      protein: number
   }
}

conversionObject

{
   measureId: number,
   measureDescription?: string,
   conversionFactorValue: number,
}

recipeObject

{
   _id: mongoose.SchemaTypes.ObjectId,
   owner: mongoose.SchemaTypes.ObjectId,
   title: string,
   description: string,
   image: string, // to be changed
   ingredients: Array<ingredientObject>, // must include portion and nutrient field 
   instructions: Array<string>,
   nutrition: {
      calories: number,
      fat: number,
      cholesterol: number,
      sodium: number,
      potassium: number,
      carbohydrates: number,
      fibre: number,
      sugar: number,
      protein: number
   }
}

userObject

{
   _id: mongoose.SchemaTypes.ObjectId,
   username: string,
   email: string,
   bio: string,
   relationship?: { // relationship with given target (usually current signed in user)
      _id: mongoose.SchemaTypes.ObjectId,
      target: mongoose.SchemaTypes.ObjectId,
      type: number // 0 = no relationship, 1 = friends, 2 = received friend request, 3 sent friend request, 4 = self
   }
}

userFolderObject

{
   _id: mongoose.SchemaTypes.ObjectId,
   folders: Array<userFolderObject | mongoose.SchemaTypes.ObjectId>,
   users: Array<userObject | mongoose.SchemaTypes.ObjectId>
}

Router Documentation

There are 4 routers the client can utilize:

  • Authentication router
  • Ingredient router
  • Recipe router
  • User router

/authentication Routes

The authentication router is used for managing a clients access to a user token

/register

Type:
   POST - Registers a new user

Expects 3 arguments in body:
   username: string
   email: string
   password: string

Route description:
   - Checks if the username or email is already registered
   - Salts and hashes the password
   - Creates a user object and saves it to the database
   - Creates JSON Web Token cookies and sends them to the client

Returns:
   - 201 user was successfully registered
   - 400 invalid or missing arguments
   - 409 username or email is already registered

/login

Type:
   POST - Logs user in

Expects 3 arguments in body:
   username: string
   password: string
   rememberMe: boolean

Route description:
   - Retrieves user data from the database
   - Verifies the password matches the hashed password
   - Creates JWT cookies and sends them to the client
   - If rememberMe is true, sets cookie max-age to 30 days

Returns:
   - 200 user verified and cookies sent
   - 400 invalid or missing arguments
   - 401 username or password is incorrect

/refresh

Type:
   POST - Issues a new user token

Expects 0 arguments in body

Route description:
   - Checks validity of refresh tokens in the client’s cookies
   - Creates and sends a new user token

Returns:
   - 200 new user token sent
   - 400 arguments were provided
   - 401 no valid refresh token was found

/logout

Type:
   POST - Logs the current user out

Expects 0 arguments in body

Route description:
   - Removes all cookies from the client’s browser

Returns:
   - 200 cookies successfully removed
   - 400 arguments were provided with this request

/ingredient Routes

The ingredient router is used to manage the clients access to any ingredient related objects inside the database

/getObject/:foodId/:measureId?/:amount?

Type:
   GET - Returns a completed ingredient object

Expects 3 arguments in params:
   foodId: number
   measureId: number (optional)
   amount: number (optional)

Route description
   - Grabs ingredientObject for foodID from the database
   - If both measureId and amount have been provided, attach nutrition field to the ingredientObject
   - Returns ingredientObject to client

Returns:
   - 200 ingredientObject returned
   - 400 invalid or missing arguments

payload: IngredientObject

/list

Type:
   GET - returns a list of ingredientObjects

Expects 4 arguments in query:
   foodDescription: string (optional)
   foodGroupId: string (optional)
   skip: number (optional, default 0)
   limit: number (optional, default 15)

Route description:
   - Collects a list of ingredientObjects from the postgres database
   - List will skip over the first {skip} number of results
   - List will be limited to {limit} number of results
   - Convert the contents of the list into an ingredientObject array

Returns:
   - 200 ingredientObject array returned
   - 400 Invalid arguments

payload: {
   ingredientObjectArray: ingredientObject[],
   count: number
}

/conversionOptions/:foodId

Type:
   GET - returns a list of conversion options for a given foodId

Expects 1 argument in params:
   foodId: number

Route description:
   - Gathers a list of all conversion types associated with the foodId

Returns:
   - 200 conversionObject array
   - 400 invalid or missing arguments

payload: conversionObject[]

/groups

Type:
   GET - returns a list of all food groups inside postgres

Expects 0 arguments in query

Method 'GET' description:
   - Gathers a list of all food groups inside the postgres database

Method 'GET' returns:
   - 200 foodGroupObject array returned
   - 400 arguments were provided with this request

payload: foodGroupObject[]

/recipe Routes

The recipe router is used to manage the clients access to any recipe related objects inside the database

/getObject/:recipeId/:includeNutrition?

Type:
   GET - returns a completed recipe object

Expects 1 argument from params:
   recipeId: integer
   includeNutrition: boolean (optional, default false)

Route description:
   - Builds a completed recipeObject using the recipeId provided
   - Checks to make sure the client has read access to the recipe
   - If includeNutrition is true, attaches the nutrition field to the recipeObject
   - Returns the competed recipe object

Returns:
   - 200 recipeObject returned
   - 400 invalid or missing arguments
   - 401 client is attempting to access a private recipeObject without an access token
   - 403 client does not have read access to recipeObject being requested

payload: recipeObject

/find

Type: 
   GET - returns a list of recipes from the database

Expects 6 arguments from query:
   category: enum["public", "friends", "personal"] (optional, default "public")
   title: string (optional)
   ingredients: number[] (optional)
   limit: number (optional, default 6)
   skip: number (optional, default 0)
   count: boolean (optional, default false)
   includeNutrition: boolean (optional, default false)
Route description:
   - Collect a list of recipeObjects based on title and ingredients provided
   - Skip the first {skip} number or recipeObjects found
   - Limit the list to {limit} number of recipeObjects
   - If {includeNutrition} is true, attach the nutrition field to each recipeObject
   - Return the list to the client
   - If {count} is true, also return the total number of recipeObjects that match search criteria

Returns:
   - 200 recipeObject array returned
   - 400 invalid or missing arguments
   - 401 client is attempting to use a category other than "all" without an access token

payload: {
   recipeObjectArray: recipeObject[], 
   count: number
}

/edit

Type:
   POST - Creates a new recipe in the database
   PUT - Makes changes to an already existing recipe in the databases

Expects 6 arguments from body:
   _id: mongoose.SchemaTypes.ObjectId (Only for PUT method)
   title: string
   description: string
   image: string
   ingredients: ingredientObject[]
   instructions: string[]

Route Description:
   - Packages arguments into a single json object
   - Checks the json object to make sure it forms a valid RecipeObject
   - If using POST method, saves the recipeObject to the database with current user as the recipe owner
   - If using PUT method, checks to make sure client has write pillages for recipe with _id provided
   - If using PUT method, replaces contents of the recipeObject in database with contents of the new recipeObject

Returns: 
   - 201 recipe was added/changed in the database
   - 400 invalid or missing arguments
   - 401 client did not provide a valid access token
   - 403 client does not have write access to the recipeObject

/user Routes

The user router is used to manage the clients access to any user related objects inside the database

/getObject/:userId?/:relationship?

Type:
   GET - return a userObject from the database

Expects 2 arguments from params:
   userId: mongoose object id (optional)
   relationship: boolean (optional, default false)

Route Description:
   - Gets a userObject based on userId provided
   - If userId is not provided, get the userObject associated with the current signed in user
   - If {relationship} is true, attach the relationship field to the userObject
   - Reminder: the relationship field represents how the userObject feels about the current user

Returns:
   - 200 userObject returned
   - 400 invalid arguments
   - 401 userId and access token missing or relationship was requested without an access token

payload: userObject

/find

Type:
   GET - return a list of users from the database

Expects 6 arguments from query:
   username: string (optional)
   email: string (optional)
   limit: number (optional, default 6)
   skip: number (optional, default 0)
   relationship: number (optional)
   count: boolean (optional, default false)

Route description:
   - Collects a list of all users in the database that contain {username} and {email}
   - If relationship field exists, only include userObjects that the current user has the given relationship with
   - Reminder: 1 = friends, 2 = received friend requests, 3 = sent friend requests
   - Skip over the first {skip} number of results found
   - Limit the list size to the {limit} number of objects
   - Return the list to the client
   - If {count} is true, return the total number of items matching search criteria alongside count

Returns: 
   - 200 userObject array returned
   - 400 missing or invalid arguments
   - 401 relationship filed exists but no access token found

payload: {
   userObjectArray: userObject[]
   count: number
}

/folder

Type: 
   GET - return a list of folders from the database

Expects 4 arguments from query:
   folderId: mongoose object id (optional)
   skip: number (optional, default 0)
   limit: number (optional, default 6)
   count: boolean (optional, default false)

Route description:
   - If folderId does not exits, Collects a list of all folders owned by the current user
   - If folderId field exists, Collect a list of all folders that have {folderId} in the parent folder field
   - Skip the first {skip} number of folderObjects
   - Limit the list to the {limit} number of folders
   - Return list to client
   - If count is true, return the number of folderObjects that meet search criteria

Returns:
   - 200 folderObject array returned
   - 400 invalid arguments
   - 401 access token could not be found

payload: {
   folderObjectArray: folderObject[]
   count: number
}

/updateAccount

Type:
   POST - change the userObject saved in the database for current user

Expects 3 arguments from body:
   username: string
   email: string
   bio: string (optional)

Route description:
   - Make sure the username or email doesn't already exist in database
   - Update usersObject associated with the signed in user inside the database

Returns:
   - 200 userObject associated with signed in user has been updated
   - 400 missing or invalid arguments
   - 401 access token could not be found

/sendFriendRequest

Type:
   POST - creates a friend request in server database

Expects 1 arguments from body:
   receiverId: mongoose object id

Route description:
   creates a friend request in the database, setting the current user as the sender and the {userId} as the receiver

Returns:
   - 201 friendRequestObject created and saved in the database
   - 400 missing or invalid arguments
   - 401 access token could not be found
   - 409 friendRequestObject or friendshipObject already exists in the database

payload: friendRequestObject

/processFriendRequest

Type:
   POST - logs user out

Expects 2 arguments from body:
   requestId: mongoose object id
   accept: boolean

Route description:
   - Checks the validity of the friend request
   - If accept is true, create a friendship object between the sender and receiver of the request
   - If accept is false, delete the friend request from the database

Returns:
   - 201 friendRequestObject has been deleted and friendObject has been created in the database
   - 204 friendRequestObject has been deleted from the database
   - 400 missing or invalid arguments
   - 401 client did not provide a valid access token
   - 403 client does not have write access to the friendRequestObject
   - 404 requestId not found in database
   - 409 the approved friendship already exists in the database

payload: friendshipObject

/deleteFriendRequest

Type:
   POST - deletes a friendship object from the database

Expects 1 argument from body:
   relationshipId: mongoose object id

Route description:
   - Deletes the friendship object from the database

Return:
   - 204 friendshipObject removed from databases
   - 400 missing or invalid arguments
   - 401 client did not provide a valid access token
   - 403 client doesn't have write access to the friendship object