Express.js’s best controller pattern
Sorry for the click-bait title, but this is in my opinion the best expressjs controller pattern.
I consider express more like a library that a framework, and maybe it is, cause there are a lot of frameworks using express, so yeah, it’s a tool for building a web-server.
It’s most important features are it’s route handling, and middleware managing. Those two powerful features are the only two things needed for building a scalable back end. Everything else is optional. Whether you use controllers or plain routes, chains of middlewares or chains of promises, it’s all up to you.
As you may know, Laravel is a 6 year old framework built over PHP and it’s one of the most famous PHP frameworks, and it’s not for nothing. It has really powerful tools that come with the basic install, and it’s super scalable. It has a robust routing/middleware/controller structure that gives every section it’s responsibilities.
It has a RouteProvider that loads the route files. It adds a prefix, a common middleware for all it’s sub-routes, and most importantly, it groups them.
In express we can do something like this:
So now we have a file called
routes/api_v1.js that has every route regarding the API. If we need to make a v2 of the API, we will just have to add another line below that one linking
/api/v2 to the
Let’s get into this routes file. We need to only link routes to controllers and it’s methods, and then the controller will take care of the middleware required for each method (if you think about it, the route does not need to know the middleware, and most importantly, it should not have the code there). The validation middleware, the authentication, the role checker, media uploads, that should belong in the controller, so that’s what we’ll make.
Using the same Laravel namespace style, if we want to place the app login route logic, we will create a
controllers/app/auth.controller.js file that will have the
login method, and that’s what will be called from the route. So using a namespace style will look like
App/AuthController@login. We use @ to specify the method name. So the route will look like
Of course we will have to wrap it into a smart function that will include the method, cause that code won’t work with vanilla expressjs.
Now that the routes are ready, we need to take care of the controllers. We suppose a magic function that will load the controllers, index them, and append/prepend it’s middleware, we named it Ctrlrs, and it’s on controllers/index.js, but we’ll talk about it later. Now I wanna get into the controllers.
We need to clarify two things. Controllers have methods, public methods, and each public method is static, and represents a route. The method login is called when there’s a request on the route /auth/login, so it get’s the parameters request, response and next, like every other route.
The Controller also can have a global middleware, that’s called for every method on that controller, so we’ll export the static array ‘middleware’ (optional) including the controller’s middleware.
Like a function is also an object, we can add the ‘middleware’ property to the function, so then the Ctrlrs function can search for any middleware and build the route property. The order is important, so we can add ‘posmiddleware’ to specify some middleware that’ll be triggered after the method on success (when next() is called)
So basically the what the Ctrlrs method does, is indexes every controller, and using the namespace notation, it’ll find the controller file, find it’s method, and form an array containing:
[...controllerMiddleware, ...methodMiddleware, method, ...methodPosMiddleware]
As you may know, express’s routes expect one or many (req, res, next) functions, or an array of (req, res, next) functions, so this way we return the built array so the middleware is executed in that specific order.
If you’d like, you can check the code. I made a gist with the Ctrlrs code, and bare in mind it’s using require-directory to require all the controllers inside the controllers folder.
Let me know what you think of this! And I hope you find it useful for your current or future expressjs projects :)