Optimizing Node API Development with SWC Compiler and ESLint
Tags: javascript, node, tutorial, typescript
Published: 2022-04-10 13:20:35

As our APIs get a larger code base, consequently, the time it takes to build and even hot reload will be longer. By the way, who ever made a small change and then had to wait almost three seconds for the API to hot reload? Or even making several changes in a short amount of time and then having problems with the process running?

This is where compilers like SWC help us, whether during the development of our applications or during the compilation and bundling process. In today's article we are going to setup an API in TypeScript and then we will proceed to configure the SWC together with ESLint.

During the development of the application, we will want the SWC to watch the changes we make to our TypeScript source code, as soon as it has any changes it will transpile to JavaScript from the same file we made the changes. Finally, we will use nodemon to watch the changes that occur in the transpiled code and we will hot reload the API as soon as there is a change.

When we need to put the API into production, just do the regular process, just run the build command and then we would have to run the start command.

![enough talk gif](https://media1.giphy.com/media/l4Ep8iKYKrIKCBhIs/giphy.gif?cid=ecf05e47iztxpl6cgy60zqeb5bpycfjlf5tcee4rr4bhvqop&rid=giphy.gif&ct=g)

## Project Setup

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

```sh
mkdir swc-config
cd swc-config
```

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

```sh
npm init -y
npm install -D typescript @types/node
```

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

```js
{
  "compilerOptions": {
    "target": "es2020",
    "module": "es2020",
    "allowJs": true,
    "removeComments": true,
    "resolveJsonModule": true,
    "typeRoots": [
      "./node_modules/@types"
    ],
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": [
      "es2020"
    ],
    "baseUrl": ".",
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": "Node",
    "skipLibCheck": true,
    "paths": {
      "@routes/*": [
        "./src/routes/*"
      ],
      "@middlewares/*": [
        "./src/middlewares/*"
      ]
    }
  },
  "include": [
    "src/**/*"
  ],
  "exclude": ["node_modules"],
}
```

As you may have noticed, we already defined some things in our `tsconfig.json` that I don't usually define in my articles, such as creating a path alias and using a "very current" version of ES.

With the configuration of our project in TypeScript, we can now install the necessary dependencies. In this project I will use the Koa framework, however this setup works with many others, such as Express, Fastify, etc.

```sh
# dependencies
npm install koa @koa/router koa-body

# dev dependencies
npm install -D @types/koa @types/koa__router
```

Now with these base dependencies, we can create a simple api, starting with the entry file:

```js
// @/src/main.ts
import Koa from 'koa'
import koaBody from 'koa-body'

import router from '@routes/index'

const startServer = async (): Promise<Koa> => {
  const app = new Koa()

  app.use(koaBody())
  app.use(router.routes())

  return app
}

startServer()
  .then((app) => app.listen(3333))
  .catch(console.error)
```

Then we can create our routes:

```js
// @/src/routes/index.ts
import KoaRouter from '@koa/router'
import { Context } from 'koa'

import { logger } from '@middlewares/index'

const router = new KoaRouter()

router.get('/', logger, (ctx: Context): void => {
  ctx.body = { message: 'Hello World' }
})

export default router
```

And a simple middleware:

```js
// @/src/routes/index.ts
import { Context, Next } from 'koa'

export const logger = async (ctx: Context, next: Next): Promise<Next> => {
  const start = Date.now()
  const ms = Date.now() - start
  console.log(`${ctx.method} ${ctx.url} - ${ms} ms`)
  return await next()
}
```

With this, we can now move on to the next step, which will be the SWC configuration.

## SWC Setup

Now we can install the necessary dependencies to configure our SWC:

```sh
npm install -D @swc/cli @swc/core chokidar nodemon concurrently
```

Next, let's create a `.swcrc` file and add the following configuration to it:

```js
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": false,
      "decorators": true,
      "dynamicImport": true
    },
    "target": "es2020",
    "paths": {
      "@routes/*": ["./src/routes/*"],
      "@middlewares/*": ["./src/middlewares/*"]
    },
    "baseUrl": "."
  },
  "module": {
    "type": "commonjs"
  }
}
```

Now let's add the necessary scripts to our `package.json`:

```js
{
  // ...
  "scripts": {
    "dev": "concurrently \"npm run watch-compile\" \"npm run watch-dev\"",
    "watch-compile": "swc src -w --out-dir dist",
    "watch-dev": "nodemon --watch \"dist/**/*\" -e js ./dist/main.js",
    "build": "swc src -d dist",
    "start": "NODE_ENV=production node dist/main.js",
    "clean": "rm -rf dist"
  },
  // ...
}
```

In the `watch-compile` script swc will automatically transpile the code using chokidar. While the `watch-dev` script uses nodemon to hot reload the application. When the `dev` script is executed, concurrently executes both commands (`watch-compile` and `watch-dev`) at the same time so that swc transpiles the TypeScript code to JavaScript and nodemon hot reloads the API when notices a change.

With the SWC configured we can move on to the ESLint configuration.

## ESLint Setup

First we will install ESLint as a development dependency:

```sh
npm install -D eslint
```

Then we will initialize the eslint configuration by running the following command:

```sh
npx eslint --init
```

Then in the terminal just make the following choices:

![eslint setup through the terminal](https://res.cloudinary.com/dj5iihhqv/image/upload/v1649596395/Kapture_2022-04-10_at_14.10.39-min_lgq6we.gif)

Now we can go back to our `package.json` and add the following scripts:

```js
{
  // ...
  "scripts": {
    // ...
    "lint": "eslint --ext .ts src",
    "lint:fix": "eslint --ext .ts src --fix"
  },
  // ...
}
```

Finally, just create the `.eslintignore` file and add the following:

```
dist/
```

## Conclusion

I hope you enjoyed today's article and that it was useful to you, even if it is to try something new. Finally, I leave [here](https://github.com/FranciscoMendes10866/swc-config) the link of a repository in which I have a similar configuration, but using Express. See you 👋