Home >Web Front-end >JS Tutorial >How to Migrate Your App from Express to Fastify

How to Migrate Your App from Express to Fastify

尊渡假赌尊渡假赌尊渡假赌
尊渡假赌尊渡假赌尊渡假赌Original
2025-02-10 14:50:13677browse

How to Migrate Your App from Express to Fastify

Express was once the most popular framework for developing web applications using Node.js. However, the framework has decreased active development in recent years, resulting in a lack of support for modern JavaScript features. At the same time, many new frameworks that adopt different approaches to Node.js application development emerge, and Fastify is one of them.

This article will explore why Fastify has become a compelling alternative to Node.js web application development. We will learn how to avoid rewriting existing Express applications from scratch and instead move to Fastify step by step. After studying this article, you will be able to confidently migrate your existing Express applications and start taking advantage of the Fastify framework.

The following conditions must be met when reading this article:

  • Familiar with creating basic Express applications, defining routing, and configuring middleware.
  • Familiar with running commands in the terminal.
  • Node.js >= v14.13.0 is installed. This provides us with good support for the ECMAScript (ES) module and allows us to use top-level await. The code examples in this article use the ES module syntax (import / export).

All the sample code in this article can be found on GitHub and you can browse, download and experiment with it.

The video version of this article is also available on my website.

Key Points

  • Fastify provides built-in verification and logging, native asynchronous support, and automatic JSON parsing, improving development efficiency and application performance compared to Express.
  • fastify-express Plug-in facilitates the gradual migration from Express to Fastify by allowing the use of Express middleware and routing in the Fastify framework.
  • Integration tests should be framework-independent to ensure they remain valid before and after migration, thus simplifying the testing process.
  • Refactoring involves replacing Express code with Fastify equivalents, such as using Fastify's routing and plug-in systems instead of Express routers and middleware.
  • The migration process can be staged, first integrating Fastify into an existing Express application, and then gradually replacing the Express components.
  • After migrating, be sure to replace the third-party Express middleware with the equivalent Fastify plugin to take advantage of Fastify's performance and capabilities.

The benefits of migrating from Express to Fastify

If you are familiar with building Node.js applications using Express, you may be wondering what the benefits of migrating your existing Express applications to Fastify. Here are some important reasons to consider migration:

  • Verification and logging out of the box. These features are usually required when building web applications. When using Fastify, there is no need to select and integrate libraries for these tasks, as it provides us with these capabilities. We will learn more about these features later in this article.

  • Native support for asynchronous code. Fastify natively handles Promise and supports async/await. This means that the route will capture uncatched rejected promises for us. This allows us to write asynchronous code safely. It also allows us to do some concise things, such as automatically sending the return value of the routing handler function as the response body:

    <code>  app.get("/user/:id", async (request) => await getUser(request.params.id));
    </code>
  • Automatically parse and serialize JSON. We don't need to configure Fastify to parse the JSON request body, nor do we need to serialize the object to JSON for response. It will automatically handle all of this for us:

    <code>  app.get("/user/:id", async (request, reply) => {
        const name = request.body.name;
    
        reply.send({ user: { name } });
      });
    </code>
  • Developer friendly. With a clear and expressive API, and excellent support for TypeScript, Fastify is designed with developer experience in mind.

  • Fast speed. We do not want frameworks to be the source of performance bottlenecks in our applications. The good news is that Fastify is built to achieve high performance. The Fastify benchmark shows how it compares to other Node.js web frameworks.

  • Actively developing. The Fastify framework is under active development. Release improvements and bug/security fixes regularly.

How to Confidently Migrate API

We want to be sure that it still works as expected after the application is migrated to Fastify. One thing that helps us spot errors or identify unexpected changes is API integration testing.

Integration testing tests the components of the application in a different way than unit testing. Unit tests individual components' functionality. Integration testing allows us to verify the behavior of multiple components working together.

If we write API integration tests for Express applications, we want to be able to run the same test after migrating the application to Fastify. When writing integration tests for APIs, the following points need to be considered:

  • They should not be bound to a specific framework. We want to be able to run the same tests before and after the migration without changing the tests or any libraries we are using.
  • Keep it simple. At the very least, integration tests should make a request to the endpoint exposed by the API and verify that a response was returned, but not often more. We may want to check for specific HTTP status codes or response headers, but we should try to make the test as simple as possible.
  • Select the tools you are familiar with. There are many different tools that can help us create and run API tests, but it is important to use tools we are familiar with. To write effective integration tests, we need to be able to issue HTTP requests and assert responses from our API. Generally speaking, we don't need many libraries or tools to achieve this.

We won't go into detail about how to implement API integration tests in this article, but you should consider writing them before doing framework migrations.

Migrate from Express to Fastify using fastify-express

The idea of ​​migrating existing Express applications to a completely different framework may seem rather daunting. Fortunately, the Fastify team created a plugin - fastify-express - that can help simplify migration paths.

The fastify-express plugin adds full Express compatibility to Fastify. It provides a use() method that we can use to add Express middleware and routes to our Fastify server. This gives us the option to gradually migrate parts of our existing Express application to Fastify.

This is an example of an Express router:

<code>  app.get("/user/:id", async (request) => await getUser(request.params.id));
</code>

We can then use fastify-express to add our existing Express router to the Fastify server instance:

<code>  app.get("/user/:id", async (request, reply) => {
    const name = request.body.name;

    reply.send({ user: { name } });
  });
</code>

After a moment, we will explore the details of all these working as we start migrating the application to Fastify.

It is important to note that using the fastify-express plugin is not a long-term solution. If we want to get the full benefits of Fastify, we eventually need to migrate our Express-specific application code. However, the fastify-express plugin provides us with the opportunity to migrate to Fastify in phases.

Our Example Express Application

We will build a sample Express application and then migrate it to using the Fastify framework. Now let's look at its code.

Required Dependencies

First, let's create a new project:

<code>// src/routes.js
const router = express.Router();

router.get("/:user_id", function getUser(request, response, next) {
  response.json({});
});

export default router;
</code>

We will then run this command in the terminal to install the dependencies required by our Express application:

<code>// src/server.js

import Fastify from "fastify";
import ExpressPlugin from "fastify-express";

import routes from "./routes.js";

const fastify = Fastify();

await fastify.register(ExpressPlugin);

fastify.use("/user", routes);

await fastify.listen(3000);
</code>

Finally, open package.json and add the following line above the scripts section:

<code>mkdir express-to-fastify-migration
cd express-to-fastify-migration
npm init -y
</code>

This will allow us to load the ES module in our application.

Routing Module

We will create an Express router instance to help us encapsulate our routing and middleware. Routers in Express can be used to help us organize applications into discrete modules. For example, we might have a router for /user routing and another for /address routing. We'll see later how this helps us to phase out Express applications to Fastify.

Let's create a router instance and add some middleware to it:

<code>npm install express cors
</code>

In the above code, we configured two Express middleware examples:

  • express.json(). This middleware function is built into Express. It handles parsing the JSON request body.
  • cors. This middleware helps us add the CORS header to our API response. It will allow our API to be called from the web page.

These middleware tools will run against any requests made by any routes we define on this router.

Now that we have configured the middleware, we can add the first route to our router:

<code>"type": "module",
</code>

In a real application, the above routing handler function will verify the data it receives and then call the database to create a new user record. For this example, we are sending data received as the response body.

Now we will add a route to retrieve users:

<code>  app.get("/user/:id", async (request) => await getUser(request.params.id));
</code>

Like POST routing, the above routing handler usually calls the database to retrieve user data, but for this example, we have hardcoded an object to send in the response body.

Finally, we will export the router object so that we can import it in another module:

<code>  app.get("/user/:id", async (request, reply) => {
    const name = request.body.name;

    reply.send({ user: { name } });
  });
</code>

app module

Now we will create an app module:

<code>// src/routes.js
const router = express.Router();

router.get("/:user_id", function getUser(request, response, next) {
  response.json({});
});

export default router;
</code>

In this module, we define a function that creates a new Express server instance. We then add the router object to the server instance.

Server Module

Finally, we will create a server module. This module uses the buildApp() function we defined in the app module to create a new Express server instance. It then starts our Express server by configuring it to listen on port 3000:

<code>// src/server.js

import Fastify from "fastify";
import ExpressPlugin from "fastify-express";

import routes from "./routes.js";

const fastify = Fastify();

await fastify.register(ExpressPlugin);

fastify.use("/user", routes);

await fastify.listen(3000);
</code>

Run our application

We now have a complete runnable Express application that we can run in the terminal:

<code>mkdir express-to-fastify-migration
cd express-to-fastify-migration
npm init -y
</code>

In another terminal, we can use cURL to make a request to the API to confirm whether it is working:

<code>npm install express cors
</code>

We should receive a response as follows:

<code>"type": "module",
</code>

Migrate our application from Express to Fastify

Now we have a fully-featured Express application that we will migrate to using the Fastify framework.

Required Dependencies

We need to install three dependencies:

  • Fastify framework
  • fastify-express plugin
  • fastify-cors plugin - This is a ported version of the Express cors middleware that our application already uses

Let's run this command in the terminal to install them:

<code>// src/routes.js

import express from "express";
import cors from "cors";

const router = express.Router();

router.use(express.json());

router.use(cors({ origin: true }));
</code>

You can view the differences in these code changes on GitHub.

Refactor our app module

Now that we have the dependencies installed, we need to refactor our app module. We will change it to:

  • Import fastify and fastify-express instead of express
  • Create a Fastify server instance instead of an Express server instance
  • Add our Express router object to the server using the fastify-express plugin

This is what we look like after we make these changes:

<code>// src/routes.js

router.post("/", function createUser(request, response, next) {
  const newUser = request.body;

  if (!newUser) {
    return next(new Error("Error creating user"));
  }

  response.status(201).json(newUser);
});
</code>

You can view the differences in these code changes on GitHub.

You will notice in the above code that when we create a Fastify server instance, we are passing the logger option. This enables Fastify's built-in logging feature. We'll learn more about this later.

Refactor our server module

Now we need to change our server module to work with the Fastify server instance:

<code>// src/routes.js

router.get("/:user_id", function getUser(request, response, next) {
  const user = {
    id: request.params.user_id,
    first_name: "Bobinsky",
    last_name: "Oso",
  };

  response.json(user);
});
</code>

You can view the differences in these code changes on GitHub.

Since Fastify natively supports Promise, in the above code we can use await and then use Fastify's built-in logging feature to capture and log any errors.

Next step

Our application now uses Fastify to route requests and send responses. It's full-featured, but our routing is still using Express. In order to fully migrate to Express, we need to migrate our routes to using Fastify as well.

Refactor our routing module

Routing in Express applications is encapsulated in Express routers. We refactored this router to a Fastify plugin. Plug-in is a feature of Fastify that allows us to encapsulate routing and any related features.

We will start by removing some Express-specific rows to refactor our routing module (src/routes.js):

<code>  app.get("/user/:id", async (request) => await getUser(request.params.id));
</code>

Then we need to change the default module export to an asynchronous function that accepts the Fastify server instance. This is the foundation of the Fastify plugin. The rest of the code in our routing module will be moved into this plugin function:

<code>  app.get("/user/:id", async (request, reply) => {
    const name = request.body.name;

    reply.send({ user: { name } });
  });
</code>

In order for our middleware and routing to work with Fastify, we need to change:

  • Change router reference to fastify
  • Change the route handler function to an asynchronous function
  • Change the route handler function parameters from (request, response, next) to (request, reply)
  • Change the response reference to reply
  • Change the response.json() call to reply.send()
  • Change the next(error) instance to throw error

After making all these changes, our routing module is now a Fastify plugin with Fastify routing:

<code>// src/routes.js
const router = express.Router();

router.get("/:user_id", function getUser(request, response, next) {
  response.json({});
});

export default router;
</code>

Now we need to change our app module (src/app.js) to use the plugin we exported from the routing module. This means replacing the fastify.use() call with a call to fastify.register():

<code>// src/server.js

import Fastify from "fastify";
import ExpressPlugin from "fastify-express";

import routes from "./routes.js";

const fastify = Fastify();

await fastify.register(ExpressPlugin);

fastify.use("/user", routes);

await fastify.listen(3000);
</code>

You can view the differences in these code changes on GitHub.

Our example Express application has only one router, so we can migrate all routes in the application to using Fastify at one time. However, if we had a larger Express application with multiple routers, we could migrate each router to Fastify step by step at a time.

Replace the middleware with plug-in

Our application is in good condition and we have almost completely migrated it from Express to Fastify. One more thing to be migrated: our use of the cors Express middleware package. We installed the fastify-cors plugin before, and now we need to add it in our application to replace the cors middleware.

In our routing module (src/routes.js), we need to replace the import of the cors middleware:

<code>mkdir express-to-fastify-migration
cd express-to-fastify-migration
npm init -y
</code>

Then we need to replace the call to fastify.use() with the call to fastify.register():

<code>npm install express cors
</code>

Note that when we register the plugin with Fastify, we need to pass the plugin function and option objects as separate parameters.

Since we no longer use the use() function provided by the fastify-express plugin, we can remove it completely from our application. To do this, let's delete the following lines from our app module (src/app.js):

<code>  app.get("/user/:id", async (request) => await getUser(request.params.id));
</code>

You can view the differences in these code changes on GitHub.

Delete Express dependencies

The migration of our application from Express to Fastify has been completed! We can now remove Express-related dependencies by running this command in the terminal:

<code>  app.get("/user/:id", async (request, reply) => {
    const name = request.body.name;

    reply.send({ user: { name } });
  });
</code>

You can view the differences in these code changes on GitHub.

Run our migrated application

Now that we have completely migrated the application to Fastify, it is a good time to check if everything is still working as expected. Let's run the same command we ran before when the application was using Express.

First, we will run the application in the terminal:

<code>// src/routes.js
const router = express.Router();

router.get("/:user_id", function getUser(request, response, next) {
  response.json({});
});

export default router;
</code>

Then, in another terminal, we will use cURL to make a request to the API to confirm that it works as expected:

<code>// src/server.js

import Fastify from "fastify";
import ExpressPlugin from "fastify-express";

import routes from "./routes.js";

const fastify = Fastify();

await fastify.register(ExpressPlugin);

fastify.use("/user", routes);

await fastify.listen(3000);
</code>

We should receive a response as follows:

<code>mkdir express-to-fastify-migration
cd express-to-fastify-migration
npm init -y
</code>

Get rid of middleware

Our example Express application uses only some middleware functions, but our real-world Express application may use more. As we can see, the fastify-express plugin allows us to continue using Express middleware (if needed). This allows us to delay rewriting our own custom Express middleware to Fastify plugin. But how do we replace third-party Express middleware?

Luckily, Fastify provides a healthy ecosystem of plugins. Here are some popular Express middleware packages that we can replace with the Fastify plugin:

  • cors ➜ fastify-cors
  • helmet ➜ fastify-helmet
  • csurf ➜ fastify-csrf
  • express-session ➜ fastify-server-session
  • express-jwt ➜ fastify-jwt
  • http-errors ➜ fastify-sensible
  • serve-static ➜ fastify-static
  • multer ➜ fastify-multer

Some Fastify plugins are direct ports or wrappers for their Express counterparts. This means we don't usually need to change the configuration options passed to the Fastify plugin.

You can find a complete list of plugins on the Fastify ecosystem page.

Make full use of Fastify

Now that we have started getting familiar with Fastify by migrating Express applications, it is time to start looking at other Fastify features that we can benefit from.

Verification

Fastify provides request verification function. It uses Ajv (another JSON schema validator) in the background, which allows us to define validation rules using JSON Schema.

This is an example of using JSON mode to verify the request body on a POST route:

<code>npm install express cors
</code>

Verification errors are automatically formatted and sent as JSON response:

<code>"type": "module",
</code>

Learn more in the Fastify verification and serialization documentation.

Log Record

Log logging in Node.js applications can negatively affect performance in production environments. This is because serializing and transferring log data elsewhere (for example, to Elasticsearch) involves many steps. High optimization in this application is very important.

Log logging is fully integrated into Fastify, which means we don't need to spend time selecting and integrating loggers. Fastify uses fast and flexible logger: pino. It generates logs in JSON format:

<code>  app.get("/user/:id", async (request) => await getUser(request.params.id));
</code>

When we create a Fastify server instance, we can enable logging and customize the options passed to pino. Fastify will then automatically output the log message as shown above. The logger instance is available on a Fastify server instance (for example, fastify.log.info("...")) and all request objects (for example, request.log.info("...")).

Learn more in the Fastify logging documentation.

Error handling

Fastify provides a setErrorHandler() method that allows us to explicitly specify the error handling function. This is different from Express, where an error handling middleware can only be distinguished by the parameters it accepts (err, req, res, next) and must be added in a specific order.

For complete flexibility, we can specify different Fastify error handlers in different plugins. Learn more in the Fastify error documentation.

Decorators

Decorators are a powerful feature in Fastify that allow us to customize core Fastify objects—such as our Fastify server instance—as well as request and reply objects. Here is an example of a basic decorator:

<code>  app.get("/user/:id", async (request, reply) => {
    const name = request.body.name;

    reply.send({ user: { name } });
  });
</code>

The decorator allows us to use content like database connections or view engines throughout the Fastify application. Learn more in the Fastify Decorator documentation.

Conclusion

In this article, we learned how to migrate existing Node.js applications from Express to Fastify. We've already learned how the fastify-express plugin can help us gradually migrate existing applications. This allows us to start benefiting from the features provided by Fastify, even if parts of our application are still using Express.

The following are some resources that you might find useful when migrating from Express to Fastify:

  • The sample code in this article. Explore the code and run the application we built in this article.
  • Fastify Documentation. Comprehensive documentation for the Fastify framework.
  • Fastify ecosystem. Fastify plugin directory. Easy to find plugins for replacing Express middleware.
  • Fastify sample application. Sample application created by one of Fastify's main maintainers. It demonstrates core Fastify concepts, best practices, and suggestions.
  • Fastify Community Discord Server. A great place to get help and advice on developing applications with Fastify.

FAQs for Migrating from Express to Fastify

What is the main difference between Express and Fastify?

Express and Fastify are both Node.js web frameworks, but they have some key differences. Express is a minimalist web application framework that provides a simple interface to build web applications and APIs. It has been around for a long time, with a huge community and rich middleware. Fastify, on the other hand, is a newer framework focused on providing the best developer experience with minimal overhead and a powerful plug-in architecture. It is designed very fast, hence the name, and benchmarks show that it can handle more requests per second than Express.

How to migrate my Express application to Fastify?

Migrating from Express to Fastify includes several steps. First, you need to install Fastify and replace the Express instance in the application with a Fastify instance. You then need to replace Express-specific middleware with Fastify plugin or custom code. You also need to update your route to use Fastify's routing system. Finally, you need to update your error handling code to use Fastify's error handling mechanism.

Can I use Express middleware in Fastify?

Fastify has its own middleware system, but it also supports Express-style middleware through the "middie" plugin. However, using Express middleware in Fastify can affect performance, so it is recommended to use Fastify plugins or custom code where possible.

How to handle errors in Fastify?

Fastify has a built-in error handling mechanism that you can use to handle errors in your application. You can define custom error handlers for a specific route or for the entire application. Fastify also supports the async/await syntax, which makes error handling more direct than Express.

How to use hooks in Fastify?

Hook is a powerful feature in Fastify that allows you to run custom code at different stages of the request/response lifecycle. You can use hooks to modify requests or responses, perform authentication, record requests, and more. Fastify supports multiple hooks, including "onRequest", "preHandler", "onSend", and "onResponse".

How to use plugins in Fastify?

Plugin is a key feature of Fastify that allows you to extend the functionality of your application. You can use plugins to add new features, integrate with other services, or encapsulate application logic. Fastify has a rich plugin ecosystem, and you can create your own plugins as well.

How to define a route in Fastify?

Fastify has a powerful routing system that supports parameters, query strings, wildcards, etc. You can define a route using the "route" method, which accepts an option object that specifies the route's methods, URLs, handlers, and other options.

How to send a response in Fastify?

In Fastify, you can send a response using the "reply" object passed to the routing handler. There are several ways to send responses to the "reply" object, including "send", "code", "header", and "type". Fastify also automatically serializes JSON responses for performance.

How to perform verification in Fastify?

Fastify supports request and response verification using JSON Schema. You can define patterns for routes, and Fastify will automatically verify incoming requests and outgoing responses based on these patterns. This helps detect errors early and improve application reliability.

How does Fastify improve performance?

Fastify is designed to be fast and efficient. It uses a lightweight architecture, supports HTTP/2 and HTTP/3, and has a powerful plug-in system that minimizes overhead. Fastify also automatically serializes JSON responses and supports request and response verification, which helps improve performance.

The above is the detailed content of How to Migrate Your App from Express to Fastify. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn