DEV Community

Cover image for A Comprehensive Guide to Building Node APIs with esbuild
Francisco Mendes
Francisco Mendes

Posted on • Updated on

A Comprehensive Guide to Building Node APIs with esbuild

One of the most interesting things about building an api in Node is the fact that we can choose the tooling we want to use or experiment with. And one of the things we can choose is our compiler, in the case of this article we will be interested in transpiling transpiling the code from TypeScript to JavaScript in a quick way during the development process. Just like we want to have that quick build to use in our production environment.

To be honest, I'm a big fan of SWC, but I had no idea that there were dependencies that help us configure a project with esbuild in a super fast and simple way. It's so easy but so easy, to the point that we can make the migration from babel to esbuild in the blink of an eye.

Project Setup

First let's start with the usual one, which is to create the project folder:

mkdir ts-esbuild
cd ts-esbuild
Enter fullscreen mode Exit fullscreen mode

Next, initialize a TypeScript project and add the necessary dependencies:

npm init -y
npm install -D typescript @types/node
Enter fullscreen mode Exit fullscreen mode

Next, create a tsconfig.json file and add the following configuration to it:

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "allowJs": true,
    "removeComments": true,
    "resolveJsonModule": true,
    "typeRoots": [
      "./node_modules/@types"
    ],
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": [
      "esnext"
    ],
    "baseUrl": ".",
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": "Node",
    "skipLibCheck": true,
  },
  "include": [
    "src/**/*"
  ],
  "exclude": ["node_modules"],
}
Enter fullscreen mode Exit fullscreen mode

Now with our TypeScript environment configured, we can now install the dependencies we need to use the esbuild, for the build and development process (with live reloading).

npm install -D nodemon esbuild esbuild-node-tsc
Enter fullscreen mode Exit fullscreen mode

With these dependencies installed we can configure nodemon.json:

{
  "watch": ["src"],
  "ignore": ["src/**/*.test.ts", "node_modules"],
  "ext": "ts,mjs,js,json,graphql",
  "exec": "etsc && node ./dist/server.js",
  "legacyWatch": true
}
Enter fullscreen mode Exit fullscreen mode

Now in our package.json we are going to specify that we are going to use ESM and we are going to add the following scripts:

{
  // ...
  "main": "server.js",
  "scripts": {
    "dev": "nodemon",
    "build": "etsc",
    "start": "node dist/server.js"
  },
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Whenever nodemon sees a change in our source code, as soon as there is one, it builds the folder and then reloads the api. However, the code being executed is not TypeScript but the JavaScript code that will be saved in the dist folder.

Finally, we can create a simple api:

// @/src/server.ts
import fastify, {
  FastifyRequest,
  FastifyReply,
  FastifyInstance,
} from "fastify";

const startServer = async (): Promise<FastifyInstance> => {
  const app = fastify();

  app.get("/", async (request: FastifyRequest, reply: FastifyReply): Promise<FastifyReply> => {
    return reply.send({ hello: "world" });
  });

  return app;
};

startServer()
  .then((app) => app.listen(3333))
  .catch(console.error);
Enter fullscreen mode Exit fullscreen mode

Creating an api in Node using TypeScript along with the esbuild is as simple as this, literally. One thing I want to mention, but I think you've noticed this, esbuild-node-tsc takes into account the tsconfig.json configuration but the build is done with esbuild.

If you want to try it out without having to configure all this (although it wasn't much), you can clone this repository. Have a nice day 👊

Top comments (4)

Collapse
 
ddialar profile image
Dailos Rafael Díaz Lara

Nice post. It helped me a lot in order to configure a new project using esbuild.

I would like to provide an additional point. In my case, I usually work with path aliases in TypeScript, so in order to allow nodemon to understand those paths, it's needed to install the next development dependency:

npm install -D tsconfig-paths

Once it's done, we must update the nodemon configuration file this way:

{
  "watch": ["src"],
  "ignore": ["src/**/*.test.ts", "node_modules"],
  "ext": "ts,mjs,js,json,graphql",
  "exec": "etsc && node -r tsconfig-paths/register -r ts-node/register ./dist/server.js",
  "legacyWatch": true
}
Enter fullscreen mode Exit fullscreen mode

That's all.

Collapse
 
trainingmontage profile image
Jeff Caldwell

Just wanted to say I've come back and referenced this article several times since I first read it. Great work!

Collapse
 
upperapps profile image
UpperApps

When trying to run the transpiled code I'm getting the following error:

Using default config
Built in: 21.304ms
file:///Users/rodrigo.melo/Repositories/Personal/Projects/go-non-go-matrix/dist/server.js:24
var import_http = __toESM(require("http"), 1);
                  ^

ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and '/Users/rodrigo.melo/Repositories/Personal/Projects/go-non-go-matrix/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.

Enter fullscreen mode Exit fullscreen mode

Can I (or should I) change the output files to be .cjs? If yes, how should I do that?

Collapse
 
aderchox profile image
aderchox

ESBuild is fast! Cool read! 👌