Online Food Ordering App (2)
Tags: express, node, react, reactnative
Published: 2020-12-27 17:02:41

*<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>*


***

Welcome back!


Today we are going to start implementing authentication for the backend of our app "Gourmet".


In this post we will implement the **sign up** and **verify** endpoints.


***


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


***


### 2. Backend - Authentication


**Sign up**

For users to register on our app, we will require their first name, last name, phone number, address, and password. If the values provided are valid, we will send a OTP (One-Time-Password/Passcode) via SMS to their phone number which they can use to confirm their registration.


Following the TDD approach, we are first going to write our tests then we will implement validations, middlewares, routes, controllers and finally we will configure Sequelize to be able to save data in the database.


Before we begin, ensure you have installed and configured [Postgres](https://www.postgresql.org/) properly as it's the database we are going to be using. Check out [this article](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-16-04) on how to install it on Ubuntu.


Our signup task is going to be made up of 2 sub-tasks, one for signing up and another for confirming the user's registration. Let's begin with the first one.


- Ensure you are on your `main` branch then run `git pull origin main` to make sure your local branch is up to date with the remote branch
- Run `git checkout -b ft-authentication` to create a new branch for today's task


As we build our API there are things that we will need often and to avoid repeating ourselves it's good practice to structure our code for re-usability. That being said, create a new directory called `utils` inside `src`. Create two new files `statusCodes.js` and `messages.js` inside utils.


- Open `src/utils/statusCodes.js` and paste the following inside:

![carbon (3)](https://dev-to-uploads.s3.amazonaws.com/i/qw20f1vz9kb3aqw9lbz1.png)
 
 
These are all the HTTP status codes that our API is going to use.

- Open `src/utils/messages.js` and paste the following inside:

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

This file will contain all the response messages that our API will be returning to the client apps on top of status codes and other data.


Now let's write our tests.


- Create a file called `authentication.test.js` in tests directory and paste the following inside:

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


In this file, we import our express app along with our assertion libraries (chai and chai-http) and our status codes and messages we defined above. We then define a base URL for our authentication routes and we initialize chai to be able to test http apps. Learn more about [chai here](https://www.chaijs.com/).

We then define a `SIGN UP` suite to hold our 5 test cases. In the first test case, we are testing for when a user submits an empty request (tries to signup without providing any data), what response he/she should get. Notice the use of one of our status code and messages we defined earlier.

In the second test case, we are testing for when a user submits an invalid phone number. Notice the missing `+` sign on the phone number. The phone number must be in a valid international format since we will use it to send the OTP.

In the third test case, we are testing for when a user submits any other value apart from the required ones (firstName, lastName, phoneNumber, address, and password). Notice the email property.

In the fourth test case, we are testing for when a user submits valid values conforming to validation rules that we will define next. In this case, we expect a successful response that contains a status code of `201`, a `account created` message, a JWT token that the user can use to authenticate for subsequent requests, and a data object containing details of the user. Notice how we expect the user's account status to be false since he/she has not yet verified it. Finally, we retrieve the token in a variable called `userToken` that we will use in other test cases when verifying the user's account.

In the fifth test case, we are testing for when a user tries to signup more than once using the same phone number.

At this point if you run the tests they will fail apart from `Server initialization test` which is exactly what we want.


Next up is to write code to make our tests pass.


- Create the following directories `config`, `controllers`, `database`, `helpers`, `middlewares`, `routes`, `services`, and `validations` inside `src` directory.

- Create a new file called `authentication.js` inside validations directory and paste the following code inside:

![carbon (4)](https://dev-to-uploads.s3.amazonaws.com/i/3rxojadr8q8n4wts9fom.png)
 
We will use this file for authentication validation. In the code above, we start by importing a library called `Joi` and our response messages we defined in utils. Joi is a powerful data validator for Javascript and I personally like it because it is robust and easy to use. Check out its docs [here](https://joi.dev/).

We created a function `createErrorMessages` to help us in - you guessed it - create validation error messages. The function takes `error type` and `empty, min, max, and pattern` custom messages as parameters and depending on the type of the error we assign a custom message. This function returns an object of error types and their messages.

We use the second function `signup` to define a schema of values that we want users to submit when signing up. Notice the use of Regular Expressions to enforce validation rules. If you're familiar with RegEx, it's pretty straight forward as our use-case is not too complex.

Finally we call Joi's built-in method `validate` on our schema and pass in a data object i.e. req.body and some options to return all errors at once and to prevent other values not defined in our schema. Check out [Joi API](https://joi.dev/api/) for more details and advanced use-cases.

In case of errors, our signup validation function will return an `errors` object containing a `details` property. This details property is an array containing all the error messages. We need a way to extract and use the contents of this details property.


- Create a `misc.js` file inside `helpers` directory and paste the following code:

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

In this file we define 3 functions:

  - We will use `successResponse` and `errorResponse` to return success and error responses respectively.

  - `returnErrorMessages` checks to see if the parameter `errors` is present then destructure its details property. We then format each message in our details array to make it more readable and then we use `errorResponse` defined above to return the result of these formatted messages.

If errors is null it means our validations are passing and we continue with the execution of the request. Think of `returnErrorMessages` as a middleware.

Let's now use this `returnErrorMessages` function.


- Create a file `authentication.js` in middlewares directory and paste the following code:

![carbon (5)](https://dev-to-uploads.s3.amazonaws.com/i/ewftw0byad49xw4cu9ti.png)
  
Notice the use of `returnErrorMessages` by giving it the error object returned by our signup validation function as a parameter.

Before we implement our controller, let's update `src/helpers/misc.js` with the following:

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

Notice the additional functions: `generateToken`, `generateOTP`, and `generateHashedPassword`.

We will use `generateToken` to generate a JWT token based on the data passed in. Update your `.env` file and include the `JWT_SECRET_KEY` like `JWT_SECRET_KEY=somesecretkey`.

We will use `generateOTP` to generate a random six digit code that we will send to a user.

Finally, `generateHashedPassword` will be used to take a plain-text password, encrypt it and return a hash string which we will store in our database. For security reasons, **You should never store plain-text passwords in your database**.


Okay, let's implement our controller.

- Create a `authentication.js` file in `controllers` directory and paste the following:

![carbon (8)](https://dev-to-uploads.s3.amazonaws.com/i/offw7ff7bluhkabt29yy.png)
 
Our controller is where a request that has passed all validations and middlewares will end its journey. This where we will implement saving data in the database and sending OTP to users before returning a response to the user.

Let's implement our routes to see how it looks so far.

- Create two files `authRoutes.js` and `index.js` in routes directory.

- Paste the following inside `src/routes/authRoutes.js`:

![carbon (9)](https://dev-to-uploads.s3.amazonaws.com/i/16wvahi6atq6xjx9q8vg.png)
 
If you remember, in our tests we defined our base URL as `/api/auth/`. This means we will be able to define `/api/auth/signup`, `/api/auth/login`, and `/api/auth/logout` routes respectively.

Let's implement the parent `/api/auth/` route handler.

- Paste the following inside `src/routes/index.js`:

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

Our endpoint is almost complete. We just need to let our express app know about it.

- Update `src/server.js` to look look like this:

![carbon (11)](https://dev-to-uploads.s3.amazonaws.com/i/ere14w4m4e26u0jpyu9h.png)
 
- Run your tests again. This time around, some of them are passing.

Great Job if you managed to reach here! :tada:


Let's now implement sending OTP. When we finish, we will setup Sequelize in order to persist data in the database.


Starting with OTP implementation, we are going to use [Twilio](https://www.twilio.com/). Click [here](https://www.twilio.com/try-twilio) to create a Twilio trial account. After creating your account you should be given some credit you can use to buy numbers and send SMS in trial mode.

Trial accounts have some limitations, namely you cannot send SMS to unverified numbers. So in order to test this functionality, there are 2 options.

**Option 1**
You can upgrade your account.

**Option 2**
You can verify numbers you intend to use. Just remember to upgrade your account before going into production to allow everyone to signup.


We are going to use option 2 for now.

- Login to your Twilio account. Click on the `#` sign that says `Phone numbers` on the left panel. On the phone numbers page, click `Buy number` button and proceed to search for a number you want. Make sure to tick the SMS checkbox.

- Click on `Verified Caller IDs` then click on the red plus button to add and verify a number. Make sure to provide a valid phone number that you have access to because Twilio will send a OTP to verify it.


Once done, head back to VS Code and add the following keys in your `.env` file.

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


Let's now install the Twilio library.


- Open your terminal in your project's root dir and run `yarn add twilio`

- Create a `twilioConfig.js` file in config directory and paste the following:

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


In this file we initialize a twilio client instance that we can use throughout our app to send SMS.

Let's now use this client in our code.

- Update `src/heplers/misc.js` to look like the following:

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

The `sendOTP` function will take a phone number and a message and it will take care of sending our SMS. Let's now use this function in our controller.

- Update `src/controllers/authentication.js` like this:

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


Now run your tests again and you should get a OTP delivered to the number you specified in `TWILIO_CUSTOMER_NUMBER` env variable.


Great! Let's now implement [Sequelize](https://sequelize.org/) and save data in our database.

Since we already installed all the required sequelize library and plugins, let's start using them.

- In your terminal, navigate to `src/database` and run `npx sequelize-cli init`. This command will create the following directories and files: `config/config.json`, `models`, `migrations`, and `seeders`.

The models directory will contain our models. Think of models as tables in a database.

The migrations directory will contain migrations which are modifications made to our models. We use migrations to change the structure our 'tables'. We can do things like add/remove/rename columns, add/change constraints on columns, etc.

*Note that each time we modify the structure of our models we need to run migrations for those changes to take effect. More on this later.*

The `seeders` directory will contain data that we want to inject in the database. Use-case: Imagine you want to test the `login` functionality. since we have already implemented the signup tests and we know it works well, we can use the seeders to insert in the database valid records of users thus skipping the signup and verify tests which will make our tests run faster. We will use seeders later in this series.

The `config.json` file will contain credentials to connect to our database. We will need to modify this file and make it dynamic to avoid exposing our database credentials. Let's do it right away.

- Rename `src/database/config/config.json` to `src/database/config/config.js`

- Replace the contents inside with:

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

 - Update your `.env` file and add the keys for development and test like below:

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

Notice the different database names for development and test.

Note that for now we **don't** need to provide credentials for production in our `.env` file. The production credentials will be provided to us by heroku when we "provision" (setup) a production database.

- Replace `src/database/models/index.js` with the following:

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

 
This file will allow us to import our models dynamically by doing something like: `import models from '../database/models'` then destructure models to retrieve each model in models directory. This file also creates and exports a sequelize instance that we will be using to interact with the database.


Cool! Let's now use Sequelize to create our first model - User.

- In your terminal run `npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,phoneNumber:string,address:string`

This command will create 2 new files: `user.js` (our user model) and `**-create-user.js` (our first migration) inside models and migrations directories respectively.

- Update `package.json` to include commands to create and drop the database as well as run the migrations like:

![carbon (25)](https://dev-to-uploads.s3.amazonaws.com/i/k5qff0uy6mogmnxxgxz1.png)
 
Notice we didn't include the `pretest` command on the `test` command since our CI service does this automatically for each build.


If we were to run our migrations right now, our database would be created with just the 4 columns defined when creating our model above.

Let's update our model and add more columns and create a new migration to apply those changes.

- Update `src/database/models/user.js` like below:

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

- In your terminal run `npx sequelize-cli migration:generate --name add-password-otp-and-status-to-user` to create a new migration that will apply the new columns we added to our model.

*Tip: As migrations can become many as our app scales, it's good practice to name each migration with what it does. By looking at the name of the new migration, we would know that it add password, otp, and status columns to user model.*


- Replace the contents of `src/database/migrations/**-add-password-otp-and-status-to-user.js` with the following:

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


Check out [this link](https://sequelize.org/master/manual/migrations.html) to learn more about creating models and migrations.
 
 
If we were to run our 2 migrations now, all the 7 columns would be added to our user table.


One of the things I like about Sequelize is its nice API that allows to interact with the database without writting SQL queries like "INSERT INTO tableName VALUES(....". Oh! This API allows also to write those queries in case you wish to use them. Nice, right!


We are almost done!

- Create a `services.js` file inside services directory and paste the following:

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


We will be using this file to create functions that will use Sequelize API to CRUD the database.

`saveData` function receives a model name and obj as parameters then calls the Sequelize built-in method `create` on the model and returns the data saved in the database.

Similarly we use `findByCondition` function to find if a record exists in a table given a conditon. Check out [this link](https://sequelize.org/master/manual/model-querying-basics.html) to learn more about these built-in model methods.

As you might have guessed, we will use `findByCondition` to check if a user exists in the database and `saveData` to save the user.


Okay, let's update `src/middlewares/authentication.js` to look like the following:

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


We need to run this function after the validations and before the controller.

- Update `src/routes/authRoutes.js` to look like:

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


- Lastly, let's update our controller to use the `saveData` function we defined in our services. Update `src/controllers/authentication.js` to look like the following:


 
In the code above we added the `saveData` and lodash's `omit` and `pick` methods to choose which properties should be in the userData object returned in the response and token respectively.


That's it! Our signup endpoint is done!


Now if you run your tests, they should all pass! Nice, right!


In case you run into a timeout error, make sure to update your script's test command in `package.json` by adding a timeout flag like below:

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

This allows to extend the default Mocha's timeout of 2 seconds for each test case to 8 seconds which will give enough time to our asynchronous functions to finish executing.


***


**Verify**


After users have registered and we have sent the OTP, we need a way to verify their phone number thus confirming their account registration. 

We are going to implement **verify endpoints**, the first one will be to check if the OTP submitted by the user is correct. The second will be to re-send the OTP to the user in case there has been an issue and the user didn't receive the first OTP.


- Open `tests/authentication.js` and add the following:

![carbon (1)](https://dev-to-uploads.s3.amazonaws.com/i/46s8ygzkfgaspuvdwx6i.png)
 
In the code above, we have added test cases for the `verify` and `verify/retry` endpoints.

- In `SIGNUP` test suite, update `Valid signup should return 201` test case like this:

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


- Open `src/utils/messages.js` and add the following messages:

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


- Open `src/validations/authentication.js` and add the following:

![carbon (3)](https://dev-to-uploads.s3.amazonaws.com/i/86q0afqqvnksr3cdekr2.png) 


- Open `src/middlewares/authentication.js` and add the following:

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

  - The `validateVerifyOTP` middleware will help us to use `verifyOTP` function to validate the `otp` submitted by the user.
  - The `checkUserToken` middleware will help us to check if a request contains the Authorization header then will try to decode the token to check if the who made the request exists in our database then returns the user's data or an error. This is how we will be able to link users with their requests.
  - The `checkOTP` middleware will help us to check if the otp submitted by the user is the same as the one we sent to them via SMS.


- Open `src/services/services.js` and add the following:

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

- Open `src/controllers/authentication.js` and add the following:

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


- Open `src/routes/authRoutes.js` and add the following:

![carbon (8)](https://dev-to-uploads.s3.amazonaws.com/i/2njl6vxzubh7ebtz39a8.png)
 

Now all our tests should be passing. Let's now update our travis config file and package.json file before we commit our changes to Github.

- Update `.travis.yml` file to look like this:

![carbon (4)](https://dev-to-uploads.s3.amazonaws.com/i/7vrei7wvraozflzji50h.png)
 
 
 
We added the `services` option and `before_script` command which will tell Travis to create a postgres database called `gourmet_test` before running our tests.

- Update `package.json` to include a `heroku-postbuild` command.

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

As the name suggests, this command will run after each build. You can use it to run scripts that you want to execute before your app is deployed. Here we are using it to run our migrations automatically.


The last step is to make sure our CI service and production environments are up to date.

- Login on [Travis](https://travis-ci.com/) then open our `gourmet-api` repo then click on settings to add environments variables. Make sure to add each env variable with its value.

![carbon (1)](https://dev-to-uploads.s3.amazonaws.com/i/rprtkh6bo6c1titbddm6.png)
 
- Head back to VS Code and commit our changes to github. Open a PR on github and wait for Travis to finish building. Both the branch and PR should show a successful build.


Before we merge this PR, let's create a production database on heroku.


- On your app page on heroku, click on `Resources` tab then in the `Add-ons` search field type `postgres`. Select `Heroku Postgres` and in the confirmation modal click `Submit order form`. You should see a confirmation that the add-on `heroku-postgresql` has been added. Check out the [docs](https://devcenter.heroku.com/articles/heroku-postgresql) for more info.

- Click on `Heroku Postgres` to open it in a new tab then click on `Settings` tab then click the `View credentials` button.

You should see the credentials of our database. When you provision a database on heroku like this, it adds the `DATABASE_URL` env variable automatically on your app.

Let's now add the database credentials as env variables. Alternatively, you could use the `DATABASE_URL` variable in the `database/config/config.js` and `database/models/index.js` files.

- On your main app's settings tab, click on `Reveal config vars` button and add each credential key and its corresponding value from the database we've just created.

- **Don't forget our Twilio credentials and JWT_SECRET_KEY**

Now it's time to merge our PR which will trigger a production build on heroku.

- Head over to github and merge the PR we created earlier.

Travis should build our merge commit successfully then Heroku should build successfully as well then run our migrations.

Now you could copy the URL of your app from heroku and test the endpoints we implemented with POSTMAN or Insomnia and everything should go smoothly. Check out the links to their docs below.


Today's task was huge because we covered a lot of things. But we have laid the foundation for Sequelize, validations, and middlewares. The next endpoints are going to be fairly straightforward.


In the next post, we will implement the `login` and `logout` endpoints.


*Tip: To test your API as you build it, you should use a tool like [Postman](https://www.postman.com/) or [Insomnia](https://insomnia.rest/).*

*They are both great at designing and testing APIs and you can even do things like creating and hosting your API documentation.*

*Check out the [Postman docs](https://learning.postman.com/docs/getting-started/introduction/) and [Insomnia docs](https://support.insomnia.rest/category/9-getting-started) to learn more.*


*Note: The endpoints we implemented in this post are a bit naive. For example, we are not checking if a user's account is verified before verifying it. We should also limit requests to the endpoints that uses external resources since the billing of these resources can become a lot. Check out this [library](https://www.npmjs.com/package/express-rate-limit) to learn how to limit the number of requests. About the other issue of checking if a user's account is verified before verifying it, we can achieve this by using a simple middleware function.*


Thanks for reading and or following along!

See you in the next one!


***


You can find the code in this post [here](https://github.com/the22mastermind/gourmet-api)