# Hands-On Guide: Building Efficient APIs with Node.js - Part 3

In our previous article, we delved into database integration, testing with Postman and the essential considerations when building a robust API. For comprehensive understanding, I recommend [Hands-On Guide: Building Efficient APIs with Node.js - Part 1](https://sanjayynithin.hashnode.dev/restapi-part-1) before proceeding with this article. However, if you're primarily interested in deployment, feel free to skip the preceding sections.


### Authenticate with JSON Web Tokens (JWT)

![JWT Token Gatekeepers: Unleashing the Power of Secure Validation in Your  Application](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc835d78e-255b-4ee5-9f56-656432563368_837x348.png align="left")

#### **Why do we need authentication?**

1. REST APIs operate in a stateless manner, meaning they don't establish sessions upon login.
    
2. Consequently, anyone could potentially access our data. To counter this, user authentication is imperative.
    
3. Upon a user's login, a token is generated, which has a finite validity period. Subsequent API requests are authenticated using this token.
    

#### **JWT Integration**

1. **Installing jsonwebtoken Package**
    
    1. Start by installing the [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) package using the command
        
        ```javascript
        npm install jsonwebtoken --save
        ```
        
2. **Setting up environment variable**
    
    1. Create an environment variable ‘*JWT\_KEY’*. This can be done via the ‘*nodemon.json’* file or [dotenv](https://www.npmjs.com/package/dotenv) package or by setting up the variable in your terminal.
        
3. **Generating token upon login**
    
    1. First, we’ll see if the given email id exists. If not, return a “404 Not Found” error.
        
    2. If email id exists, compare the passwords. If passwords don’t match, return a “401 Auth Failed” error.
        
    3. If passwords match, sign an object containing the user's email and ‘\_id’. This token is essential for subsequent requests.
        
        ***Note:*** *It is recommended to hash and compare passwords using the* [*bcrypt*](https://www.npmjs.com/package/bcrypt) *package for enhanced security.*
        
        ```javascript
        const jwt = require('jsonwebtoken');
        router.post('/login', (req, res) => {
            // Extract email and password from the request body
            const email = req.body.email;
            const password = req.body.password;
        
            // Search for users with the given email in the database
            Users.find({ email: email }).exec()
                .then(docs => {
                    // If no users are found with the given email
                    if (docs.length === 0) {
                        res.status(404).json({
                            error: 'User Not Found'
                        });
                    } else {
                        // If a user is found with the given email, check the password
                        if (docs[0].password === password) {
                            // Generate a JWT token with user's email and ID
                            const token = jwt.sign({
                                email: docs[0].email,
                                id: docs[0]._id
                            },
                            // Use the JWT_KEY stored in environment variables
                            process.env.JWT_KEY,
                            {
                                expiresIn: "1h" // Token expiration time: 1 hour
                            });
        
                            // Respond with success status and the generated token
                            res.status(200).json({
                                message: 'Auth successful',
                                token: token
                            });
                        }
                        else {
                            // If password doesn't match, respond with unauthorized status
                            res.status(401).json({
                                message: 'Auth failed'
                            });
                        }
                    }
                })
                .catch(err => {
                    // If an error occurs during the database query, respond with server error status
                    res.status(500).json({
                        error: err
                    });
                });
        });
        ```
        

1. **Authenticate using jwt**
    
    1. Upon making a login request, copy the response token and decode it using tools like [jwt.io](https://jwt.io/) or [DevToys](https://devtoys.app/).
        
        *api/middlware/authenticate.js*
        
        ```javascript
        const jwt = require("jsonwebtoken");
        
        // Middleware function to handle JWT authentication
        module.exports = (req, res, next) => {
            try {
                // Extract the token from the "Authorization" header
                const token = req.headers.authorization.split(" ")[1]; // Assuming the header format is "Bearer token"
                console.log(token);
        
                // Verify the token using the JWT secret key stored in the environment variable
                const decoded = jwt.verify(token, process.env.JWT_KEY);
        
                // Attach the decoded user data to the request object for further use
                req.userData = decoded;
        
                // Call the next middleware or route handler
                next();
            } catch (err) {
                // If token verification fails, return a 401 Unauthorized response
                res.status(401).json({
                    message: 'Auth failed'
                });
            }
        };
        ```
        
2. **Adding Middleware for authentication**
    
    1. This function can be imported and used as middleware in our routes. The ‘*authenticate’* middleware is executed before executing the route functions.
        
    2. Similarly, ‘*authenticate’* middleware can be used in all routes except for those that generate tokens (i.e., login and signup routes).
        
        ```javascript
        const authenticate = require('../middleware/authenticate');
        
        // Define a route that requires authentication using the 'authenticate' middleware
        router.get('/', authenticate, (req, res) => {
            // Query the Users collection to retrieve user data
            Users.find()
                .select('_id name email password') // Select specific fields to return
                .exec()
                .then(users => {
                    // Construct a response containing user data and additional metadata
                    res.status(200).json({
                        users: users.map(user => ({
                            _id: user._id,
                            name: user.name,
                            email: user.email,
                            // Note: Sending password in the response is not recommended for security reasons
                            password: user.password,
                            request: {
                                type: 'GET',
                                url: 'http://localhost:3000/users/' + user._id
                            }
                        }))
                    });
                })
                .catch(err => {
                    // Handle errors by sending a 500 Internal Server Error response
                    res.status(500).json({
                        error: err
                    });
                });
        });
        ```
        
3. **Authorizing User Access**
    
    For deleting a user or patching a user, an additional layer of security can be added by comparing the decoded token's email with the email associated with the passed `_id`. Consider the following example:
    

```javascript
router.delete('/:id', authenticate, (req, res) => {
    // Find the user by their ID
    Users.findById(req.params.id).exec()
        .then(user => {
            console.log(req.userData);
            // Check if the authenticated user's email matches the user's email
            if (user.email !== req.userData.email) {
                // If authentication fails, send a 401 Unauthorized response
                res.status(401).json({
                    message: 'Auth failed'
                });
            } else {
                // If authentication passes, delete the user by their ID
                Users.findByIdAndDelete(req.params.id).exec()
                    .then(deletedUser => {
                        // Send a success response with the deleted user's data
                        res.status(200).json({
                            message: 'User deleted successfully!',
                            user: deletedUser
                        });
                    })
                    .catch(err => {
                        // Handle errors during deletion and send a 500 Internal Server Error response
                        res.status(500).json({
                            error: err
                        });
                    });
            }
        })
        .catch(err => {
            // Handle errors during user retrieval and send a 500 Internal Server Error response
            res.status(500).json({
                error: err
            });
        });
});
```


### Testing with Postman

Install [Postman](https://www.postman.com/) if you haven’t already or use the [Web](https://web.postman.co/) version.

1. Login with your credentials and copy the token.
    

![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F96f48355-b4d1-4f54-907b-420239f13b08_1425x623.png align="left")

1. Go to Headers, set up ‘*Authorization’* header and use the token in Bearer Format.
    
    ```http
    Authorization: Bearer <token>
    ```
    

![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43ba8212-bc83-4451-9ef1-eebee0846b5e_889x130.png align="left")

1. Let’s test various scenarios:
    
    1. Requesting with token:
        
        ![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73dc623e-cffe-4047-bb75-900665264bd6_1437x676.png align="left")
        
    2. Requesting without token:
        
        ![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2e8cc340-e854-4c3b-ac23-b652162b7ab8_1393x474.png align="left")
            

### Structuring Codebase and Implementing Best Practices

1. **Adding Authentication Middleware to Routes**
    
    1. Create a middleware file, e.g., ‘authenticate.js’, in your middleware directory.
        
    2. Implement the *‘authenticate’* middleware in this file to verify JWT tokens.
        
    3. In your route files, import the ‘*authenticate’* middleware and apply it to routes that require authentication.
        
    4. Exclude ‘*authenticate’* from the login and signup routes to ensure they are accessible without authentication.
        
2. **Creating an Environment Variable for URL**
    
    1. Define an environment variable, *URL=http://localhost:3000/*. The value can be later changed to the URL of deployment server.
        
    2. Replace occurrences of *http://localhost:3000/* in your code with *process.env.URL*.
        
3. **Adding Route Functions to Controllers (MVC):**
    
    1. Create a *controllers* directory in your project.
        
    2. Inside the *controllers* directory, create separate files for each route's controller, e.g., *Users.js*.
        
    3. In each controller file, define functions that handle the logic for specific routes (e.g., `getUsers`, `deleteUser`).
        
    4. Import the controller functions in your route files and use them for corresponding routes.
        
    5. This separation follows the Model-View-Controller (MVC) pattern and helps keep your code organized.
        

By following these steps, you'll achieve a structured and organized codebase, utilize environment variables for configuration, and ensure secure routes using authentication middleware. Customize the steps based on your application's specific requirements and coding practices.


### Deploying our API with Render

![Render: Shooting for a cloud-native app hosting platform you never outgrow  – Intellyx – The Digital Transformation Experts – Analysts](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F835df80c-3f1f-4886-a931-612e1d8c39e7_1200x628.png align="left")

[Render](https://render.com/) is a cloud platform that provides hosting and deployment services for web applications, websites, and APIs. Render has a free tier plan of 500 minutes of build time per month, which is sufficient for starting.

**Note:** Make sure that you’ve committed your API to Github and also, sign in to Render using your Github account for streamlined deployment. Now, let’s deploy our API.

1. Create a *Web Server* under *Dashboard* and connect your repository. Add the following *Build and Start command*.
    
    ![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F490cbd5f-35fa-406c-a14b-fc19fbdb7d94_1640x489.png align="left")
    
2. Configure Environment variables.
    
    ![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbed2a453-dec5-49db-ae75-5b994361d87d_1494x354.png align="left")
    
3. Create a web server and wait for it to build.
    
4. Our API is now live!
    

![](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F216797ae-b27a-4d00-b43a-2d9091d907d8_543x165.png align="left")


### What to do next?

1. Test the live API using Postman for all the API Endpoints and verify if they are working as expected.
    
2. Consider implementing OTP Verification using [nodemailer](https://nodemailer.com/) and password hashing using [bcrypt](https://www.npmjs.com/package/bcrypt).
    

Congratulations! You have successfully added authentication and deployed your API. What are your preferred areas of interest or topics you'd like to explore?
