Online Food Ordering App (4)
Tags: express, node, react, reactnative
Published: 2021-01-24 19:47:14

*<span>Photo by <a href="https://unsplash.com/@abillion?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">abillion</a> on <a href="https://unsplash.com/s/photos/food-mobile-app?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>*

<br/>
Hi, Welcome back!<br/><br/>
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.<br/><br/>

### Project steps
1. [Backend - Project Setup](https://dev.to/the22mastermind/online-food-ordering-app-1-594j)
2. Backend - Authentication
  1. [Backend - Authentication - Signup](https://dev.to/the22mastermind/online-food-ordering-app-2-28po)
  2. [Backend - Authentication - Login and Logout](https://dev.to/the22mastermind/online-food-ordering-app-3-1g8h)
3. [Backend - Place order]() :pushpin:
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]()

<br/>
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.<br/>

We also need a way to differentiate the users of our app by their roles namely **customer**, **admin**, and **staff**.
<br/>
**Customer** refers to a user who will download our app from the Google play store to place orders.<br/>

**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.<br/>

**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.<br/><br/>

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.<br/><br/>
- Create a new branch called `ft-place-order` off our main branch.<br/>

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

![carbon](https://dev-to-uploads.s3.amazonaws.com/i/8n165yb7q0ba5r6pojfv.png)
 
<br/>
- 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)](https://dev-to-uploads.s3.amazonaws.com/i/q03052hob3lfz6vzpqus.png)
 
<br/>
- 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)](https://dev-to-uploads.s3.amazonaws.com/i/dcs6pw43opp4uvyj5tsp.png)
 
<br/>
- Update `src/database/models/User.js` and add the role field like this:

![carbon (3)](https://dev-to-uploads.s3.amazonaws.com/i/m7j4mgn2945sopsdl63p.png)
 
<br/>
- 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`
<br/>
- Update the newly created `src/database/migrations/**-add-role-to-user.js` file to look like this:

![carbon (4)](https://dev-to-uploads.s3.amazonaws.com/i/8fyzeh4t4miqrtoopwhx.png)
 
<br/>
- Update `src/controllers/authentication.js` to add the role of **customer** on sign up like this:

![carbon (5)](https://dev-to-uploads.s3.amazonaws.com/i/kztzdvwpszjxvrno30qq.png)
 
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!
<br/>
**Admin account**
<br/>
Let's now create our admin account starting with the tests.
<br/>
- Create a `tests/authentication_admin.js` file and paste the following inside:

![carbon (6)](https://dev-to-uploads.s3.amazonaws.com/i/eyc94atusar5oyaabemz.png)
 
In the test case above we are checking if given the correct admin credentials, the admin can login successfully.<br/>
At this point this test case should fail because we haven't yet created the admin account.<br/>
Let's now create a script that will create the admin account and make the test case above pass.<br/>
- Create a `src/database/scripts/adminScript.js` file and paste the following code inside:

![carbon (7)](https://dev-to-uploads.s3.amazonaws.com/i/1vi1gb087uckv13q2jte.png)
 
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.<br/>
Lastly we call `createAdmin` function and export it so that when we will execute this file, it will call this createAdmin function. Cool!
<br/>
- Update `.env` file and add the `ADMIN_PHONE` and `ADMIN_PASSWORD` environment variables.
<br/>
Let's now create a command to run our script.
<br/>
- 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)](https://dev-to-uploads.s3.amazonaws.com/i/aa3ptxxy8aoax2sg88eh.png)
 
Now run your tests again and they should all pass. Great!
<br/>
***
**Place order**
<br/>
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. <br/>
We are going to create 4 additional models namely: Menu, Item, Order, and Contents.<br/>
**Menu** will refer to a category such as Breakfast, Lunch, Dinner, Drinks, etc.<br/>
**Item** will refer to the actual dish or drink such as Cheese Burger, Coke Diet, Orange Juice, etc.<br/>
**Order** will refer to the orders placed by customers and will contain details such as the total amount, order status, user Id, etc.<br/>
Lastly, **Contents** will contain each item details for a specific order.<br/>
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.<br/>
Great! Now that we have an idea of the structure of our new models and associations, let's start implementing the place order feature.<br/>
As always, we will begin by writing our tests.<br/>
- Create a `tests/orders.test.js` file and paste the following code:

![carbon (8)](https://dev-to-uploads.s3.amazonaws.com/i/ztngqcewlbdcx5c6em07.png)
 
<br/>
- Update `src/utils/messages.js` and add the new messages:

![carbon](https://dev-to-uploads.s3.amazonaws.com/i/xda3gjbyoc0yveyygojl.png)
 
<br/>
- Create a new model called **Menu** with the following command `npx sequelize-cli model:generate --name Menu --attributes name:string`
<br/>
- Create another model called **Item** with `npx sequelize-cli model:generate --name Item --attributes name:string,description:string,cost:decimal,size:string,image:string`
<br/>
- 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`
<br/>
- Update the newly created `src/database/migrations/**-add-menuId-to-item.js` migration to look like the following:

![carbon (1)](https://dev-to-uploads.s3.amazonaws.com/i/3ruaj33nvr96v3jmrq1f.png)
 
<br/>
- Update `src/database/models/item.js` to add the relation/association between `Item` and `Menu`:

![carbon (2)](https://dev-to-uploads.s3.amazonaws.com/i/tx2462hry0jdmfis1yrf.png)
 
<br/>
- Update `src/database/models/menu.js` to add the One-to-many association between `Item` and `Menu`:

![carbon (3)](https://dev-to-uploads.s3.amazonaws.com/i/n3aylppdzw0yrfhg7n57.png)
 
<br/>
- Create another model called **Order** with `npx sequelize-cli model:generate --name Order --attributes total:decimal,status:string,paymentId:string`
<br/>
- Create another model called **Contents** with `npx sequelize-cli model:generate --name Contents --attributes itemId:integer,itemName:string,cost:decimal,quantity:integer`
<br/>
- 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`
<br/>
- Update the newly created `src/database/migrations/**-add-orderId-to-contents.js` migration to look like the following:

![carbon (4)](https://dev-to-uploads.s3.amazonaws.com/i/3wc5b79qvzxxg773kxzx.png)
 
<br/>
- 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`
<br/>
- Update the newly created `src/database/migrations/**-add-userId-to-order.js` migration to look like the following:

![carbon (5)](https://dev-to-uploads.s3.amazonaws.com/i/jmabwy0d7885t4xcy5rt.png)

<br/>
- Update `src/database/models/order.js` to add the association between `Order` and `Contents` and between `Order` and `User`:

![carbon (6)](https://dev-to-uploads.s3.amazonaws.com/i/z5gk9ds7z99buv2ss1hn.png)

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

![carbon (7)](https://dev-to-uploads.s3.amazonaws.com/i/yqou15ef9sffds7q3zvo.png)

Let us now create our validations for place order.<br/>
- Create a `src/validations/orders.js` file and paste the following inside:

![carbon (9)](https://dev-to-uploads.s3.amazonaws.com/i/ugnqazcgp225xv40cbse.png)

**Do not forget to export the `createErrorMessages` function from `src/validations/authentication.js`**<br/>
- Create a new `src/middlewares/orders.js` file and paste the following inside:

![carbon (10)](https://dev-to-uploads.s3.amazonaws.com/i/dwsfiresaer6n1ikfvux.png)

Before we create the controller and route for placing orders, let's think about how a customer will place an order.<br/>
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.<br/>
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)](https://dev-to-uploads.s3.amazonaws.com/i/5bjf279xwmg5qt39unxn.png)

The order is for one double cheese burger and two diet cokes.<br/>
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.<br/>
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.<br/>
- Update `src/helpers/misc.js` and add the `parseOrderContents` function:

![carbon (11)](https://dev-to-uploads.s3.amazonaws.com/i/1i5akr7kyd46aylfyfe1.png)

- Update `src/services/services.js` and add the `saveManyRows` function:
 
![carbon (13)](https://dev-to-uploads.s3.amazonaws.com/i/xqc3etvh6hr4mhghbkdw.png)

The `bulkCreate` method unlike `create`, allows us to create multiple records at the same time.
<br/>
We are now ready to create the controller and use these functions we created above.<br/>
- Create a new `src/controllers/orders.js` file and paste the following:

![carbon (14)](https://dev-to-uploads.s3.amazonaws.com/i/vmo9sfork40xf3gm41qe.png)

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.<br/>
Let us now create the place order route and use the controller we just created.<br/>
- Create a `src/routes/ordersRoutes.js` file and paste the following inside:

![carbon (15)](https://dev-to-uploads.s3.amazonaws.com/i/0278t0xlsx0bo2j6ordv.png)
 
- Update a `src/routes/index.js` file and add the orders router:

![carbon (16)](https://dev-to-uploads.s3.amazonaws.com/i/li2925mla80oswyj4vw4.png)

<br/>
Now run your tests and they should all pass.<br/>
And if you check the records in Orders and Contents tables in the database, you should see that our data is saved.
<br/>
***
**Gourmet Menu**<br/>
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.<br/>
Cool, let us create our seeds.<br/>
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.
<br/>
- Run `npx sequelize-cli seed:generate --name menus` command in your project root
<br/>
- Update the newly created `src/database/seeders/**-menus.js` to look like this:

![carbon (17)](https://dev-to-uploads.s3.amazonaws.com/i/xq2gs5mcsv8wj3j453nn.png)
 
<br/>
- Run `npx sequelize-cli seed:generate --name items` command in your project root
<br/>
- Update the newly created `src/database/seeders/**-items.js` to look like this:

![carbon (18)](https://dev-to-uploads.s3.amazonaws.com/i/59debitjfhe8tf5vyesx.png)
 
Now let us update the scripts section in `package.json` to create a command that we will use to create the seeds.
<br/>
- Update scripts in `package.json` to add the `seed` command and to include the seed command on the `pretest` command:

![carbon (19)](https://dev-to-uploads.s3.amazonaws.com/i/m1t6ad3qnkn970tcv424.png)

<br/>
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.<br/><br/>
And that's it for today!<br/>
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.
<br/>
Thank you for reading!
<br/>
See you in the next one!
<br/>
The code in this post can be found [here](https://github.com/the22mastermind/gourmet-api)
<br/>