Node.js is notorious for being the Wild West of software development, where few, if any, rules and standards apply. To this day there are no established conventions for how to slice the code, how to structure the file hierarchy, and how to implement the most basic building blocks of a web application.
NestJS is the closest that we've ever got to having a defined enterprise structure in Node.js - it popularized several badly needed concepts within the ecosystem, such as modular structure, separation of concerns across layers, and dependency injection. While nothing new in the world of software engineering, they are rarely remembered about in Node.js.
However, NestJS is only a step in the right direction - it is not an ultimate solution. For all the things that it got right, it also introduces significant complexity of its own, and has some strongly opinionated limitations, while also leaving sizable gaps in several areas, such as configuration management.
Trying to do better
What if there was another way? What if we could have all the flexibility of a lightweight library, without sacrificing the "batteries-included" convenience of a deliberately designed framework? What if a group of experienced engineers from an established company distilled their experience of building and running backend services in production into something that anyone could use?
Ladies and gentlemen, let me introduce to you the node-service-template.
As the name suggests, it's not a framework, it's a template. The underlying idea of it is that you copy it over, and then adjust based on what you need (or not).
It is built on top of a wonderful fastify framework, which is quite lightweight on its own, but within the template is enriched with a carefully curated selection of plugins and libraries, as well as custom-built foundational code, providing a complete enterprise service feature set.
Let's take a look at what it has to offer!
node-service-template at a glance
* fastify as a basis for the general web application skeleton;
* Modular, domain-driven structure that encourages separation of concerns;
* Server/app separation, for convenient bootstrapping in e2e tests;
* Extendable global error handler;
* JSON-based, single line standardized logging, using pino;
* Automatic population of `req.id` for incoming requests based on `x-request-id` header, or generation of new UUID if none is set, for the purposes of distributed tracing.
* Type-safe config management;
* Dependency injection, using awilix;
* Job scheduling, using toad-scheduler and redis-semaphore;
* Type-safe dependency mocking for tests;
* Background job scaffolding;
* Redis repository scaffolding;
Basic building block examples:
* Repository, using prisma;
* Domain service;
* API schema;
* e2e test;
* Smart healthcheck plugin, using fastify-custom-healthcheck;
* JWT plugin, using @fastify/jwt;
* Generate OpenAPI specification file from your route definitions;
* Validate your OpenAPI specification file.
I would like to thank Andreas Kristiansen, Ivan Pesin, Andris Sevcenko, Rupert McKay, Eugene Dementjev, Alex Fedorov and all other engineers without whom this project would not be possible. It was a great joy and honour to collaborate with them on this. I am also grateful to our fantastic company, Lokalise, for encouraging this project, and for the willingness to release the fruits of this labour to the wider community.
Please take our template out for a test drive, either to bootstrap your services, or as a base for your own internal template.
And if we could do something better - let us know! Looking forward to your suggestions, bug reports, and complaints!
There is another project aiming to solve same problem in a different way - platformatic (still in fairly early development phase, but already available for the use). Co-founded by legendary Matteo Colllina, and covering not just the application building part, but also deploying, running and observing it during the runtime, it is definitely a platform to keep an eye on.