Hey there fellow developers! π Today, let's dive into the buzzing world of Node.js and uncover the top 5 backend frameworks making waves in 2024. Buckle up for a rollercoaster of coding fun! π’
Introduction: Unveiling the Backend Marvels
Since 2009, Node.js has been stealing the spotlight, becoming the go-to choice for backend developers. With big players like Netflix and PayPal on board, it's like the rockstar of web development. π€
The reason for the increase in popularity is the reduction in loading time and performance improvements. Therefore, it is crucial to analyze the top 5 Node.js Backend frameworks for 2024.
Hence, this article will cover the top 5 Node.js backend frameworks for 2024, their features, and common use cases.
Express.js: Where Minimalism Meets Power πͺ
Express.js, the heartthrob of Node.js frameworks. Open-source, freely available, and a favorite among both newbie and seasoned developers. Perfect for crafting web applications and snazzy RESTful APIs.
Key Features: The Express.js Showtime π¬
1. Efficient Routing: Handling HTTP requests like a boss! π
Express.js makes handling HTTP requests a breeze. Picture it like a GPS for your code, efficiently directing requests to specific tasks. πΊοΈ Let's break it down with a quick example:
// app.js
const express = require('express');
const app = express();
const port = 3000;
// Homepage Route
app.get('/', (req, res) => {
res.send('Welcome to the homepage!');
});
// User Profile Page Route
app.get('/user/:id', (req, res) => {
const userId = req.params.id;
res.send(`User Profile Page - ID: ${userId}`);
});
2. Middleware Support π¦ΈββοΈ
Express.js allows middleware support for handling HTTP requests. Letβs look at a simple example of creating a middleware for logging HTTP request details.
const express = require('express');
const app = express();
const port = 3000;
app.use((req, res, next) => {
console.log(`[${new Date().toLocaleString()}] ${req.method} ${req.url}`);
next();
});
3. Easy Database Integration: It's the Tinder of databases.
Express.js swipes right for all! π
Express.js offers remarkable flexibility in the realm of databases. It doesn't limit you to a specific database, but instead, it allows developers the liberty to select their preferred database system. This flexibility is made possible by the modular and adaptable nature of Express.js. The smooth integration with databases is achieved through its modular design and the abundance of npm packages dedicated to database connectivity. This lively ecosystem provides developers with the tools to seamlessly link Express.js with various databases, guaranteeing a streamlined and efficient development journey. π
4. Beginner-Friendly Brilliance:
Express.js takes the crown for its straightforward and minimalist design, making it a breeze for developers to grasp, especially if they're well-versed in JavaScript and Node.js.
Getting started with Express.js is a walk in the park, thanks to tools like Bit. For the uninitiated, Bit is a cutting-edge build system tailored for creating modular software.
Interestingly, Express.js itself is inherently composable. It behaves like building blocks that you can effortlessly plug and play anywhere in your application. π§©β¨
You can see two components: an Authorizer as well as an API app. These two components have been implemented as independent Bit components and are maintained and versioned in its isolated space.
By doing so, youβre able to design your app in a composable manner, quickly!
2.NestJS: A Fresh and Structured Approach
NestJS stands out as a framework designed for crafting scalable and efficient server-side applications in Node.js. Its approach incorporates progressive JavaScript and offers the flexibility to code in TypeScript. Despite its full support for TypeScript, it accommodates pure JavaScript and embraces Object-Oriented Programming, Functional Programming, and Functional Reactive Programming.
Key Features: What makes it stand out
1. Modular Brilliance
Nest.js excels in breaking down code into manageable modules, enhancing maintainability. Take a glance at the example module below:
import { Module } from '@nestjs/common';
@Module({
imports: [
CacheModule
],
controllers: [PaymentController],
providers: [PaymentService],
})
export class PaymentModule {}
This payment module can be seamlessly exported to other modules. In this instance, the common cache module is exported within the payment module. Nest.js' module structure simplifies maintenance, making it a standout feature. ππ§©
2. Scalable Synergy
Nest.js takes scalability to the next level by breaking down applications into manageable modules. It supports flexible component replacement, seamlessly handles high traffic through microservices, and excels in asynchronous operations. This ensures the efficient handling of increased workloads while maintaining the utmost reliability and performance.
3.Dependency Injection Dance
Dependency injection in Nest.js involves adding an external dependency to a class rather than creating it within the class itself. Let's dive into an example:
import { HttpException, Injectable, NotFoundException } from '@nestjs/common';
@Injectable()
export class PaymentService {
constructor() {}
getReceipt() {
return 'Payment Receipt';
}
}
In this snippet, the PaymentService is created and marked with the @Injectable() annotation, making it injectable. Now, let's see how we can use this service:
import { Controller, Get, Post, Body } from '@nestjs/common';
import { PaymentService } from './payment.service';
@Controller('payment')
export class PaymentController {
constructor(private readonly paymentService: PaymentService) {}
@Get()
getPaymentReceipt() {
return this.paymentService.getReceipt();
}
}
This example showcases the injection of the PaymentService into the PaymentController, allowing seamless access to its functionality. πΊπ‘
4. Shielded by TypeScript Armor
Nest.js leverages TypeScript to provide robust type safety, acting as a vigilant guardian against potential errors during development. This not only enhances the overall reliability of the code but also contributes to its maintainability. Let's explore an example:
export class PaymentDto {
@IsNotEmpty()
@IsEnum(SERVICE_PROVIDER_SLUG, {
message: `Invalid serviceProvider. Valid options are: ${Object.values(SERVICE_PROVIDER_SLUG).join(', ')}`,
})
serviceProvider: string;
@IsNotEmpty()
@IsNumber()
value: number;
@IsNotEmpty()
@IsString()
validityPeriod: string;
@IsNotEmpty()
@IsArray()
@ArrayNotEmpty()
@ValidateNested()
@Type(() => PaymentAttributesDto)
paymentAttributes: PaymentAttributesDto[];
}
In this example, we've crafted a Data Transfer Object (DTO) called PaymentDto, equipped with various parameters. The annotations, such as @IsNumber() and @IsString(), serve as guardians, ensuring that each parameter adheres to the specified type. For instance, attempting to assign a string value to the "value" parameter will trigger an error, adding an extra layer of protection to your application. π‘οΈπ§
Koa.js: Elegant and Lightweight
Koa.js, a creation of the Express.js team, emerges as a compact and expressive web framework. It provides a departure from callbacks, opting for the sophistication of async functions to handle errors seamlessly.
Key Features: What makes it stand out
1. Contextual Power (ctx)
Koa.js introduces the concept of ctx (context) to capture intricate details of requests and responses. This context gracefully flows through each middleware. Observe the example below:
const Koa = require('koa');
const app = a new Koa();
app.use(async (ctx) => {
const { method, url, request, response } = ctx;
console.log('Method: ' + method + ' Request: ' + request);
});
app.listen(3000);
Here, the ctx object encapsulates crucial information like the HTTP method, URL, request, and response, offering developers a comprehensive view of the ongoing process.
2.Middleware Composition
Similar to Express.js, Koa.js embraces the concept of middleware functions to handle HTTP requests and responses. Behold a simple middleware example:
javascript
Copy code
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
await next();
});
app.listen(3000);
In this snippet, a basic middleware is created, showcasing Koa's affinity for handling middleware in a manner reminiscent of its predecessor, Express.js. ππ
3. Async/Await Support
Koa uses the async/await syntax for writing asynchronous code in a more synchronous-looking way. The below example consists of using async/await keywords.
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx) => {
const data = await fetchData();
ctx.body = Data: ${data} ;
});
app.listen(3000);
4. Error Handling
Koa.Js supports various types of error handling. We can use app.emit() or ctx.throw() to handle errors. The below example consists of mentioned error-handling methods.
const koa = require('koa');
const app = new koa();
//Error Handling Method 1
app.use(async (ctx, next) => {
try {
await Promise.reject('Something went wrong');
} catch (err) {
ctx.status = err.status || 500;
ctx.body = err.message;
ctx.app.emit('error', err, ctx);
}
});
//Error Handling Method 2
app.use(async (ctx, next) => {
ctx.throw(500, 'error');
});
app.on('error', (err, ctx) => {
console.log(err);
});
app.listen(3000);
Hapi.js
Hapi.js, an open-source framework with a nifty abbreviation (short for Http-API), stands tall as a powerhouse for developing scalable web applications. Its forte includes crafting REST APIs, and it earned its stripes at Walmart Labs, handling the massive online shopping traffic surge during events like Black Friday.
Key Features: What makes it stand out
1. Design by Configuration
Hapi.js adopts a configuration-driven design, allowing developers to effortlessly set up routes, define settings, and integrate plugins using a configuration object. Feast your eyes on this example:
const Hapi = require('@hapi/hapi');
const server = Hapi.server({
port: 3000,
routes: {
cors: true,
},
});
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello, Hapi!';
},
});
async function start() {
await server.start();
console.log(`Server running at ${server.info.uri}`);
}
start();
In this snippet, the power of configuration shines as the server is set up, routes are defined, and CORS support is seamlessly configured.
2. Powerful Plugin System
Hapi.js boasts a powerful plugin system, allowing easy integration without breaking a sweat. Witness this example:
const start = async function () {
const server = Hapi.server();
await server.register([{
plugin: require('plugin1'),
options: {}
}, {
plugin: require('plugin2'),
options: {}
}]);
};
This snippet demonstrates the effortless integration of two plugins, with the ability to pass options to each plugin through the options key.
3. Authentication and Authorization
Hapi.js stands as a fortress with built-in support for various authentication strategies and a simple approach to defining access control policies. Observe the authentication prowess in this example:
server.route({
method: 'GET',
path: '/private-data',
handler: (request, h) => {
// Access private data only if authenticated
const user = request.auth.credentials;
return `Welcome, ${user.username}!`;
},
options: {
auth: 'jwt', // Use JWT authentication strategy
},
});
In this scenario, the authentication strategy is elegantly defined as 'jwt', ensuring secure access to private data.
4. Input Validation
Hapi.js places great emphasis on input validation. In the route's options object, developers can define which inputs need validation. The default validate object includes checks for headers, params, query, payload, state, and a fail action strategy.
Adonis.js
Adonis.js excels in routing, providing a seamless and intuitive way to define routes for your application. The routing system is designed to be expressive and powerful, making it easy for developers to define and manage the various endpoints of their application.
Key Features: What makes it stand out
1. Full-stack MVC framework
Adonis.js follows the MVC architectural pattern. Having a MVC framework helps organize code and makes it easier to maintain and scale.
2. Integrated ORM (Lucid) for database interactions
Adonis.js incorporates Lucid, its own Object-Relational Mapping (ORM) system. Lucid simplifies database interactions by providing an expressive query builder and supporting various database systems.
Here's a glimpse into how you can use Lucid to interact with the database:
const Model = use('Model');
class User extends Model {
}
module.exports = User;
In this example, the User model is defined using Lucid, and it becomes a powerful tool for reading and writing to the database. The route below demonstrates how effortlessly you can fetch all
const Route = use('Route');
const User = use('App/Models/User');
Route.get('users', async () => {
return await User.all();
});
By using User.all(), Adonis.js simplifies the process of fetching users from the database.
3. Authentication system
Adonis.js doesn't stop at just being an MVC framework; it brings a robust authentication system to the table. With built-in support for user authentication and authorization, Adonis.js makes it a breeze to handle user sessions, password hashing, and access control.
Take a look at this example illustrating the simplicity of fetching users:
const Route = use('Route');
const User = use('App/Models/User');
Route.get('users', async () => {
return await User.all();
}).middleware(['auth']);
In this scenario, the middleware(['auth']) ensures that only authenticated users can access the route for fetching users. Adonis.js streamlines the authentication process, making it an integral part of your application's security. π‘οΈπ
Top comments (22)
Koa is the worst web library ever.
Nest.js is a monster library introduced a lot useless concepts.
The only acceptable library is express, but express's code is coupled and hard to extend and evolve.
Excellent points @610470416 , couldn't agree more. As Principal Engineer I am constantly looking for solutions that offers mechanics that allows me to be productive but doesn't take my freedom on creating new things or being able to extend capabilities of the framework. Another important aspect for me is that the group of features available in the framework are the ones that are extremely necessary, nothing more, avoiding then unnecessary boilerplate or extravagant spaghettis code to get something simple done as you imply in your text.
I am working heavily using ExpressoTS Framework, and I and my team are enjoying very much.
Thanks.
ExpressoTS is a good try of modern frameworks which introduced new features by using ts, but it is still somewhat mvc based which is in fact not proper for the web framework as you can sometimes feel it:)
You can take a look at aex, which totally has nothing to do with mvc and obeys the Web Straight line theory to separate web logic from business logic easily. With aex, you can make your code cleaner, better organized, more simplified regardless of the ways how http requests are handled.
MVC separations only exist in your business code when necessary instead of the web framework, aex.
There are popular new frameworks
thnaks for mentioning them π«‘
Good article! :)
Few things to consider:
.NET Core its a strong contender however is not the "lingua franca" of the internet. Internet is widely dominated by the javascript / typescript nodejs ecosystem. It doesn't mean that .NET Core framework is not used, in fact it is used especially here in America.
Related to the frameworks above mentioned, the only ones actively maintained are Nestjs and Koa. Hapi and Adonis are in a plateau without changing or significant change
I would recommend exploring expresso-ts.com/. It's a new framework (2.5 years of development, 1 year made public), currently being used in Asia, and Oceania and is gaining some traction this year in North America, especially in Canada.
@rsaz Thank you so much for your positive feedback! π I appreciate your insights and observations. I'll definitely take your recommendation to explore expresso-ts.com into consideration. It's exciting to learn about emerging frameworks gaining traction globally,
I just use SvelteKit.
Express.js is super nice to work with. On its own, it's already useful. There are some great middleware's out there and it's easy to build on top of.
One of my repo's
MrLinxed / zod-express-middleware
Express middleware to validate requests using zod schema's.
zod-express-middleware
Middleware for express that uses zod to make requests type-safe.
Installation
This package relies on zod, express and @types/express. These have been added as peer dependencies so they can be upgraded independently of this package.
zod-express-middleware can be installed using:
npm install zod-express-middleware
Usage
This package provides the
validateRequest
function, which can be used to validate the.body
,.query
and.params
properties of an ExpressRequest
. Separate functions for each of these are also provided (validateRequestBody
,validateRequestQuery
andvalidateRequestParams
).Basic example:
Absolutely! Express.js is awesome to work with. Its simplicity and the availability of great middleware make it super handy
For Nodejs, I noticed that Fastify framework is not mentioned.
Please just remove hapi!!!
Apparently you don't know the must know one:
aex
For an enterprise project, nest.js is a solid and safe choice. Especially with DDD approach.
Forgot to mention Fastify. It's honestly the most performant and modern of the Node frameworks.