Connect with us

Technology

Migrate Your App From Specific to Fastify – SitePoint


Specific has lengthy been the preferred framework for growing internet functions with Node.js. Sadly, this framework hasn’t seen a lot lively improvement lately. Which means that it doesn’t have help for contemporary JavaScript options. Within the meantime, a variety of new frameworks have emerged which take a distinct method to Node.js software improvement. One among these frameworks is Fastify.

On this article, we’ll have a look at what makes Fastify an interesting different for growing internet functions with Node.js. We’ll find out how we will keep away from the necessity to rewrite our current Specific functions from scratch, and as a substitute migrate them to utilizing Fastify in phases. By the point we’re executed, you’ll have the ability to confidently migrate your current Specific functions and begin leveraging the advantages of the Fastify framework.

There are a number of necessities for following together with this text:

  • You’ll must be snug with making a primary Specific software, defining routes and configuring middleware.
  • You’ll must be snug working instructions in a terminal.
  • You’ll have to have Node.js >= v14.13.0 put in. This supplies us with good help for ECMAScript (ES) modules and permits us to make use of top-level await. The code examples on this article use ES module syntax (import / export).

The entire instance code on this article is obtainable on GitHub so that you can browse, obtain and experiment with.

What are the advantages of migrating from Specific to Fastify?

In the event you’re snug constructing Node.js functions with Specific, you may be questioning what the advantages are of migrating current Specific functions to Fastify. Listed below are some nice causes to think about making the transfer:

  • Validation and logging out of the field. These options are generally required when constructing internet functions. When utilizing Fastify, there’s no want to decide on and combine libraries for these duties, because it supplies them for us. We’ll be taught extra about these options later on this article.

  • Native help for async code. Fastify natively handles guarantees and helps async / await. Which means that routes will catch uncaught rejected guarantees for us. This enables us to put in writing asynchronous code safely. It additionally lets us do neat issues, like robotically ship the return worth from a route handler operate because the response physique:

    app.get("/consumer/:id", async (request) => await getUser(request.params.id));
    
  • Computerized parsing and serialization of JSON. We don’t have to configure Fastify to parse JSON request our bodies, or to serialize objects as JSON for responses. It handles all of this robotically for us:

    app.get("/consumer/:id", async (request, reply) => {
      const identify = request.physique.identify;
    
      reply.ship({ consumer: { identify } });
    });
    
  • Developer pleasant. With specific and expressive APIs, in addition to glorious help for TypeScript, Fastify has been designed with developer expertise in thoughts.

  • It’s quick. We by no means need a framework to turn into the supply of efficiency bottlenecks in our functions. The excellent news is that Fastify has been constructed to be extremely performant. The Fastify benchmarks present the way it compares towards different Node.js internet frameworks.

  • In lively improvement. The Fastify framework is being actively developed. There are common releases with enhancements and bug/safety fixes.

Migrate an API with Confidence

We need to be assured that our software continues to be working as anticipated after it has been migrated to Fastify. One of many issues which might help us catch bugs or establish unintended adjustments is API integration exams.

Integration exams train the parts of an software another way to unit exams. Unit exams train the features of particular person parts on their very own. Integration exams permit us to confirm the conduct of a number of parts working collectively.

If we write API integration exams for an Specific software, we would like to have the ability to run those self same exams as soon as we’ve migrated the appliance to Fastify. When writing integration exams for an API, there are a number of key issues to think about:

  • They shouldn’t be tied to a selected framework. We wish to have the ability to run the identical exams earlier than and after migration, with out the necessity to change the exams or any of the libraries that we’re utilizing for them.

  • Maintain them easy. At a minimal, the mixing exams ought to make requests to the endpoints which an API exposes and confirm {that a} response is returned, however typically not way more. We’d need to examine for particular HTTP standing codes or response headers, however we must always attempt to hold the exams so simple as attainable.

  • Decide instruments you’re snug with. There are many totally different instruments which might help us with creating and working API exams, but it surely’s vital to make use of instruments which we’re snug with. To write down efficient integration exams, we’d like to have the ability to make HTTP requests and make assertions towards the responses from our API. On the whole, we don’t want a number of libraries or instruments to make this work.

We gained’t be digging into the main points of find out how to implement API integration exams on this article, however they’re one thing you need to take into account writing earlier than enterprise a framework migration.

Transitioning from Specific to Fastify with fastify-express

The concept of migrating an current Specific software to a very totally different framework can appear fairly daunting. Thankfully, the Fastify staff have created a plugin — fastify-express — which might help ease the migration path.

The fastify-express plugin provides full Specific compatibility to Fastify. It supplies a use() methodology which we will use so as to add Specific middleware and routes to our Fastify server. This provides us the choice of steadily migrating elements of an current Specific software over to Fastify.

Right here’s an instance of Specific router:


const router = categorical.Router();

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

export default router;

We are able to then use fastify-express so as to add our current Specific router to a Fastify server occasion:



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

import routes from "./routes.js";

const fastify = Fastify();

await fastify.register(ExpressPlugin);

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

await fastify.hear(3000);

We’ll discover the main points of how this all works after we begin migrating our software to Fastify slightly later.

It’s vital to bear in mind that utilizing the fastify-express plugin just isn’t a long-term answer. If we need to get the total advantages of Fastify, we’ll have to migrate our Specific-specific software code sooner or later. Nonetheless, the fastify-express plugin supplies us with the chance for a phased migration to Fastify.

Our Instance Specific Software

We’re going to construct an instance Specific software after which migrate it to make use of the Fastify framework. Let’s check out the code for it now.

Required dependencies

First, let’s create a brand new venture:

mkdir express-to-fastify-migration
cd express-to-fastify-migration
npm init -y

Then, we’ll run this command in our terminal to put in the dependencies which our Specific software would require:

npm set up categorical cors

Lastly, open up package deal.json and add the next line above the scripts part:

"sort": "module",

This may permit us to load ES modules in our app.

The router module

We’re going to create an Specific router occasion to assist us encapsulate our routes and middleware. Routers in Specific can be utilized to assist us set up our software into discrete modules. For instance, we would have one router for /consumer routes and one other router for /handle routes. We’ll see later how this might help us migrate our Specific software to Fastify in phases.

Let’s create a router occasion and add some middleware to it:



import categorical from "categorical";
import cors from "cors";

const router = categorical.Router();

router.use(categorical.json());

router.use(cors({ origin: true }));

Within the code above, we’ve configured two examples of Specific middleware:

  • categorical.json(). This middleware operate is inbuilt to Specific. It handles parsing JSON request our bodies.
  • cors. This middleware helps us add CORS headers to our API responses. It’s going to permit our API to be referred to as from an online web page.

These middleware instruments shall be run for any requests that are made to routes that we outline on this router.

Now that we’ve configured the middleware, we will add the primary path to our router:



router.publish("https://www.sitepoint.com/", operate createUser(request, response, subsequent) {
  const newUser = request.physique;

  if (!newUser) {
    return subsequent(new Error("Error creating consumer"));
  }

  response.standing(201).json(newUser);
});

In an actual software, the route handler operate above would validate the information which it has obtained, after which name a database to create a brand new consumer file. For this instance, we’re sending the information we’ve obtained because the response physique.

Now we’ll add a route for retrieving a consumer:



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

  response.json(consumer);
});

As with the POST route, the route handler above would usually make a name to a database to retrieve the consumer knowledge, however for this instance we’ve laborious coded an object to ship within the response physique.

Lastly we’ll export the router object in order that we will import it in one other module:



export default router;

The app module

Now we’re going to create an app module:



import categorical from "categorical";

import routes from "./routes.js";

export default operate buildApp() {
  const app = categorical();

  app.use("/consumer", routes);

  return app;
}

On this module, we’re defining a operate which creates a brand new Specific server occasion. We then add our router object to the server occasion.

The server module

Lastly, we’ll create a server module. This module makes use of the buildApp() operate we outlined in our app module to create a brand new Specific server occasion. It then begins our Specific server by configuring it to hear on port 3000:



import buildApp from "./app.js";

const categorical = buildApp();

categorical.hear(3000, () => {
  console.log("Instance app listening at http://localhost:3000");
});

Operating our software

We now have an entire functioning Specific software that we will run in our terminal:

node src/server.js

In a separate terminal, we will make a request to the API with cURL to verify that it’s working:

curl --verbose --request GET 
  --url http://localhost:3000/consumer/3d395cb4-531c-4989-b8ed-9cc75198187e 
  --header 'Origin: http://example-origin.com'

We must always obtain a response which appears like this:

< HTTP/1.1 200 OK
< X-Powered-By: Specific
< Entry-Management-Enable-Origin: http://example-origin.com
< Differ: Origin
< Content material-Sort: software/json; charset=utf-8
< 

{"id":"3d395cb4-531c-4989-b8ed-9cc75198187e","first_name":"Bobinsky","last_name":"Oso"}

Migrating Our Software from Specific to Fastify

Now that we’ve a totally useful Specific software, we’re going emigrate it to make use of the Fastify framework.

Required dependencies

We have to set up three dependencies:

Let’s run this command in our terminal to put in them:

npm set up fastify fastify-express fastify-cors

You may view the diff of those code adjustments on GitHub.

Refactoring our app module

Now that we’ve our dependencies put in, we have to refactor our app module. We’re going to vary it to:

  • import fastify and fastify-express as a substitute of categorical
  • create a Fastify server occasion as a substitute of an Specific server occasion
  • use the fastify-express plugin so as to add our Specific router object to the server

That is what it appears like after we’ve made these adjustments:



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

import routes from "./routes.js";

export default async operate buildApp() {
  const fastify = Fastify({
    logger: true,
  });

  await fastify.register(ExpressPlugin);

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

  return fastify;
}

You may view the diff of those code adjustments on GitHub.

You’ll discover within the code above that we’re passing the logger choice after we create our Fastify server occasion. This allows Fastify’s built-in logging performance. We’ll be taught extra about this afterward.

Refactoring our server module

We now want to vary our server module to work with a Fastify server occasion:



import buildApp from "./app.js";

const fastify = await buildApp();

strive {
  await fastify.hear(3000);
} catch (error) {
  fastify.log.error(error);
  course of.exit(1);
}

You may view the diff of those code adjustments on GitHub.

As Fastify has native help for guarantees, within the code above we’re in a position to make use of await after which catch and log any errors with Fastify’s built-in logging performance.

Subsequent steps

Our software is now utilizing Fastify to route requests and ship responses. It’s absolutely useful, however Specific continues to be being utilized by our routes. As a way to absolutely migrate away from Specific, we have to migrate our routes to make use of Fastify as nicely.

Refactoring our routes module

The routes in our Specific software are encapsulated in an Specific router. We’re going to refactor this router right into a Fastify plugin. Plugins are a characteristic of Fastify which permit us to encapsulate routes and any associated performance.

We’ll begin refactoring our routes module (src/routes.js) by eradicating among the Specific-specific strains:

-  import categorical from "categorical"

-  const router = categorical.Router();

-  router.use(categorical.json());

We then want to vary the default module export to be an async operate which accepts the Fastify server occasion. That is the idea of a Fastify plugin. The remaining code in our routes module shall be moved inside this plugin operate:

export default async operate routes(fastify) {
  
}

To make our middleware and routes work with Fastify, we have to change:

  • router references to fastify
  • route handler features to be async
  • route handler operate arguments from (request, response, subsequent) to (request, reply)
  • response references to reply
  • calls to response.json() to reply.ship()
  • cases of subsequent(error) to throw error

After making all of those adjustments, our routes module is now a Fastify plugin containing Fastify routes:



import cors from "cors";

export default async operate routes(fastify) {
  fastify.use(cors({ origin: true }));

  fastify.publish("https://www.sitepoint.com/", async operate createUser(request, reply) {
    const newUser = request.physique;

    if (!newUser) {
      throw new Error("Error creating consumer");
    }

    reply.standing(201).ship(newUser);
  });

  fastify.get("/:user_id", async operate getUser(request, reply) {
    const consumer = {
      id: request.params.user_id,
      first_name: "Bobinsky",
      last_name: "Oso",
    };

    reply.ship(consumer);
  });
}

We now want to vary our app module (src/app.js) to make use of the plugin which we’re exporting from the routes module. This implies changing the fastify.use() name with a name to fastify.register():

-  fastify.use("/consumer", routes);
+  fastify.register(routes, { prefix: "/consumer" });

You may view the diff of those code adjustments on GitHub.

Our instance Specific software solely has one router, so we have been capable of migrate all the routes in our software to make use of Fastify in a single go. Nonetheless, if we’ve a bigger Specific software with a number of routers, we may steadily migrate every router over to Fastify one by one.

Changing middleware with plugins

Our software is in fine condition, and we’ve virtually fully migrated it from Specific to Fastify. There’s one factor left emigrate: our use of the cors Specific middleware package deal. We put in the fastify-cors plugin earlier, and now we have to add it in our software to interchange the cors middleware.

In our routes module (src/routes.js), we have to change the import of the cors middleware:

-  import cors from "cors";
+  import CorsPlugin from "fastify-cors";

We then want to interchange the decision to fastify.use() with a name to fastify.register():

-  fastify.use(cors({ origin: true }));
+  fastify.register(CorsPlugin, { origin: true });

Be aware how, after we register the plugin with Fastify, we have to move within the plugin operate and the choices object as separate arguments.

As we’re not utilizing the use() operate which the fastify-express plugin supplies, we will take away it fully from our software. To do that, let’s delete the next strains from our app module (src/app.js):

-  import ExpressPlugin from "fastify-express";

-  await fastify.register(ExpressPlugin);

You may view the diff of those code adjustments on GitHub.

Eradicating Specific dependencies

The migration of our software from Specific to Fastify is full! We are able to now take away the Specific-related dependencies by working this command in our terminal:

npm uninstall categorical cors fastify-express

You may view the diff of those code adjustments on GitHub.

Operating our migrated software

Now that we’ve absolutely migrated our software to Fastify, it’s time to examine that every part continues to be working as we count on it to. Let’s run the identical instructions which we ran earlier when our software was utilizing Specific.

First, we’ll run the appliance in our terminal:

node src/server.js

Then, in a separate terminal, we’ll make a request to the API with cURL to verify that it’s working as anticipated:

curl --verbose --request GET 
  --url http://localhost:3000/consumer/3d395cb4-531c-4989-b8ed-9cc75198187e 
  --header 'Origin: http://example-origin.com'

We must always obtain a response which appears like this:

< HTTP/1.1 200 OK
< range: Origin
< access-control-allow-origin: http://example-origin.com
< content-type: software/json; charset=utf-8
< 

{"id":"3d395cb4-531c-4989-b8ed-9cc75198187e","first_name":"Bobinsky","last_name":"Oso"}

Transferring Away from Middleware

Our instance Specific software solely used a few middleware features, however our real-world Specific functions are probably utilizing many extra. As we’ve seen, the fastify-express plugin permits us to proceed utilizing Specific middleware if we have to. This enables us to defer rewriting our personal customized Specific middleware into Fastify plugins. However what can we do about changing third-party Specific middleware?

Thankfully for us, there’s a wholesome ecosystem of plugins obtainable for Fastify. Listed below are among the fashionable Specific middleware packages which we will change with Fastify plugins:

A number of the Fastify plugins are direct ports of — or wrappers round — their Specific counterparts. This implies we frequently gained’t want to vary the configuration choices which we move to the Fastify plugin.

You will discover a complete checklist of plugins on the Fastify Ecosystem web page.

Making the Most of Fastify

Now that we’ve began to get snug with Fastify by migrating an Specific software, it’s time to start out taking a look at different Fastify options which we will profit from.

Validation

Fastify supplies options for request validation. It makes use of Ajv (One other JSON schema validator) beneath the hood, which permits us to outline validation guidelines with JSON Schema.

Right here’s an instance which makes use of a JSON schema to validate the request physique on a POST route:

const schema = {
  physique: {
    sort: "object",
    required: ["first_name"],
    properties: {
      first_name: { sort: "string", minLength: 1 },
    },
  },
};

app.publish("/consumer", { schema }, async (request, reply) => {
  reply.ship(request.physique);
});

Validation errors are robotically formatted and despatched as a JSON response:

{
  "statusCode": 400,
  "error": "Unhealthy Request",
  "message": "physique ought to have required property 'first_name'"
}

Study extra within the Fastify Validation and Serialization documentation.

Logging

Logging in Node.js functions can have a unfavourable impression on efficiency in manufacturing. It is because there are numerous steps concerned in serializing and transporting log knowledge elsewhere (for instance, to Elasticsearch). It’s vital that this facet of our functions is extremely optimized.

Logging is absolutely built-in in Fastify, that means that we don’t have to spend time selecting and integrating a logger. Fastify makes use of a quick and versatile logger: pino. It produces logs in JSON format:

{"stage":30,"time":1615881822269,"pid":14323,"hostname":"localhost","msg":"Server listening at http://127.0.0.1:3000"}
{"stage":30,"time":1615881829697,"pid":14323,"hostname":"localhost","reqId":"req-1","req":{"methodology":"GET","url":"/consumer/abc123","hostname":"localhost:3000","remoteAddress":"127.0.0.1","remotePort":38238},"msg":"incoming request"}
{"stage":30,"time":1615881829704,"pid":14323,"hostname":"localhost","reqId":"req-1","res":{"statusCode":200},"responseTime":6.576989000663161,"msg":"request accomplished"}

After we create a Fastify server occasion, we will allow logging and customise the choices that are handed to pino. Fastify will then robotically output log messages like these proven above. The logger occasion is accessible on the Fastify server occasion (reminiscent of fastify.log.data("...")) and on all Request objects (reminiscent of request.log.data("...")).

Study extra within the Fastify Logging documentation.

Error dealing with

Fastify supplies a setErrorHandler() methodology which permits us to explicitly specify a operate for error dealing with. That is totally different from Specific, the place error dealing with middleware can solely be distinguished by the parameters it accepts (err, req, res, subsequent), and have to be added in a selected order.

For full flexibility, we will specify totally different Fastify error handlers in numerous plugins. Study extra within the Fastify Errors documentation.

Decorators

Decorators are a strong characteristic in Fastify which permit us to customise core Fastify objects — reminiscent of our Fastify server occasion — and request and reply objects. Right here’s an instance of a primary decorator:

fastify.register(async (fastify, choices) => {

  fastify.embellish("yolo", () => {
    return { yo: "lo" };
  });

  fastify.get("/yolo", async (request, reply) => {
    
    reply.ship(this.yolo());
  });

});

Decorators permit us to make issues like database connections or view engines obtainable all through our Fastify software. Study extra within the Fastify Decorators documentation.

Conclusion

On this article, we’ve realized find out how to migrate an current Node.js software from Specific to Fastify. We’ve seen how the fastify-express plugin might help us steadily migrate our current functions. This enables us to start out benefiting from the options which Fastify supplies, even whereas elements of our software are nonetheless utilizing Specific.

Listed below are some assets you may discover useful as you progress from Specific to Fastify:

Click to comment

Leave a Reply

Your email address will not be published. Required fields are marked *