avatarMarcos Henrique da Silva

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

6344

Abstract

an>.<span class="hljs-property">email</span>); <span class="hljs-keyword">if</span> (user) { res.<span class="hljs-title function_">status</span>(<span class="hljs-number">400</span>).<span class="hljs-title function_">send</span>({<span class="hljs-attr">error</span>: <span class="hljs-string">User email already exists</span>}); } <span class="hljs-keyword">else</span> { <span class="hljs-title function_">next</span>(); } }</pre></div><div id="5d28"><pre><span class="hljs-keyword">async</span> <span class="hljs-title function_">validateUserExists</span>(<span class="hljs-params">req: express.Request, res: express.Response, next: express.NextFunction</span>) { <span class="hljs-keyword">const</span> userService = <span class="hljs-title class_">UsersService</span>.<span class="hljs-title function_">getInstance</span>(); <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> userService.<span class="hljs-title function_">readById</span>(req.<span class="hljs-property">params</span>.<span class="hljs-property">userId</span>); <span class="hljs-keyword">if</span> (user) { <span class="hljs-title function_">next</span>(); } <span class="hljs-keyword">else</span> { res.<span class="hljs-title function_">status</span>(<span class="hljs-number">404</span>).<span class="hljs-title function_">send</span>({<span class="hljs-attr">error</span>: <span class="hljs-string">User <span class="hljs-subst">${req.params.userId}</span> not found</span>}); } }</pre></div><div id="d2c3"><pre><span class="hljs-keyword">async</span> extractUserId(req: express.Request, res: express.Response, <span class="hljs-built_in">next</span>: express.NextFunction) { req.body.<span class="hljs-built_in">id</span> = req.params.userId; <span class="hljs-built_in">next</span>(); } }</pre></div><p id="f1e7">We created some functions that contains some business logic to our application such as to control if an user exists or not, if we are not duplicating any email and on. Those rules are generic and just to make us to have an example on how to use middleware</p><blockquote id="b12f"><p>In a <b>real world scenario</b>, you should map the business rules with your client and create validations that will make sense to each case of use.</p></blockquote><p id="56e9">Now we are ready to add the middleware to our routes file. Let’s open again the <code>user.routes.config.ts</code> file and update it with the following:</p><div id="f025"><pre><span class="hljs-keyword">import</span> {CommonRoutesConfig, configureRoutes} <span class="hljs-keyword">from</span> <span class="hljs-string">'../common/common.routes.config'</span>; <span class="hljs-keyword">import</span> {UsersController} <span class="hljs-keyword">from</span> <span class="hljs-string">'./controllers/users.controller'</span>; <span class="hljs-keyword">import</span> {UsersMiddleware} <span class="hljs-keyword">from</span> <span class="hljs-string">'./middlewares/users.middleware'</span>; <span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;</pre></div><div id="5d75"><pre><span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UsersRoutes</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">CommonRoutesConfig</span> <span class="hljs-title">implements</span> <span class="hljs-title">configureRoutes</span></span>{ constructor(app: express.<span class="hljs-type">Application</span>) { <span class="hljs-keyword">super</span>(app, '<span class="hljs-type">UsersRoute</span>'); <span class="hljs-keyword">this</span>.configureRoutes(); }</pre></div><div id="e170"><pre><span class="hljs-title function_">configureRoutes</span>(<span class="hljs-params"></span>) { <span class="hljs-keyword">const</span> usersController = <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsersController</span>(); <span class="hljs-keyword">const</span> usersMiddleware = <span class="hljs-title class_">UsersMiddleware</span>.<span class="hljs-title function_">getInstance</span>(); <span class="hljs-variable language_">this</span>.<span class="hljs-property">app</span>.<span class="hljs-title function_">get</span>(<span class="hljs-string">/users</span>, [ usersController.<span class="hljs-property">listUsers</span> ]);</pre></div><div id="7263"><pre>this.app.post(/users, [ usersMiddleware.validateRequiredCreateUserBodyFields, usersMiddleware.validateSameEmailDoesntExist, usersController.createUser ])<span class="hljs-comment">;</span></pre></div><div id="f24b"><pre>this.app.<span class="hljs-keyword">put</span>(/users/:userId, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.<span class="hljs-keyword">put</span> ]);</pre></div><div id="245b"><pre><span class="hljs-keyword">this</span>.app.patch(<span class="hljs-string">/users/:userId</span>, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.patch ]);</pre></div><div id="9bf2"><pre><span class="hljs-keyword">this</span>.app.<span class="hljs-keyword">delete</span>(<span class="hljs-string">/users/:userId</span>, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.removeUser ]); <span class="hljs-keyword">this</span>.app.get(<span class="hljs-string">/users/:userId</span>, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.getUserById ]); }</pre></div><div id="2879"><pre>}<span class="hljs-keyword">import</span> {CommonRoutesConfig, configureRoutes} <span class="hljs-keyword">from</span> <span class="hljs-string">'../common/common.routes.config'</span>; <span class="hljs-keyword">import</span> {UsersController} <span class="hljs-keyword">from</span> <span class="hljs-string">'./controllers/u

Options

sers.controller'</span>; <span class="hljs-keyword">import</span> {UsersMiddleware} <span class="hljs-keyword">from</span> <span class="hljs-string">'./middlewares/users.middleware'</span>; <span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;</pre></div><div id="fe45"><pre><span class="hljs-keyword">export</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UsersRoutes</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">CommonRoutesConfig</span> <span class="hljs-title">implements</span> <span class="hljs-title">configureRoutes</span></span>{ constructor(app: express.<span class="hljs-type">Application</span>) { <span class="hljs-keyword">super</span>(app, '<span class="hljs-type">UsersRoute</span>'); <span class="hljs-keyword">this</span>.configureRoutes(); }</pre></div><div id="4307"><pre><span class="hljs-title function_">configureRoutes</span>(<span class="hljs-params"></span>) { <span class="hljs-keyword">const</span> usersController = <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsersController</span>(); <span class="hljs-keyword">const</span> usersMiddleware = <span class="hljs-title class_">UsersMiddleware</span>.<span class="hljs-title function_">getInstance</span>();</pre></div><div id="1871"><pre> <span class="hljs-keyword">this</span>.app.<span class="hljs-keyword">get</span>(/users, [ usersController.listUsers ]);</pre></div><div id="ae14"><pre>this.app.post(/users, [ usersMiddleware.validateRequiredCreateUserBodyFields, usersMiddleware.validateSameEmailDoesntExist, usersController.createUser ])<span class="hljs-comment">;</span></pre></div><div id="be17"><pre>this.app.<span class="hljs-keyword">put</span>(/users/:userId, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.<span class="hljs-keyword">put</span> ]);</pre></div><div id="9ba8"><pre><span class="hljs-keyword">this</span>.app.patch(<span class="hljs-string">/users/:userId</span>, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.patch ]);</pre></div><div id="6155"><pre><span class="hljs-keyword">this</span>.app.<span class="hljs-keyword">delete</span>(<span class="hljs-string">/users/:userId</span>, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.removeUser ]);</pre></div><div id="db40"><pre> <span class="hljs-keyword">this</span>.app.<span class="hljs-keyword">get</span>(/users/:userId, [ usersMiddleware.validateUserExists, usersMiddleware.extractUserId, usersController.getUserById ]); }</pre></div><div id="385e"><pre>}</pre></div><p id="4804">Now we can check that before calling the usersController, we are passing more functions that will handle specific rules that we want.</p><p id="5b66">Remember that our middleware <b>always</b> call the <code>next</code> function when everything is ok, which will make ExpressJS to call the next function or our end function that we are using as Controller. If anything went wrong we are using the response object already to return an error to the client.</p><p id="94bf">And that’s it all we need to make things work. We can test now running <code>npm start</code> and send the requests but now considering what we’ve added as a middleware. A simple test to do is to create twice the same user with same email.</p><p id="d702">Create a new user:</p><div id="0e22"><pre>curl --<span class="hljs-keyword">location</span> <span class="hljs-title">--request</span> POST 'localhost:<span class="hljs-number">3000</span>/users'
--header 'Content-<span class="hljs-keyword">Type</span>: application/json'
--data-raw '{ <span class="hljs-string">"email"</span>:<span class="hljs-string">"[email protected]"</span>, <span class="hljs-string">"password"</span>:<span class="hljs-string">"amazingMediumArticle"</span> }'</pre></div><p id="b5c7">Feel free to test your brand new middleware. With the next article we are going to put e2e tests and we will be able to start testing and coding without much need of Postman or Insomnia clients since we can do it using our own project.</p><p id="6075">Thanks for reading and see you at the <a href="https://readmedium.com/another-expressjs-api-tutorial-for-2020-part-07-your-first-tests-f2f6a7ec9ebf">next article</a>!</p><div id="db33" class="link-block"> <a href="https://readmedium.com/another-expressjs-api-tutorial-for-2020-part-07-your-first-tests-f2f6a7ec9ebf"> <div> <div> <h2>Another ExpressJS API tutorial for 2020, part 07 — Your first tests</h2> <div><h3>We already configured some routes, some services and middlewares, now it is time to introduce some testings to our API</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*eWYm3Dm7s0t8lLWYRW4pow.jpeg)"></div> </div> </div> </a> </div><p id="711f">tips:</p><ul><li>the full code of this article is <a href="https://github.com/makinhs/expressjs-api-tutorial/tree/006-configurando-middlewares">HERE</a></li><li>don’t forget to use a nice app to test your API such as <a href="https://www.postman.com/">Postman</a> or <a href="https://insomnia.rest/">Insomnia</a></li><li>I know you still didn’t… Read the ExpressJS <a href="https://expressjs.com/en/4x/api.html">documentation</a>!</li><li>Book suggestion: <a href="https://amzn.to/3dgcWwI">Clean Code</a> and again <a href="https://amzn.to/2WGb1f5">Design Patterns</a></li><li>Brazilian? PT-BR Book suggestion: <a href="https://amzn.to/2wnMel1">Clean Code</a> and <a href="https://amzn.to/2U6uXWI">Design Patterns</a></li><li>Complete project is <a href="https://github.com/makinhs/expressjs-api-tutorial">here</a>! (Includes even the non existing articles yet)</li></ul></article></body>

Another ExpressJS API tutorial for 2021, part 06 — Middleware

We are finally reaching the half of the series of articles about how to develop an API using ExpressJS and Typescript. Today we are going to talk about Middleware

Hey there, if you are completely lost here, then I recommend to go back to the first article about how to create an API using ExpressJS and Typescript.

Just as a quick overview, here is the series of articles that we are writing:

So… let’s review what we have so far:

  • A configured environment for Typescript
  • Routes configuration
  • Controllers
  • Services
  • A temporary database

Today we are going to add and introduce Middleware to our API, using the users routes as an example. Make sure to grab the last code article before going further. The code is HERE.

Ok, ok, but what are middleware? By ExpressJS documentation is basically:

Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

Remember that when we configured our routes we used an Array and we were using only the Controller on it. The code was like the following:

this.app.get(`/users`, [
            usersController.listUsers
        ]);

What we want to do now is to create some steps before the final destination that is the controller in order to maintain short functions at our code and to have a good organisation. We will be also being able to re-use middleware during our coding.

So, with all said, should we start?

Let’s create a folder called middleware inside the users folder and a file called users.middleware.ts with the following code:

import express from 'express';
import {UsersService} from '../services/user.services';
export class UsersMiddleware {
    private static instance: UsersMiddleware;
static getInstance() {
        if (!UsersMiddleware.instance) {
            UsersMiddleware.instance = new UsersMiddleware();
        }
        return UsersMiddleware.instance;
    }
validateRequiredCreateUserBodyFields(req: express.Request, res: express.Response, next: express.NextFunction) {
        if (req.body && req.body.email && req.body.password) {
            next();
        } else {
            res.status(400).send({error: `Missing required fields email and password`});
        }
    }
async validateSameEmailDoesntExist(req: express.Request, res: express.Response, next: express.NextFunction) {
        const userService = UsersService.getInstance();
        const user = await userService.getByEmail(req.body.email);
        if (user) {
            res.status(400).send({error: `User email already exists`});
        } else {
            next();
        }
    }
async validateUserExists(req: express.Request, res: express.Response, next: express.NextFunction) {
        const userService = UsersService.getInstance();
        const user = await userService.readById(req.params.userId);
        if (user) {
            next();
        } else {
            res.status(404).send({error: `User ${req.params.userId} not found`});
        }
    }
async extractUserId(req: express.Request, res: express.Response, next: express.NextFunction) {
        req.body.id = req.params.userId;
        next();
    }
}

We created some functions that contains some business logic to our application such as to control if an user exists or not, if we are not duplicating any email and on. Those rules are generic and just to make us to have an example on how to use middleware

In a real world scenario, you should map the business rules with your client and create validations that will make sense to each case of use.

Now we are ready to add the middleware to our routes file. Let’s open again the user.routes.config.ts file and update it with the following:

import {CommonRoutesConfig, configureRoutes} from '../common/common.routes.config';
import {UsersController} from './controllers/users.controller';
import {UsersMiddleware} from './middlewares/users.middleware';
import express from 'express';
export class UsersRoutes extends CommonRoutesConfig implements configureRoutes{
    constructor(app: express.Application) {
        super(app, 'UsersRoute');
        this.configureRoutes();
    }
configureRoutes() {
        const usersController = new UsersController();
        const usersMiddleware = UsersMiddleware.getInstance();
        this.app.get(`/users`, [
            usersController.listUsers
        ]);
this.app.post(`/users`, [
            usersMiddleware.validateRequiredCreateUserBodyFields,
            usersMiddleware.validateSameEmailDoesntExist,
            usersController.createUser
        ]);
this.app.put(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.put
        ]);
this.app.patch(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.patch
        ]);
this.app.delete(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.removeUser
        ]);
        this.app.get(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.getUserById
        ]);
    }
}import {CommonRoutesConfig, configureRoutes} from '../common/common.routes.config';
import {UsersController} from './controllers/users.controller';
import {UsersMiddleware} from './middlewares/users.middleware';
import express from 'express';
export class UsersRoutes extends CommonRoutesConfig implements configureRoutes{
    constructor(app: express.Application) {
        super(app, 'UsersRoute');
        this.configureRoutes();
    }
configureRoutes() {
        const usersController = new UsersController();
        const usersMiddleware = UsersMiddleware.getInstance();
        this.app.get(`/users`, [
            usersController.listUsers
        ]);
this.app.post(`/users`, [
            usersMiddleware.validateRequiredCreateUserBodyFields,
            usersMiddleware.validateSameEmailDoesntExist,
            usersController.createUser
        ]);
this.app.put(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.put
        ]);
this.app.patch(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.patch
        ]);
this.app.delete(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.removeUser
        ]);
        this.app.get(`/users/:userId`, [
            usersMiddleware.validateUserExists,
            usersMiddleware.extractUserId,
            usersController.getUserById
        ]);
    }
}

Now we can check that before calling the usersController, we are passing more functions that will handle specific rules that we want.

Remember that our middleware always call the next function when everything is ok, which will make ExpressJS to call the next function or our end function that we are using as Controller. If anything went wrong we are using the response object already to return an error to the client.

And that’s it all we need to make things work. We can test now running npm start and send the requests but now considering what we’ve added as a middleware. A simple test to do is to create twice the same user with same email.

Create a new user:

curl --location --request POST 'localhost:3000/users' \
--header 'Content-Type: application/json' \
--data-raw '{
 "email":"[email protected]",
 "password":"amazingMediumArticle"
}'

Feel free to test your brand new middleware. With the next article we are going to put e2e tests and we will be able to start testing and coding without much need of Postman or Insomnia clients since we can do it using our own project.

Thanks for reading and see you at the next article!

tips:

Expressjs
Middleware
Typescript
Tutorial
Nodejs
Recommended from ReadMedium