API with NestJS #89. Replacing Express with Fastify (2024)

This entry is part 89 of 138 in the API with NestJS

By default, NestJS uses Express under the hood. Moreover, since Express is very popular, NestJS can choose from a broad set of compatible third-party solutions.

A significant advantage of NestJS is that it is framework-independent. Instead of using Express, we can use an adapter using another library with a similar request/response pipeline. The most popular implementation besides Express is the Fastify adapter.

Check out this repository to see the full code from this article.

Introducing Fastify

The main selling point of Fastify isperformance. We can find various comparisons on the web that prove that Fastify handles HTTP requests faster than Express.

However, there is a big chance that Express is not the bottleneck in your application. Instead, optimizing how we use our database and caching can yield great results.

If you want to know more about caching with NestJS, check out the following articles:

Using Fastify with NestJS

At first glance, it’s very straightforward to start using Fastify with NestJS. To do that, we only need to modify our main.ts file.

1

npm install @nestjs/platform-fastify

main.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import { NestFactory } from '@nestjs/core';

import { AppModule } from './app.module';

import {

FastifyAdapter,

NestFastifyApplication,

} from '@nestjs/platform-fastify';

import { ConfigService } from '@nestjs/config';

async function bootstrap() {

const app = await NestFactory.create<NestFastifyApplication>(

AppModule,

new FastifyAdapter(),

);

const configService = app.get(ConfigService);

await app.listen(configService.get('PORT'));

}

bootstrap();

When we do the above, NestJS starts using Fastify as its HTTP provider, and we don’t need to provide any additional configuration.

However, one of the biggest strengths of Express is its wide selection of compatible libraries. When using Fastify, we must ensure that the packages we use are compatible or use alternatives developed with Fastify in mind.

We also need to remember that since Express is the default solution for NestJS, all of the libraries maintained by the NestJS team are usually developed with Express in mind. Therefore, if we decide to go with Fastify, we must brace ourselves to deal with some incompatibilities.

Things to watch out for when switching to Fastify

Modifying our main.ts file is enough to start using Fastify. Although that’s the case, let’s go deeper and investigate some real-life scenarios to see what it’s like using Fastify.

In the third part of this series, we’ve implemented authentication using bcrypt, Passport, JWT, and cookies. Since many projects include some authentication, it is a good starting point to learn how to use Fastify with NestJS.

Accessing the request and response objects

When using Express, we can easily access the request and response object using the correct decorators.

categories.controller.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

import {

Controller,

Get,

UseInterceptors,

ClassSerializerInterceptor,

Req,

Res,

} from '@nestjs/common';

import CategoriesService from './categories.service';

import express from 'express';

@Controller('categories')

@UseInterceptors(ClassSerializerInterceptor)

export default class CategoriesController {

constructor(private readonly categoriesService: CategoriesService) {}

@Get()

async getAllCategories(

@Req() request: express.Request,

@Res() response: express.Response,

) {

console.log(`${request.method} ${request.url}`); // GET /categories

const categories = await this.categoriesService.getAllCategories();

response.send(categories);

}

// ...

}

When we use Fastify, we need to use different interfaces for the above objects.

categories.controller.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

import {

Controller,

Get,

UseInterceptors,

ClassSerializerInterceptor,

Req,

Res,

} from '@nestjs/common';

import CategoriesService from './categories.service';

import { FastifyReply, FastifyRequest } from 'fastify';

@Controller('categories')

@UseInterceptors(ClassSerializerInterceptor)

export default class CategoriesController {

constructor(private readonly categoriesService: CategoriesService) {}

@Get()

async getAllCategories(

@Req() request: FastifyRequest,

@Res() response: FastifyReply,

) {

console.log(`${request.method} ${request.url}`); // GET /categories

const categories = await this.categoriesService.getAllCategories();

response.send(categories);

}

// ...

}

While in the above example, Express and Fastify work the same, this is not always the case. For example, to set a header in the response, we need to use the response.header() function instead of response.setHeader().

authentication.controller.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

import {

Req,

Controller,

HttpCode,

Post,

UseGuards,

ClassSerializerInterceptor,

UseInterceptors,

Res,

} from '@nestjs/common';

import { AuthenticationService } from './authentication.service';

import RequestWithUser from './requestWithUser.interface';

import { LocalAuthenticationGuard } from './localAuthentication.guard';

import { FastifyReply } from 'fastify';

@Controller('authentication')

@UseInterceptors(ClassSerializerInterceptor)

export class AuthenticationController {

constructor(private readonly authenticationService: AuthenticationService) {}

@HttpCode(200)

@UseGuards(LocalAuthenticationGuard)

@Post('log-in')

async logIn(

@Req() request: RequestWithUser,

@Res({ passthrough: true }) response: FastifyReply,

) {

const { user } = request;

const cookie = this.authenticationService.getCookieWithJwtToken(user.id);

response.header('Set-Cookie', cookie);

return user;

}

// ...

}

Thanks to using passthrough: true we can return the data from the above method and let NestJS send the data. Without that, we would need to call the response.send() method instead.

Working with cookies

The cookie-parser library is a very popular middleware ready to use with Express. However, when using Fastify, we need to find an alternative.

1

npm install @fastify/cookie

Fortunately, the @fastify/cookie library is straightforward. For our application to support cookies, we need to modify our main.ts file and call the app.register method.

main.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

import { NestFactory } from '@nestjs/core';

import { AppModule } from './app.module';

import {

FastifyAdapter,

NestFastifyApplication,

} from '@nestjs/platform-fastify';

import { ConfigService } from '@nestjs/config';

import cookie from '@fastify/cookie';

async function bootstrap() {

const app = await NestFactory.create<NestFastifyApplication>(

AppModule,

new FastifyAdapter(),

);

await app.register(cookie);

const configService = app.get(ConfigService);

await app.listen(configService.get('PORT'));

}

bootstrap();

Passport

In this series, we’ve used the Passport library to avoid implementing all aspects of authentication manually. Sadly, the @nestjs/passport library does not support Fastify officially.

There is the @fastify/passport package, but it’s not very popular. Unfortunately, it integrates with Passport differently, and guards built into NestJS might not work out of the box with it.

Thankfully, @nestjs/passport works fine with Passport as long as we use simple JWT-based authentication.

authentication.controller.ts

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

import { ExtractJwt, Strategy } from 'passport-jwt';

import { PassportStrategy } from '@nestjs/passport';

import { Injectable } from '@nestjs/common';

import { ConfigService } from '@nestjs/config';

import { UsersService } from '../users/users.service';

import TokenPayload from './tokenPayload.interface';

@Injectable()

export class JwtStrategy extends PassportStrategy(Strategy) {

constructor(

private readonly configService: ConfigService,

private readonly userService: UsersService,

) {

super({

jwtFromRequest: ExtractJwt.fromExtractors([

(request: { cookies: Record<string, string> }) => {

return request?.cookies?.Authentication;

},

]),

secretOrKey: configService.get('JWT_SECRET'),

});

}

async validate(payload: TokenPayload) {

return this.userService.getById(payload.userId);

}

}

We can access request?.cookies?.Authentication thanks to using the @fastify/cookie library.

Please notice that above we use request: { cookies: Record<string, string> } instead of request: FastifyRequest. This is because using the latter would cause TypeScript to complain that FastifyRequest is incompatible with express.Request.

While JWT-based authentication works fine, we might encounter issues when implementing OAuth. Thankfully, the official Discord channel of NestJS is a great place to get help with such problems. For example, Jay McDoniel, who is a part of the core NestJS team, suggests adding the following snippet to our main.ts file if we want to make @nestjs/passport work with OAuth and Fastify:

1

2

3

4

5

6

7

8

9

10

11

const fastifyInstance: FastifyInstance = app.getHttpAdapter().getInstance()

fastifyInstance

.addHook('onRequest', async (req, res) => {

req.socket['encrypted'] = process.env.NODE_ENV === 'production'

})

.decorateReply('setHeader', function (name: string, value: unknown) {

this.header(name, value)

})

.decorateReply('end', function () {

this.send('')

})

In the above code we try to make Fastify more compatible with how the request and response objects work in Express.

Summary

In this article, we’ve replaced Express with Fastify and achieved a fully-working application that includes authentication. While configuring NestJS to use Fastify is very simple, working with Fastify might not be that convenient. When switching to Fastify, we might increase the performance of our application, but we need to be aware of the disadvantages.

API with NestJS #89. Replacing Express with Fastify (1)

There is a big community behind Express, and it shows. If your application requires top-notch performance, it’s worth giving Fastify a try.

Series Navigation<< API with NestJS #88. Testing a project with raw SQL using integration testsAPI with NestJS #90. Using various types of SQL joins >>

As an expert in web development and the NestJS framework, I have extensive knowledge of the topics discussed in the provided article series. My expertise spans various aspects of building APIs with NestJS, including controllers, routing, module structure, database setup, authentication, error handling, data validation, serialization, dependency injection, testing, file uploads, microservices, GraphQL, database relationships, caching, and many more.

The article you've shared is the 89th entry in a series that specifically focuses on using Fastify as the HTTP provider in NestJS instead of the default Express. The series covers a wide range of topics, from basic API setup to advanced topics such as working with databases (PostgreSQL, MongoDB), implementing authentication strategies, optimizing performance, using different libraries and frameworks, and deploying applications to cloud services like AWS.

In the 89th entry, the author introduces Fastify as a high-performance alternative to Express and walks through the process of integrating it into a NestJS application. The article highlights key points to consider when switching from Express to Fastify, such as differences in handling request and response objects, and the need for compatibility with libraries used in the NestJS ecosystem.

Some specific concepts and topics covered in the provided article include:

  1. Fastify Integration with NestJS:

    • Switching from Express to Fastify in the main.ts file.
    • Using @nestjs/platform-fastify and @nestjs/core to create a NestFastifyApplication.
  2. Handling Request and Response Objects:

    • Adapting code to use FastifyRequest and FastifyReply instead of Express's Request and Response.
    • Making adjustments in controllers to work seamlessly with Fastify.
  3. Working with Cookies:

    • Introducing the @fastify/cookie library to support cookies in a Fastify-based NestJS application.
    • Modifying the main.ts file to register the cookie plugin.
  4. Authentication with Passport:

    • Discussing challenges when using Passport with Fastify.
    • Demonstrating how to use JWT-based authentication with Fastify and @nestjs/passport.
  5. OAuth and NestJS Passport:

    • Addressing potential issues with OAuth authentication in a Fastify environment.
    • Adding code snippets to enhance compatibility with OAuth and Fastify.
  6. Performance Considerations:

    • Highlighting the main selling point of Fastify as performance.
    • Discussing the need to evaluate whether the performance gain justifies the potential downsides of using Fastify.

In summary, the article serves as a practical guide for developers looking to leverage the performance benefits of Fastify in a NestJS application while addressing common challenges and considerations associated with the transition from Express.

API with NestJS #89. Replacing Express with Fastify (2024)
Top Articles
Latest Posts
Article information

Author: Kelle Weber

Last Updated:

Views: 6085

Rating: 4.2 / 5 (73 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Kelle Weber

Birthday: 2000-08-05

Address: 6796 Juan Square, Markfort, MN 58988

Phone: +8215934114615

Job: Hospitality Director

Hobby: tabletop games, Foreign language learning, Leather crafting, Horseback riding, Swimming, Knapping, Handball

Introduction: My name is Kelle Weber, I am a magnificent, enchanting, fair, joyous, light, determined, joyous person who loves writing and wants to share my knowledge and understanding with you.