#express
    #node
    #server-side
    #middleware

Using Express Middleware for Input Validation

What is middleware?

Middleware, what is that?

In this post, I'll be referring to middleware specifically in terms of server-side development.

At a very high-level, middleware acts as a middle layer between an incoming HTTP request, and the request handler that ultimately makes use of it.

During that interaction in the middle of the request, middleware can augment a request, validate it, reject it, or anything that needs to happen before the request reaches its final destination.

Middleware can do some real useful things, like

  • Changing the request and response objects
  • Performing code prior to calling the next middleware function
  • End the request-response cycle

The last two are powerful, and can help us do things like

  • Ensuring a user is already authenticated when their request comes in via checking that a valid JSON web token exists on the request payload
  • Ensuring a valid cookie in the request itself

and it can do all of these things before the request gets propagated any further in the server.

Middleware is a very common concept in server frameworks, and Express makes it extra easy to make use of them!

In this post, we'll be using middleware to validate submitted posts against a /post endpoint.

You can follow along with the project I've setup on Repl for this post.


Middleware in Express

Express is a web frame for building minimal (as well as heavy-duty) servers in Node.

An Express application essentially boils down to being a powerful series of middleware, route handlers, and function calls — you can see so here on Express' middleware page.

Any time that you're registering an endpoint to be handled by Express, with a app.use(...) or app.get(...) etc. in your Express application, you're essentially telling Express you want that function to execute during that endpoint call.

For example, with

app.get('
`/post`, (request, response) => {
  response.json('I received a request for a post!');
});

you're setting up a middleware to handle any GET request to the endpoint /post and to respond with a message of "I received a request for this post".


Chaining middleware in Express

When you're registering these endpoints and their handlers, you can even stack them and use multiple of them.

Take our previous example, you can modify it slightly to add another middleware on-top of it.

app.get('/post`, (request, response, next) => {
  console.("I received a request for a post at middleware one!");
  next();
},
(request, response) => {
  console.("I received a request for a post at middleware two, and I'm ending the request!");
  response.json("I received a request for a post!");
});

In this new example, you can see we've registered 2 middleware functions to execute during a request on the /post endpoint.

The first simply logs to the console and calls the next middleware, and the following one logs to the console and responds to the request, thus ending the chain of middleware calls.

This parameter next is provided to every middleware in the chain of middleware attached to an endpoint. Any middleware that is registered to an endpoint and does not end the request-response cycle must call the next() function in order for the next middleware.

Otherwise, if next() isn't called, then subsequent middleware won't be executed and the request can hang endlessly.


Validating input with Express middlware

Now that we know what middleware is and some of the heavy-lifting is can do for us, let's make use of it in a real-world scenario.

Let's say we have an Express application server that listens on a path /post. This endpoint returns a list of posts at /post when hit with a GET request.

This same endpoint allows you to submit a new post with a POST request — but not before validating the post you're trying to add, to make sure it adheres to it's strict standards.

The submitted post must have provide the following non-empty fields

  • title
  • author
  • description

and any submitted post that fails this will be rejected with a 400. Any post that passes this will be added to list of available posts and will be responded with a 200 success code.

Let's code this up; we'll be following along with the Repl here as it already spins up a server for us and has a registered endpoint for /path.


Adding the validation middleware

In the provided Repl, we'll navigate to the api directory and open postRouter.ts.

Next, we'll add a handler for POST type requests, and we'll attach a middleware for validating the submission, and the middleware that actually inserts the new post submission into our available list.

Adding the validation middleware will look like

PostRouter.post('/', (request, response, next) => {
  const post = request.body;

  if (!post.title) {
    return response.status(400).json('Your post must include a title');
  }

  else if (!post.author) {
    return response.status(400).json('Your post must include an author');
  }

  else if (!post.description) {
    return response.status(400).json('Your post must include a description');
  }

  // Otherwise, by this step, the submission passes validation
  // so call the next middleware
  next();
}

In the above code, if any of the 3 required fields weren't provided, we reject the request with a 400 error and tell the requester what field was missed.

If the request wasn't rejected and the next function is called, then the request moves onto the next middleware.

Now, onto the tail end of the above, let's add the code that adds the validated post to the list of existing posts.

(request, response) => {
  const post = request.body;

  // By this step, the post should be validation
  // so let's add it to the list and respond with the new list
  availablePosts.push(post);
  response.status(200).json(availablePosts);
}

And there we have it.

We can test the above with a tool like Postman, since we need to submit a POST request with a JSON body, but it should validate and automatically add any new valid post.

We can also use cURL to test the server, and an invalid post submission will look something like

curl --location --request POST 'https://validationexample.cjativa1.repl.co/post' \
--header 'Content-Type: application/json' \
--data-raw '{
    "title": "This is a invalid post!",
    "description": "It has no author :("
}'

You can refer to the same Repl for the completed code in action!


zerochass

practical and goal-oriented resources

learn by doing. enjoy what you do.