How to create and use plugins with Fastify

overview

Fastify allows you to use plugins to add functionality to your application and reuse shared logic.

Plugins can provide a wide variety of functionality, such as middleware, route handlers, hooks, decorators, and more.

Here I will show you how to create a plugin with Fastify using the following versions:

Fastify v4.0.0
nodejs v19.7.0

Also, all the code I’ve created so far is listed below. https://github.com/wiblok/Fastify

How to define a plugin

The functions that can be provided by plug-ins are as follows.

Plugins are modules that add functionality to your Fastify application.   Examples of usage that are often implemented as plug-ins are as follows.

  1. How to create a plugin that provides route handlers You can add route handlers for new or existing endpoints. This allows you to customize the processing of requests and the generation of responses.
  2. How to create a plugin that provides request validation It can provide functionality for validating and validating requests. Validate input data and ensure it meets required formats and constraints.
  3. How to create plugins to add primitives You can add new primitives (e.g. decorators) to Fastify. This allows you to add reusable processing and functionality throughout your application.
  4. How to create a plugin that encapsulates middleware Plugins can add hooks (middleware) before or after request processing. This allows you to pre-process and/or post-process requests. For example, you can add processing such as authentication and logging to hooks.

How to create a plugin that provides route handlers

route.js

// definition of plugin function
function myPlugin(fastify, options, done) {
  // code that implements the functionality of the plugin

  // add route handler
  fastify.get('/hello', (request, reply) => {
    reply.send({ message: 'Hello from the plugin!' });
  });

  done(); // indicates that the plugin has been registered
}

module.exports = myPlugin;

Commentary

  • definition of plugin functions
function myPlugin(fastify, options, done) {
  // code that implements the functionality of the plugin

  // add route handler
  fastify.get('/hello', (request, reply) => {
    reply.send({ message: 'Hello from the plugin!' });
  });

  done(); // indicates that the plugin has been registered
}

A Fastify plugin is defined as a function that takes a Fastify instance, options, and a callback function that indicates registration is complete. This plugin adds a route handler to handle GET requests to the path /hello. When a request comes in, it will respond with an object containing the message “Hello from the plugin!”. Call done() to notify Fastify when the plugin registration is complete.

  • Export plugin
module.exports = myPlugin;

Export the plugin function myPlugin. This allows you to require and use this plugin from other files.

route-main.js*

const fastify = require('fastify')();
const myPlugin = require('./my-plugin');

const options = {}
fastify.register(myPlugin, options);

fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

Commentary

  • Create a Fastify instance
const fastify = require('fastify')();

Import the Fastify module and create an instance of it.

  • import plugins
const myPlugin = require('./my-plugin');

Import the plugin from a file named my-plugin.js. This file should be in the same directory.

  • Plugin registration
const options = {}
fastify.register(myPlugin, options);

Register the plugin on your Fastify instance. At this point, you can specify options to pass to the plugin. This example passes an empty options object.

  • Start server
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

Start the server with an instance of Fastify listening on port 3000. If any error occurs, print the error message to the console and terminate the process. If there are no errors, print the message “Server is running on port 3000” to the console.

test

curl http://localhost:3000

How to create a plugin that provides request validation

validation plugin

// definition of plugin function
function validationPlugin(fastify, options, done) {
  // schema definition
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

  // Validation function provided by the plugin
  function validateRequest(request, reply, done) {
    const validationResult = fastify.schemas.validate(schema, request.body);
    if (validationResult. error) {
      // if a validation error occurs
      reply.code(400).send({ error: validationResult.error.message });
    } else {
      // if validation succeeds
      request.body = validationResult.value;
      done();
    }
  }

  // indicates that the plugin has been registered
  done();

  // return the functionality of the plugin as an object
  return {
    validateRequest: validateRequest,
  };
}

module.exports = validationPlugin;

Commentary

  • definition of plugin functions
function validationPlugin(fastify, options, done) {
  // schema definition
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

A Fastify plugin is defined as a function that takes a Fastify instance, options, and a callback function that indicates registration is complete. This plugin defines a validation schema. The schema requires that it is an object, that the name property is a string, and the age property is a number greater than or equal to 0. These properties are required.

  • Validation functions provided by plugins
  function validateRequest(request, reply, done) {
    const validationResult = fastify.schemas.validate(schema, request.body);
    if (validationResult. error) {
      // if a validation error occurs
      reply.code(400).send({ error: validationResult.error.message });
    } else {
      // if validation succeeds
      request.body = validationResult.value;
      done();
    }
  }

The plugin provides a validateRequest function to validate the request body. Validation is done using Fastify’s schemas.validate method. If a validation error occurs, return a 400 status code and an error message as a response. If the validation succeeds, update the request body to the value after validation and proceed to the next handler.

  • indicates completion of plugin registration
  done();

Call done() to notify Fastify when the plugin registration is complete.

  • returns the plugin’s functionality as an object
  return {
    validateRequest: validateRequest,
  };
}

A plugin returns the functionality it provides as an object. In this example, we are returning a validateRequest function.

  • Export plugin
module.exports = validationPlugin;

Export the plugin function validationPlugin. This allows you to require and use this plugin from other files.

validation-main

const fastify = require("fastify")();
const validationPlugin = require("./validation-plugin");

// register the plugin
fastify.register(validationPlugin);

// Define route handler
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

// start the server
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

Commentary

  • Create a Fastify instance
const fastify = require("fastify")();

Import the Fastify module and create an instance of it.

  • import plugins
const validationPlugin = require("./validation-plugin");

Import the plugin from a file named validation-plugin.js. This file should be in the same directory.

  • Plugin registration
fastify.register(validationPlugin);

Register the plugin on your Fastify instance.

  • Defining route handlers
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

Define a route handler to handle POST requests to the path /hello. This handler uses the validateRequest function provided by the plugin as a preValidation hook. This will validate the request body before the handler is executed. If the validation succeeds, extract name and age from the request body and return a message containing them as a response.

  • Start server
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

Start the server with an instance of Fastify listening on port 3000. If any error occurs, print the error message to the console and terminate the process. If there are no errors, print the message “Server is running on port 3000” to the console.

test

# test a successful request
curl -X POST -H "Content-Type: application/json" -d '{"name": "John", "age": 25}' http://localhost:3000/hello
# test requests with validation errors
curl -X POST -H "Content-Type: application/json" -d '{"name": "John"}' http://localhost:3000/hello

This code uses Fastify to create a plugin to provide functionality for request validation. The plugin will be registered with your Fastify instance and validated when a request is sent to the specified path. If the validation succeeds, the handler will be executed and an appropriate response will be returned.

How to decorate a plugin to Fstify

decorator-plugin.js

// definition of plugin function
function validationPlugin(fastify, options, done) {
  // schema definition
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

  // Validation function provided by the plugin
  function validateRequest(request, reply, done) {
    console.log("===");
    const validationResult = fastify.validate(schema, request.body);
    if (validationResult. error) {
      // if a validation error occurs
      reply.code(400).send({ error: validationResult.error.message });
    } else {
      // if validation succeeds
      request.validatedBody = validationResult.value;
      done();
    }
  }

  // indicates that the plugin has been registered
  done();

  // Decorate the schema provided function
  fastify.decorate("validateRequest", validateRequest);
}

module.exports = validationPlugin;

Commentary

  • definition of plugin functions
function validationPlugin(fastify, options, done) {
  // schema definition
  const schema = {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number", minimum: 0 },
    },
    required: ["name", "age"],
  };

A Fastify plugin is defined as a function that takes a Fastify instance, options, and a callback function that indicates registration is complete. This plugin defines a validation schema. The schema requires that it is an object, that the name property is a string, and the age property is a number greater than or equal to 0. These properties are required.

  • Validation functions provided by plugins
function validateRequest(request, reply, done) {
  console.log("===");
  const validationResult = fastify.validate(schema, request.body);
  if (validationResult. error) {
    // if a validation error occurs
    reply.code(400).send({ error: validationResult.error.message });
  } else {
    // if validation succeeds
    request.validatedBody = validationResult.value;
    done();
  }
}

The plugin provides a validateRequest function to validate the request body. Validation is done using Fastify’s schemas.validate method. If a validation error occurs, return a 400 status code and an error message as a response. If validation succeeds, add a validatedBody property to the request object and set its value to the value after validation. Then proceed to the next handler.

  • indicates completion of plugin registration
done();

Call done() to notify Fastify when the plugin registration is complete.

  • Decorate schema provided functions
fastify.decorate("validateRequest", validateRequest);

Decorate (add) a validateRequest function to your Fastify instance. This allows you to call this function directly from your Fastify instance.

  • Export plugin
module.exports = validationPlugin;

Export the plugin function validationPlugin. This allows you to import and use this plugin from other files.

decorator-main.js

const fastify = require("fastify")();

// load and register the plugin
const validationPlugin = require("./decorator-plugin");
fastify.register(validationPlugin);

// Define route handler
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

// start the server
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

Commentary

  • Create a Fastify instance
const fastify = require("fastify")();

Import the Fastify module and create an instance of it.

  • Import and register plugins
const validationPlugin = require("./decorator-plugin");
fastify.register(validationPlugin);

Import the plugin from a file named decorator-plugin.js and register it with your Fastify instance. This file should be in the same directory.

  • Defining route handlers
fastify.post(
  "/hello",
  { preValidation: fastify.validateRequest },
  (request, reply) => {
    const { name, age } = request.body;
    reply.send({ message: `Hello ${name}! Your age is ${age}.` });
  }
);

Define a route handler to handle POST requests to the path /hello. This handler uses the validateRequest function provided by the plugin as a preValidation hook. This will validate the request body before the handler is executed. If the validation succeeds, extract name and age from the request body and return a message containing them as a response.

  • Start server
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log("Server is running on port 3000");
});

Start the server with an instance of Fastify listening on port 3000. If any error occurs, print the error message to the console and terminate the process. If there are no errors, print the message “Server is running on port 3000” to the console.

test

# test a successful request
curl -X POST -H "Content-Type: application/json" -d '{"name": "John", "age": 25}' http://localhost:3000/hello
# test requests with validation errors
curl -X POST -H "Content-Type: application/json" -d '{"name": "John"}' http://localhost:3000/hello

This code uses Fastify plugins to add request validation functionality and leverages the decorator pattern to add validation functions to the Fastify instance. This makes it easy to use validation functionality within your route handlers.

How to create a plugin that encapsulates middleware

capsule.js

const fastify = require('fastify')();

const myPlugin = (fastify, options, done) => {
  function myMiddleware(request, reply, next) {
    // middleware processing
    console.log('run middleware');
    next();
  }

  fastify.addHook('preHandler', myMiddleware);

  done();
};

fastify.register(myPlugin);

fastify.get('/', (request, reply) => {
  reply.send({ message: 'Hello, World!' });
});

fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

Commentary

  • Create a Fastify instance
const fastify = require("fastify")();

Import the Fastify module and create an instance of it.

  • plugin definition
const myPlugin = (fastify, options, done) => {
  function myMiddleware(request, reply, next) {
    // middleware processing
    console.log('run middleware');
    next();
  }

  fastify.addHook('preHandler', myMiddleware);

  done();
};

Define a plugin. This plugin defines a middleware function myMiddleware and adds it as a preHandler hook to the Fastify instance. The myMiddleware function will be called before the route handler is executed and will print a message to the console.

  • Plugin registration
fastify.register(myPlugin);

Register the plugin on your Fastify instance.

  • Defining route handlers
fastify.get('/', (request, reply) => {
  reply.send({ message: 'Hello, World!' });
});

Define a route handler to handle GET requests to the path /. When a request comes in, it returns an object { message: 'Hello, World!' } as a response.

  • Start server
fastify.listen({ port: 3000 }, (err) => {
  if (err) {
    console. error(err);
    process.exit(1);
  }
  console.log('Server is running on port 3000');
});

Start the server with an instance of Fastify listening on port 3000. If any error occurs, print the error message to the console and terminate the process. If there are no errors, print the message “Server is running on port 3000” to the console.

test

curl -X GET http://localhost:3000/

This code uses Fastify to create a server and uses plugins to define middleware. Plugins are a highly extensible way to add functionality to your Fastify instance. Middleware is executed before the request reaches the handler, indicating that the middleware’s work is done.