DEV Community

Cover image for The 5 Must-Know Backend Frameworks in 2024
Sufian mustafa
Sufian mustafa

Posted on

The 5 Must-Know Backend Frameworks in 2024

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! 🎒

Top 5 Node.js Frameworks for Building Awesome APIs in 2024
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. 🀘
Top 5 Node.js Frameworks for Building Awesome APIs in 2024

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.
Top 5 Node.js Frameworks for Building Awesome APIs in 2024

Top 5 Node.js Frameworks for Building Awesome APIs in 2024

Express.js: Where Minimalism Meets Power πŸ’ͺ

Express.js
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}`);
});
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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.
Top 5 Node.js Frameworks for Building Awesome APIs in 2024
By doing so, you’re able to design your app in a composable manner, quickly!

2.NestJS: A Fresh and Structured Approach

NestJS
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 {}
Enter fullscreen mode Exit fullscreen mode

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';
  }
}
Enter fullscreen mode Exit fullscreen mode

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();
  }
}
Enter fullscreen mode Exit fullscreen mode

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[];
}
Enter fullscreen mode Exit fullscreen mode

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
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);
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
  }
});
Enter fullscreen mode Exit fullscreen mode

//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
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();
Enter fullscreen mode Exit fullscreen mode

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: {}
    }]);
};
Enter fullscreen mode Exit fullscreen mode

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
  },
});
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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']);
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
610470416 profile image
NotFound404

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.

Collapse
 
rsaz profile image
Richard Zampieri • Edited

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.

Collapse
 
610470416 profile image
NotFound404

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.

Collapse
 
meirlamdan profile image
meirlamdan

There are popular new frameworks

Collapse
 
sufian profile image
Sufian mustafa

thnaks for mentioning them 🫑

Collapse
 
rsaz profile image
Richard Zampieri • Edited

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.

Collapse
 
sufian profile image
Sufian mustafa

@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,

Collapse
 
jdgamble555 profile image
Jonathan Gamble

I just use SvelteKit.

Collapse
 
mrlinxed profile image
Mr. Linxed

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

GitHub logo 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.

npm npm npm License

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 Express Request. Separate functions for each of these are also provided (validateRequestBody, validateRequestQuery and validateRequestParams).

Basic example:

import { validateRequest } from 'zod-express-middleware';
import { z } from 'zod';
// app is an express app
app.get("/", validateRequest({
    body: z.object({
      bodyKey: z.number(),
    }),
  }), (req, res) => {
    // req.body is now
…
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sufian profile image
Sufian mustafa

Absolutely! Express.js is awesome to work with. Its simplicity and the availability of great middleware make it super handy

Collapse
 
tduyng profile image
Z

For Nodejs, I noticed that Fastify framework is not mentioned.

Collapse
 
nenadj profile image
Nenad

Please just remove hapi!!!

Collapse
 
610470416 profile image
NotFound404

Apparently you don't know the must know one:
aex

Collapse
 
mukhammadsobirov profile image
Mukhammad Sobirov

For an enterprise project, nest.js is a solid and safe choice. Especially with DDD approach.

Collapse
 
marlonmarcello profile image
Marlon Ugocioni Marcello

Forgot to mention Fastify. It's honestly the most performant and modern of the Node frameworks.