Originally published at coreycleary.me. This is a cross-post from my content blog. I publish new content every week or two, and you can sign up to my newsletter if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.
In a Node REST API, there are multiple different layers where you could conceivably put request validation, the code where you verify that the request is "valid" and has the correct fields, checks that those fields are the expected types, etc.
In a layered API that roughly follows a web tier, service tier, and data tier structure, it makes more sense to do the request validation in the web tier, since it is the one that deals with the HTTP request. If the request is invalid, it doesn't make sense to call all the other layers of code. (Note: typically you will have some kind of model or query validation "further down" the stack, but that is a different type of validation than what we're talking about here.)
But the web tier is made up of middleware and controllers/router handlers. Which of these should you put your request validation in? With all the different types of logic in a REST API, knowing what code goes where can be confusing with lots of conflicting information out there.
For a recap on the difference between middleware and controllers, check out this post.
Best place
I think the conclusive place to put request validation is in the middleware. You usually want to bail as early as possible if you are dealing with an invalid request, as opposed to letting code seep "further down" to execute. Controllers/route handlers are closest to middleware - so it may not seem like a big deal to do the validation there - but it's still "one layer" deeper than just bailing early from the middleware.
You also keep your controllers thinner and cleaner by keeping request validation out of them, and you end up having better separation of concerns. Middleware is responsible for the validation, and controllers are responsible for routing the request where it needs to go after it's been validated.
You can also better reuse the validation logic in other routes if it's separated into middleware rather than baked into a controller.
Below is pseudo-code for what this would look like:
const app = express()
// the schema to use for validating the request
const orderSchema = {
type: 'object',
required: ['total', 'items'],
properties: {
total: {
type: 'number',
minimum: 0.5,
},
items: {
type: 'array',
uniqueItems: true,
items: {
type: 'object',
properties: {
itemId: {
type: 'integer'
},
quantity: {
type: 'integer'
}
}
}
}
}
};
app.post(
'/order', // route name/definition
validate({ body: orderSchema }), // validation middleware
createOrderController // controller
)
In this case, validate()
is a JSON Schema middleware library function, and that Schema is defined for an order. If the request that the REST API receives is invalid - for example, maybe it's missing the total
field - then the request will stop and return, skipping the controller.
Summary
Next time you are setting up request validation, put it into a middleware function before your controllers/route handlers. This way the code will exit as early as possible and you won't spend valuable server resource time and compute power, and will return a response to the client as quickly as possible.
Love JavaScript but still getting tripped up by unit testing, architecture, etc? I publish articles on JavaScript and Node every 1-2 weeks, so if you want to receive all new articles directly to your inbox, here's that link again to subscribe to my newsletter!