Introduction to Fastify: A practical guide to building Node.js web apps

Published on June 23, 2025

what-is-fastify-header1

Fastify is a lightweight and flexible Node.js web framework used to build dynamically generated web pages and high-performance API back ends. It focuses on being developer-friendly and helps bootstrap your apps with a high-performance, extensible framework to base your own code on.

This post explains Fastify and its advantages and use cases, and it provides practical tutorial steps to get you started building your own Fastify apps.

What is Fastify?

Fastify is a web framework for Node.js. It places a priority on performance, having low overheads, and it has built-in security features and long-term support.

Fastify gives you a foundation for building web services, including dynamically generated web pages (web pages that are rendered server-side) and REST APIs (programmatic back ends that you can connect standalone client applications to, including web apps, native mobile apps, automation scripts, and even AI assistants).

Fastify provides common web framework functionality such as routes, error handling, form and JSON data parsing, validation and serialization, and hooks that let you tap into Fastify's request and response lifecycle. You can extend Fastify using plugins that you can either develop yourself or find in the extensive Fastify plugin ecosystem. Plugins allow you to (nearly) effortlessly add extra functionality to Fastify like authentication, environment variables, page view templates, and database connectivity.

You can host Fastify manually on your own servers using Node.js or deploy it using Docker or Kubernetes. It’s also supported in many serverless environments such as AWS Lambda, Google Cloud Functions, and Vercel.

Fastify vs. Express

Fastify was inspired by the Express web framework with the aim of fixing some long-term pain points developers had experienced with Express. This included performance issues, a lack of built-in support for JSON schemas for validation and serialization, and async support. Fastify's plugin system is designed to provide better performance and modularity than Express middleware (up until recently, Fastify even supported Express middleware, but it now requires a plugin for this). These differences and additional features can make Fastify a little more complex to use, but may reduce the overall work required to bring a project into production.

Compared to Express, Fastify promotes a more structured approach to development, improving both long-term maintainability and scalability. Both Node.js web frameworks support TypeScript (though Fastify has better native TypeScript support), and you can use both to build APIs and dynamically generated websites. API services built using Fastify and Express can be used to build components in a microservices-based architecture.

Express version 5 onwards can automatically handle async errors, bringing it up to speed with Fastify in this area. Fastify includes hot-reloading and Pino for logging, whereas with Express you need to integrate additional packages for this developer-friendly functionality.

Whether Fastify or Express is the best fit for your project is a matter of developer preference and feature requirements: Express is well suited for simple applications, while Fastify gives better performance and more control with decorators, hooks, and plugins. You should assess which features each Node.js web framework includes; what can be added with reputable, well-supported plugins; and what functionality you will need to implement yourself to decide which is most suitable.

How to build a Node.js web application with Fastify

Here's a GitHub repo containing this tutorial's completed code if you want to get right into it.

Creating a new Fastify app

You can manually install Fastify and create the necessary files, add it to an existing Node.js project, or use create-fastify to create a new project with everything you need to start building with the Fastify web framework.

To use create-fastify, you'll need the latest version of Node.js installed. Then run the following command:

npm init fastify my-fastify-app

This will create a new Node.js Fastify app with the following files and directories:

  • A README.md file for documenting your project (pre-populated with instructions on how to launch your new Fastify app).

  • A package.json file listing your project’s details and dependencies.

  • app.js, which is run when you launch your Fastify app

  • The /routes directory, which contains a default root.js file for / (which returns a JSON response) as well as an /example route (which returns a text/HTML response).

  • /plugins for storing plugins, with an example plugin.

  • A /test directory for storing code for testing your app logic.

Next, run the following command in the directory created by create-fastify to install dependencies:

npm install

This tutorial won't cover tests; however, they are vital in complex applications and can greatly reduce the work required to maintain large codebases by automating the testing of new updates.

Creating a new page using EJS templates

The example pages generated by create-fastify show you how to create routes that return simple JSON and text responses. Manually generating the HTML required for more complex web pages (including metadata, navigation, headers, footers, and so on) is laborious. 

This is where templates come in: they are modular, reusable chunks of HTML that you can insert your page data into, rather than fully coding each page for each route. You can use the EJS template library for this, and you can enable it in Fastify using the point-of-view plugin. Install both by running:

npm install @fastify/view ejs

To add the @fastify/view plugin, insert the following code right after the const Autoload = require(‘fastify/autoload’) line in your app.js file:

const fastifyView = require("@fastify/view");

In the same file, add the following code under the line that says // Place here your custom code! to register the plugin:

You'll then need to create a /views directory for storing your EJS templates. Within it, create a file named display_time.ejs and add the following code:

Next, you can create a new route for rendering a page generated using an EJS template by creating a directory named get_time in the existing routes directory. Then, create a file named index.js within the get_time directory and add the following code:

This demonstrates a dynamically generated page that uses calculated data (the current time), and passes its value to a reusable template. This means that you do not need to include the template code in your route file, separating the display aspect into a different file and making the code much easier to read and maintain (following the model-view-controller pattern). You could also reuse the template file for different routes that display similar information, reducing the amount of code overall.

create-fastify has done a bit of legwork here for you: Node.js modules in the /routes directory are automatically loaded by app.js as Fastify routes, with the URL based on the folder path. You can list the routes in a Fastify app using the fastify-print-routes plugin, which can come in useful when developing complex apps and APIs.

You can run the app using the command:

npm run dev

You can access this page at the address http://localhost:3000/get_time

what-is-fastify-image1

Creating forms and validating data with Fastify 

Fastify can process form content from a HTTP request to a route using the @fastify/formbody plugin. Install it by running:

npm install @fastify/formbody

Add the following line to app.js (below where you registered fastifyView described earlier) to register the plugin:

fastify.register(require('@fastify/formbody'));

Next, create the template file for the HTML form in /views/shopping_list.ejs and add the following code to it:

Then, create /routes/shopping_list/index.js with the following code:

This route and view template displays the shopping list that is saved to a global variable (meaning it will persist between page loads, but not if the app is restarted), and a form for submitting new shopping list items. Validation and serialization are handled using Fastify's built-in JSON schema support.

You can access this page at the following address: http://localhost:3000/shopping_list

what-is-fastify-image2

Saving data to a database with Fastify and Prisma

For long-term data storage that will survive your app restarting (or crashing), you need to use a database. While you can manually create a database and write your own queries for saving and loading data to it, an ORM like Prisma can do a lot of the work for you, and allows you to easily swap out which database you're using (in this case, SQLite) with a different one between development and production. Install Prisma by running the following command in your project directory:

npm install prisma @prisma/client 

Next, initialize Prisma:

npx prisma init

And edit the generated prisma/schema.prisma file to contain the following code:

This tells Prisma the structure of the database, including the shopping list items and the fields that will need to be stored (a unique id, name, price, and the time the item was added).

Update the DATABASE_URL environment variable in the .env file:

DATABASE_URL="file:./shopping_dev.db"

Note that the URL should be the path to the SQLite database file relative to your apps directory.

Generate the database file, apply the schema to it, and create the Prisma client by running the Prisma migrate command:

npx prisma migrate dev --name init

You must have created the schema.prisma file before this, as the database and client will be created using it.

Finally, replace your code in /routes/shopping_list/index.js with the following code, which uses Prisma to handle loading and saving shopping list items:

The above code uses the Prisma findMany() and create() functions to load and save database data.

As this code is using a live database, you should switch to using the npm run start command instead of npm run dev to run your app. This is because the dev script passes a -w watch flag to the fastify command, ensuring the server gets restarted when any code files change, and you don't want this to happen for database files since it causes unexpected restarts and errors. You can see the difference between the start and dev scripts in your package.json file.

Custom errors in Fastify

By default, the above code will return a HTTP 400 status with a JSON error when invalid data is submitted (for example, a price that is less than zero): 

what-is-fastify-image4

Instead, you probably want to tell users what they entered wrong and provide a link back to the form. This can be done with a custom error page.

Create an EJS template for the validation error in the file views/validation_error.ejs:

You may also want to pass validation errors back to the form view and display the messages alongside their respective form elements, or use live validation. You can also use the include call feature of EJS to embed templates within other templates.

Register the custom error handler in app.js by adding the following code below the line that says // Place here your custom code!:

Then, when a 400 error occurs and validation error is present, the template will be rendered with information from the error, rather than the default JSON response.

what-is-fastify-image3

If you want to log unexpected errors for later debugging, you can enable logging and take advantage of Fastify's built-in support for Pino.

What else can you do with Fastify?

Fastify isn't just for building server-side rendered apps as shown in the above example. You can also build APIs that can provide data and functionality to mobile, web, desktop, and other applications (including autonomous AI agents!) in an API-first development approach, or even use Fastify in a microservices-based architecture. You're then free to build any kind of frontend application using JavaScript frameworks such as React, Angular, or Svelte, and even create different front ends targeting different devices and use cases.

You can also connect Fastify apps and services to other services in a composable architecture. By leveraging SaaS platforms that provide pre-built ecommerce, content delivery, communication, and other common functionality, you can greatly reduce the work required to implement and maintain the features your unique app requires.

Contentful: A fully managed, ready-to-use content and delivery platform for your Fastify apps 

Fastify is ideal for coding the business logic you require to make your apps unique; however, you don't need to use it to build your entire back end. Content management, optimization, and delivery are often monumental tasks that, while vital, take up unnecessary development time and add ongoing infrastructure and maintenance overheads to your apps.

Using the Contentful platform to upload, curate, and deliver all of your images, text, videos, and other content, greatly streamlines the development process. Your content is delivered to your audience faster using our global CDN accessed via REST and GraphQL APIs, meaning less infrastructure overheads and a better end user experience. We even provide SDKs for popular languages and frameworks. Integrating Contentful with your Fastify app is as simple as adding the Contentful JavaScript SDK in either your frontend or backend code (depending on where you wish to load your content): 

Then, use the Contentful client to load your content, and standard JavaScript, HTML, and CSS to render it: 

You can see the Contentful JavaScript SDK in action, including the rendering of content, in this JSFiddle

You can customize Contentful for editing rich text content, uploading images, and optimizing them automatically. Contentful even provides live previews of exactly what your published content will look like to your users. Our super fast global content delivery network then makes sure your assets load as fast as possible, wherever they’re needed — from your customers’ mobile devices to highway-spanning billboards.

Subscribe for updates

Build better digital experiences with Contentful updates direct to your inbox.

Meet the authors

David Fateh

David Fateh

Software Engineer

Contentful

David Fateh is a software engineer with a penchant for web development. He helped build the Contentful App Framework and now works with developers that want to take advantage of it.

Related articles

Icons and logo's representing HTMX vs React
Guides

HTMX vs. React: Understanding their strengths and use cases

January 9, 2025

React development concepts shown with brain icons and labels including State management, Hooks, Memory, and useState
Guides

React useState hook: Complete guide and tutorial

May 8, 2025

A WordPress to Contentful migration takes a bit of up-front work, but results in big long-term benefits. Here's why, and what's involved.
Guides

WordPress to Contentful migration: Benefits, tips, and best practices

February 27, 2019

Contentful Logo 2.5 Dark

Ready to start building?

Put everything you learned into action. Create and publish your content with Contentful — no credit card required.

Get started