Build Type Safe API with Sequelize TypeScript and Express.js
Tags: node, javascript, mongodb, tutorial
Published: 2021-12-17 17:38:38

## Overview

I think Sequelize is the most popular ORM in the Node universe. However, it is widely used by the JavaScript community and not so much by the TypeScript community, because nowadays there are several alternatives with better support and development experience. 

But in my opinion all that changes with the existence of the [sequelize-typescript](https://github.com/RobinBuschmann/sequelize-typescript) dependency. The way the entities are structured and the way the connection to the database is made becomes much more intuitive. This experience is ideal because you can transition from JavaScript to TypeScript in a much more convenient way and without changing the stack.

## Today's example

In today's example we are going to set up a Node project with TypeScript. Then we will create an API using the Express.js framework and create the CRUD of our application.

## Project setup 

As a first step, create a project directory and navigate into it:

```sh
mkdir ts-sequelize
cd ts-sequelize
```

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

```sh
npm init -y
npm install typescript ts-node-dev @types/node --save-dev
```

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

```js
{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": [
      "esnext"
    ],
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "esnext",
    "moduleResolution": "Node",
  }
}
```

Now let's add the following script to our `package.json` file.

```js
{
  // ...
  "type": "module",
  "scripts": {
    "start": "ts-node-dev main.ts"
  },
  // ...
}
```

Now proceed with the installation of the Express and Sequelize dependencies (as well as their development dependencies):

```sh
npm install express mariadb reflect-metadata sequelize sequelize-typescript --save
npm install @types/express @types/validator --save-dev
```

# Let's code

And now let's create a simple API:

```js
// @/main.ts
import "reflect-metadata";
import express, { Request, Response } from "express";

const app = express();

app.use(express.json());

app.get("/", (req: Request, res: Response): Response => {
  return res.json({ message: "Sequelize Example 🤟" });
});

const start = async (): Promise<void> => {
  try {
    app.listen(3000, () => {
      console.log("Server started on port 3000");
    });
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

void start();
```

For the API to be initialized on port `3000` just run the following command:

```sh
npm start
```

Now we can start by creating our entity from today's example, as I've been doing lately, let's create a model called Dog that will have some fields such as our four legged friend's name, his race, age and if he has been a good boy or not. The template might look like this:

```js
// @/models.ts
import { Table, Model, Column, DataType } from "sequelize-typescript";

@Table({
  timestamps: false,
  tableName: "dogs",
})
export class Dog extends Model {
  @Column({
    type: DataType.STRING,
    allowNull: false,
  })
  name!: string;

  @Column({
    type: DataType.STRING,
    allowNull: false,
  })
  breed!: string;

  @Column({
    type: DataType.BOOLEAN,
    allowNull: true,
    defaultValue: true,
  })
  isGoodBoy!: boolean;
}
```

Now with our defined model we can start working on configuring our database connection.

```js
// @/connection.ts
import { Sequelize } from "sequelize-typescript";

import { Dog } from "./models";

const connection = new Sequelize({
  dialect: "mariadb",
  host: "localhost",
  username: "root",
  password: "root",
  database: "sequelize",
  logging: false,
  models: [Dog],
});

export default connection;
```

With our template defined and our connection configured, it's now enough to go to `main.ts` to initialize the connection when the node instance starts.

```js
// @/main.ts
import "reflect-metadata";
import express, { Request, Response } from "express";

import connection from "./database";

const app = express();

// ...

const start = async (): Promise<void> => {
  try {
    await connection.sync();
    app.listen(3000, () => {
      console.log("Server started on port 3000");
    });
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

void start();
```

Now that we have everything ready, we can start working on our application's CRUD. First of all, let's create a route to get all the dogs we have in our database.

```js
app.get("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const allDogs: Dog[] = await Dog.findAll();
  return res.status(200).json(allDogs);
});
```

Next, let's just look for a dog through the id that is sent in the request parameters.

```js
app.get("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  const dog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(dog);
});
```

Now we need to insert a new record into our database table. For this, we will send the data from the request body.

```js
app.post("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const dog: Dog = await Dog.create({ ...req.body });
  return res.status(201).json(dog);
});
```

Next we need to update a record. For this, we will perform the update through the id and we will update the fields of the respective properties that are sent in the request body.

```js
app.put("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  await Dog.update({ ...req.body }, { where: { id } });
  const updatedDog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(updatedDog);
});
```

Now that we can fetch all records, create a new record and update a specific record. We still need to remove a specific record from our database table. Similar to other routes in our API, let's do it via id.

```js
app.delete("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
    const { id } = req.params;
    const deletedDog: Dog | null = await Dog.findByPk(id);
    await Dog.destroy({ where: { id } });
    return res.status(200).json(deletedDog);
  }
);
```

As you may have noticed at the endpoints for updating and removing records from the database, the data of the updated/deleted element is returned in the response body just so that they have some kind of feedback on the action taken.

The final code of our `main.ts` is as follows:

```js
// @/main.ts
import "reflect-metadata";
import express, { Request, Response } from "express";

import connection from "./database";
import { Dog } from "./models";

const app = express();

app.use(express.json());

app.get("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const allDogs: Dog[] = await Dog.findAll();
  return res.status(200).json(allDogs);
});

app.get("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  const dog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(dog);
});

app.post("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const dog: Dog = await Dog.create({ ...req.body });
  return res.status(201).json(dog);
});

app.put("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  await Dog.update({ ...req.body }, { where: { id } });
  const updatedDog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(updatedDog);
});

app.delete("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
    const { id } = req.params;
    const deletedDog: Dog | null = await Dog.findByPk(id);
    await Dog.destroy({ where: { id } });
    return res.status(200).json(deletedDog);
  }
);

const start = async (): Promise<void> => {
  try {
    await connection.sync();
    app.listen(3000, () => {
      console.log("Server started on port 3000");
    });
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

void start();
```

## Conclusion

As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🧑🏻‍💻

Hope you have a great day! 🧦