Express.js: The dangers of unopinionated frameworks and best practices for building web applications.
Introduction
Express.js is a popular Node.js web application framework that is well-known for its minimalist and unopinionated nature. While its flexibility and simplicity appeal to many developers, there are inherent risks and drawbacks associated with this. In this article, we will explore the downsides of Express.js due to its unopinionated nature and discuss the best practices for building robust, maintainable and scalable applications.
Issues with an unopinionated framework
Express.js offers developers a high level of freedom and customization, allowing them to choose from a vast ecosystem of middleware, plugins, and approaches. However, this freedom comes at a cost, as the absence of defined conventions and best practices can lead to inconsistent codebases. I have seen many Express.js code bases during my career, and some of these codes are PAINFUL to look at, to say the least. Some of these bad practices include; no separation of concern (controller, service and repository code all in one file), not using Typescript, no API documentation etc...
Without a clear structure, developers have free rain to architect an application however they choose leading to codebases that are difficult to navigate and understand. This can hinder collaboration, increase maintenance overhead, and make it harder for new team members to on board. As an application grows and evolves, maintaining an Express.js codebase becomes increasingly challenging. As mentioned, without proper separation of concern, refactoring or extending existing functionality can be complex and prone to errors. This can result in technical debt and hinder the ability to adapt to changing business requirements.
Best Practices for Building an Express.js App
While Express.js has its drawbacks, it is still one of the most popular (and most recommended) frameworks out there for writing Node.js applications due to its minimalist approach and ease of use [1][2]. Below are methods/techniques that will lead to the success of writing a well-engineered Express.js application.
Well-defined project structure
The essence of building a well-written Express.js app is to establish a clear project structure which is crucial to maintain code readability and scalability. Consider using the ever-so-popular "separation of concern" pattern to organize your codebase effectively.
In my experience, there are 2 ways that Express.js projects should be structured; these are:
Option 1: Create directories for each layer of the application; controllers, services, and repository. Inside each directory, create sub-directories for each class.
- src
- controllers // responsible for handling HTTP requests
- client
client.controller.ts
client.schema.ts
client.dto.ts
- services // responsible for business logic
- client
client.service.ts
- repository // responsible for querying databases and/or calling other services
- client
client.model.ts // this is the model for the ORM
Option 2: Create a directory for each class, and inside, create all the files related to the class.
- src
- client
client.controller.ts
client.service.ts
client.model.ts
client.schema.ts
client.dto.ts
As we can see in the above, there is a clear separation of concern for each layer of the application. This allows the application to grow and be easily maintained.
Typescript everything
A colleague once said to me: "Javascript is a terrible language, Typescript makes it bearable". And needless to say, he is 100% correct. There are numerous articles stating why we should use Typescript instead of plain Javascript; here are just a few with a simple Google search: [3][4][5]. But just to list a few advantages of Typescript, these are; enhanced productivity, maintainability and scalability, improved code documentation and much, much more. But what still surprises me is that despite all these benefits, I still see applications written in plain Javascript. This is not limited to Express.js code bases; I see the use of Javascript in a lot of applications.
And my final thoughts on this point; TYPESCRIPT EVERYTHING!!!
Document the application
From my experience, I noticed developers have a strong dislike for writing documentation; this is true even for senior developers. I hold the strong opinion that documentation is almost just as important as the app itself. And the benefits include easy onboarding, easy handover, for customers if the app is being sold, and so on.
But the question is, why isn't this done more in practice? I think I know the reason, and that is; most devs have this mindset they are only writing code for themselves and do NOT think about how it will impact the next person working on the app. I am also guilty of this in the past. But I have learnt from my mistakes and have implemented several simple ways to write API documentation; these include Swagger documentation, Postman collections, a simple confluence space or a combination of the 3.
Proper documentation makes everyone's lives easier. My only hope is that this article inspires the next developer to take writing documentation part of their daily task.
Conclusion
While Express.js's unopinionated nature provides developers with flexibility and freedom, it comes with inherent risks and challenges. By understanding these downsides and adopting best practices, developers can mitigate potential pitfalls and build robust and maintainable Express.js applications.