Express has long been the most popular framework for developing web applications with Node.js. Unfortunately, this framework hasn’t seen much active development in recent years. This means that it doesn’t have support for modern JavaScript features. In the meantime, a number of new frameworks have emerged which take a different approach to Node.js application development. One of these frameworks is Fastify.
In this article, we’ll look at what makes Fastify an appealing alternative for developing web applications with Node.js. We’ll learn how we can avoid the need to rewrite our existing Express applications from scratch, and instead migrate them to using Fastify in phases. By the time we’re done, you’ll be able to confidently migrate your existing Express applications and start leveraging the benefits of the Fastify framework.
There are a few requirements for following along with this article:
- You’ll need to be comfortable with creating a basic Express application, defining routes and configuring middleware.
- You’ll need to be comfortable running commands in a terminal.
- You’ll need to have Node.js >= v14.13.0 installed. This provides us with good support for ECMAScript (ES) modules and allows us to use top-level await. The code examples in this article use ES module syntax (
import
/export
).
All of the example code in this article is available on GitHub for you to browse, download and experiment with.
What are the benefits of migrating from Express to Fastify?
Table of Contents
If you’re comfortable building Node.js applications with Express, you might be wondering what the benefits are of migrating existing Express applications to Fastify. Here are some great reasons to consider making the move:
Validation and logging out of the box. These features are commonly required when building web applications. When using Fastify, there’s no need to choose and integrate libraries for these tasks, as it provides them for us. We’ll learn more about these features later in this article.
Native support for async code. Fastify natively handles promises and supports
async
/await
. This means that routes will catch uncaught rejected promises for us. This allows us to write asynchronous code safely. It also lets us do neat things, like automatically send the return value from a route handler function as the response body:app.get("/user/:id", async (request) => await getUser(request.params.id));
Automatic parsing and serialization of JSON. We don’t need to configure Fastify to parse JSON request bodies, or to serialize objects as JSON for responses. It handles all of this automatically for us:
app.get("/user/:id", async (request, reply) => { const name = request.body.name; reply.send({ user: { name } }); });
Developer friendly. With explicit and expressive APIs, as well as excellent support for TypeScript, Fastify has been designed with developer experience in mind.
It’s fast. We never want a framework to become the source of performance bottlenecks in our applications. The good news is that Fastify has been built to be highly performant. The Fastify benchmarks show how it compares against other Node.js web frameworks.
In active development. The Fastify framework is being actively developed. There are regular releases with improvements and bug/security fixes.
How to Migrate an API with Confidence
We want to be confident that our application is still working as expected after it has been migrated to Fastify. One of the things which can help us catch bugs or identify unintended changes is API integration tests.
Integration tests exercise the components of an application in a different way to unit tests. Unit tests exercise the functions of individual components on their own. Integration tests allow us to verify the behavior of multiple components working together.
If we write API integration tests for an Express application, we want to be able to run those same tests once we’ve migrated the application to Fastify. When writing integration tests for an API, there are a few key things to consider:
They shouldn’t be tied to a specific framework. We want to be able to run the same tests before and after migration, without the need to change the tests or any of the libraries that we’re using for them.
Keep them simple. At a minimum, the integration tests should make requests to the endpoints which an API exposes and verify that a response is returned, but generally not much more. We might want to check for specific HTTP status codes or response headers, but we should try to keep the tests as simple as possible.
Pick tools you’re comfortable with. There are lots of different tools which can help us with creating and running API tests, but it’s important to use tools which we’re comfortable with. To write effective integration tests, we need to be able to make HTTP requests and make assertions against the responses from our API. In general, we don’t need a lot of libraries or tools to make this work.
We won’t be digging into the details of how to implement API integration tests in this article, but they’re something you should consider writing before undertaking a framework migration.
Transitioning from Express to Fastify with fastify-express
The idea of migrating an existing Express application to a completely different framework can seem quite daunting. Fortunately, the Fastify team have created a plugin — fastify-express — which can help ease the migration path.
The fastify-express
plugin adds full Express compatibility to Fastify. It provides a use()
method which we can use to add Express middleware and routes to our Fastify server. This gives us the option of gradually migrating parts of an existing Express application over to Fastify.
Here’s an example of Express router:
// src/routes.js const router = express.Router(); router.get("/:user_id", function getUser(request, response, next) { response.json({}); }); export default router;
We can then use fastify-express
to add our existing Express router to a Fastify server instance:
// 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);
We’ll explore the details of how this all works when we start migrating our application to Fastify a little later.
It’s important to be aware that using the fastify-express
plugin is not a long-term solution. If we want to get the full benefits of Fastify, we’ll need to migrate our Express-specific application code at some point. However, the fastify-express
plugin provides us with the opportunity for a phased migration to Fastify.
Continue reading How to Migrate Your App From Express to Fastify on SitePoint.