DEV Community

Cover image for Online Food Ordering App (4)
Bertrand Masabo
Bertrand Masabo

Posted on

Online Food Ordering App (4)

Photo by abillion on Unsplash



Hi, Welcome back!


In today's post we are going to implement the place order functionality, the admin account, and a sample version of our restaurant's menu. At the end of this post, a customer should be able to successfully place an order.

Project steps

  1. Backend - Project Setup
  2. Backend - Authentication
    1. Backend - Authentication - Signup
    2. Backend - Authentication - Login and Logout
  3. Backend - Place order 📌
  4. Backend - View orders list and view a specific order
  5. Backend - Update order
  6. Frontend - Authentication
  7. Frontend - Place order, view orders list, and view order details



Let's begin with creating the admin account. The admin account will have access to functionalities such as accepting orders placed by customers, blacklisting/whitelisting users, creating staff accounts, and creating menus among other things. Since this account will have access to sensitive information, we cannot just create an endpoint for it. We need to create a script that will create this account by skipping the signup process.

We also need a way to differentiate the users of our app by their roles namely customer, admin, and staff.


Customer refers to a user who will download our app from the Google play store to place orders.

Admin refers to the owner or manager of Gourmet restaurant. He/she should be able to create menus of dishes, create and remove staff accounts, and manage orders.

Staff refers to an employee in the restaurant who will be created by the admin or manager. In case the manager is not there, the staff account should also be able to manage orders as the staff on duty.

Let's begin by creating the roles. We will need to modify a little bit the sign up process we created in the previous posts to make sure a user who signs up is identified as a customer by default.

  • Create a new branch called ft-place-order off our main branch.

  • Create a src/utils/roles.js file and paste the following code inside:

carbon

  • Update Valid signup should return 201 test case in test/authentication.js to check if a signed-up user is a customer like this:

carbon (1)

  • Update Valid login should return 200 test case in test/authentication_login.js to check if a logged-in user is a customer like this:

carbon (2)

  • Update src/database/models/User.js and add the role field like this:

carbon (3)

  • Create a new migration to add the role field on the User model by running the following command in your terminal npx sequelize-cli migration:generate --name add-role-to-user
  • Update the newly created src/database/migrations/**-add-role-to-user.js file to look like this:

carbon (4)

  • Update src/controllers/authentication.js to add the role of customer on sign up like this:

carbon (5)

Now run your tests and they should all pass. If you were to inspect the user created in the database, you should see that the user has a role of customer. This means that every user who signs up will be given a role of customer automatically. Awesome!


Admin account


Let's now create our admin account starting with the tests.

  • Create a tests/authentication_admin.js file and paste the following inside:

carbon (6)

In the test case above we are checking if given the correct admin credentials, the admin can login successfully.

At this point this test case should fail because we haven't yet created the admin account.

Let's now create a script that will create the admin account and make the test case above pass.

  • Create a src/database/scripts/adminScript.js file and paste the following code inside:

carbon (7)

In the code above we created a function called createAdmin that will first hash our plain-text admin password then call the findOrCreate method on the User model. findOrCreate method as the name suggests will first try to find if a record exists in the database, if it is found it will return its instance, if it doesn't exist then it creates a new record. we used this method because we want to be running our script automatically after each production build. If we were to use the create method, it would create the record the first time but would throw an error the second time as we would be trying to create a record that already exists.

Lastly we call createAdmin function and export it so that when we will execute this file, it will call this createAdmin function. Cool!

  • Update .env file and add the ADMIN_PHONE and ADMIN_PASSWORD environment variables. Let's now create a command to run our script.
  • Update package.json and include the script to create the admin account in pretest and heroku-postbuild commands. This way our admin account will be created before running our tests and after the production build respectively.

carbon (8)

Now run your tests again and they should all pass. Great!


Place order


At this point we need to start thinking about what kind of information we should show to customers and what information to expect when they place orders.

We are going to create 4 additional models namely: Menu, Item, Order, and Contents.

Menu will refer to a category such as Breakfast, Lunch, Dinner, Drinks, etc.

Item will refer to the actual dish or drink such as Cheese Burger, Coke Diet, Orange Juice, etc.

Order will refer to the orders placed by customers and will contain details such as the total amount, order status, user Id, etc.

Lastly, Contents will contain each item details for a specific order.

Regarding model relations or associations, we need to link Order model with User model by adding a foreign key of userId to the Order model. We also need to link Order model with Contents model by adding a foreign key of orderId to the Contents model. And lastly we need to link Menu model with Item model by adding a foreign key of MenuId to the Item model.

Great! Now that we have an idea of the structure of our new models and associations, let's start implementing the place order feature.

As always, we will begin by writing our tests.

  • Create a tests/orders.test.js file and paste the following code:

carbon (8)

  • Update src/utils/messages.js and add the new messages:

carbon

  • Create a new model called Menu with the following command npx sequelize-cli model:generate --name Menu --attributes name:string
  • Create another model called Item with npx sequelize-cli model:generate --name Item --attributes name:string,description:string,cost:decimal,size:string,image:string
  • Create a new migration to add the menuId field to our Item model by running npx sequelize-cli migration:generate --name add-menuId-to-item
  • Update the newly created src/database/migrations/**-add-menuId-to-item.js migration to look like the following:

carbon (1)

  • Update src/database/models/item.js to add the relation/association between Item and Menu:

carbon (2)

  • Update src/database/models/menu.js to add the One-to-many association between Item and Menu:

carbon (3)

  • Create another model called Order with npx sequelize-cli model:generate --name Order --attributes total:decimal,status:string,paymentId:string
  • Create another model called Contents with npx sequelize-cli model:generate --name Contents --attributes itemId:integer,itemName:string,cost:decimal,quantity:integer
  • Create a new migration to add the orderId field to our Contents model by running npx sequelize-cli migration:generate --name add-orderId-to-contents
  • Update the newly created src/database/migrations/**-add-orderId-to-contents.js migration to look like the following:

carbon (4)

  • Create a new migration to add the userId field to our Order model by running npx sequelize-cli migration:generate --name add-userId-to-order
  • Update the newly created src/database/migrations/**-add-userId-to-order.js migration to look like the following:

carbon (5)

  • Update src/database/models/order.js to add the association between Order and Contents and between Order and User:

carbon (6)

  • Update src/database/models/user.js to add the one-to-many association between User and Order:

carbon (7)

Let us now create our validations for place order.

  • Create a src/validations/orders.js file and paste the following inside:

carbon (9)

Do not forget to export the createErrorMessages function from src/validations/authentication.js

  • Create a new src/middlewares/orders.js file and paste the following inside:

carbon (10)

Before we create the controller and route for placing orders, let's think about how a customer will place an order.

In the Gourmet mobile app, the customer will be presented with a menu that has a list of items to choose from. When the customer taps on the add button, the item's id, name, cost, and quantity will be added to their cart. Subsequent addition of the same item will increase the item's quantity and cost. On checkout we will use the cart's items to calculate the total amount of the order and when the customer pays for the order, we will include the paymentId for reference.

The following image shows a sample of the request's body which will be send to the server when a customer places an order:

carbon (12)

The order is for one double cheese burger and two diet cokes.

The items inside the contents array is what we will save in our Contents model. And if we remember, we defined an association that will ensure an item has an orderId. We need a way to add an orderId to each item in the order's contents.

Let us create a function that will take our contents array and an orderId then add that orderId to each item inside the contents array.

  • Update src/helpers/misc.js and add the parseOrderContents function:

carbon (11)

  • Update src/services/services.js and add the saveManyRows function:

carbon (13)

The bulkCreate method unlike create, allows us to create multiple records at the same time.


We are now ready to create the controller and use these functions we created above.

  • Create a new src/controllers/orders.js file and paste the following:

carbon (14)

In the placeOrder method we destructure the request's body to reveal total, contents, and paymentId. Then, we create our order Object will will have the total, paymentId, a default status of pending, and the userId. The userId's value is handed to us by the authentication middleware function checkUserToken through req.userData.id. we then save our order record then use the returned record's id to add it to each item in the contents array by calling the parseOrderContents helper function. We then call saveManyRows function to save each item in the Contents model.

Let us now create the place order route and use the controller we just created.

  • Create a src/routes/ordersRoutes.js file and paste the following inside:

carbon (15)

  • Update a src/routes/index.js file and add the orders router:

carbon (16)



Now run your tests and they should all pass.

And if you check the records in Orders and Contents tables in the database, you should see that our data is saved.


Gourmet Menu

One way to create the menu of our restaurant would be to create admin endpoints for creating, viewing, updating, and deleting the menu but for the sake of keeping things simple we will not do that. Instead, we are going to create organized data of our menu that we will insert directly in the database (seeds). When we are done with this series you can implement the above endpoints for managing the menu as the admin on your own since we will have covered all the concepts to do it.

Cool, let us create our seeds.

We are going to create a seed for creating 3 menus, namely Breakfast, Lunch/Dinner, and Drinks. We are going to create another seed for creating items in each menu.

  • Run npx sequelize-cli seed:generate --name menus command in your project root
  • Update the newly created src/database/seeders/**-menus.js to look like this:

carbon (17)

  • Run npx sequelize-cli seed:generate --name items command in your project root
  • Update the newly created src/database/seeders/**-items.js to look like this:

carbon (18)

Now let us update the scripts section in package.json to create a command that we will use to create the seeds.

  • Update scripts in package.json to add the seed command and to include the seed command on the pretest command:

carbon (19)



Now we can add our new environments variables ADMIN_PHONE and ADMIN_PASSWORD to Travis and Heroku environments then commit our changes to GitHub, open a PR and merge it as we have done before.


And that's it for today!

In the next post we are going to look at how to fetch the list of orders and how to fetch a specific order's details. We will do this from the perspective of both the admin and the customer.


Thank you for reading!


See you in the next one!


The code in this post can be found here

Top comments (0)