Implementing Sequelize with Node.js and TypeScript

Implementing Sequelize with Node.js and TypeScript

With Best Practices and CRUD Operations

Introduction

(In the previous article we talked about implementing sqlite3 library now we will talk about Sequelize you can access the previous article here... )

Node.js has become a popular choice for building server-side applications, and when it comes to working with databases, Sequelize is a robust Object-Relational Mapping (ORM) library. In this article, we will explore how to use Sequelize with Node.js, utilizing TypeScript for strong type checking. We will ensure that our Node.js application follows best practices and SOLID principles, including the use of controllers and routers. Additionally, we will cover creating a sample Node.js app with TypeScript, installing Sequelize, and implementing CRUD operations with it.

Creating a Sample Node.js App with TypeScript

Before diving into the implementation with Sequelize, let's start by creating a basic Node.js application using TypeScript. Follow the same steps mentioned in the previous article to set up your project structure.

Installing Sequelize

Now, instead of SQLite3, you'll install Sequelize and the SQLite dialect to work with SQLite databases:

npm install sequelize sequelize-cli sqlite3 @types/sequelize --save

This will add Sequelize, Sequelize CLI, and the SQLite3 dialect as dependencies to your project. The @types/sequelize package provides TypeScript definitions for Sequelize.

Configuring Sequelize

  1. Create a sequelize.ts file to configure Sequelize:
import { Sequelize } from 'sequelize';

const sequelize = new Sequelize({
  dialect: 'sqlite',
  storage: './mydatabase.db', // Specify the database file
});

export default sequelize;
  1. Initialize Sequelize models: Define your database models using Sequelize. For example, if you had a Task entity:
import { DataTypes, Model } from 'sequelize';
import sequelize from './sequelize';

class Task extends Model {
  public id!: number;
  public title!: string;
  public description!: string;
}

Task.init(
  {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
    },
    title: {
      type: DataTypes.STRING,
      allowNull: false,
    },
    description: {
      type: DataTypes.STRING,
      allowNull: true,
    },
  },
  {
    sequelize,
    modelName: 'Task',
    tableName: 'tasks', // Make sure this matches your table name
  }
);

export default Task;

Implementing Sequelize CRUD Operations

Now, let's implement the CRUD operations for the Task entity using Sequelize.

taskController.ts

import { Request, Response } from 'express';
import Task from './taskModel';

class TaskController {
  async createTask(req: Request, res: Response) {
    try {
      const { title, description } = req.body;
      const task = await Task.create({ title, description });
      res.status(201).json(task);
    } catch (error) {
      res.status(500).json({ error: 'Failed to create task' });
    }
  }

  async getTaskById(req: Request, res: Response) {
    try {
      const taskId = parseInt(req.params.id, 10);
      const task = await Task.findByPk(taskId);
      if (task) {
        res.status(200).json(task);
      } else {
        res.status(404).json({ error: 'Task not found' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Failed to retrieve task' });
    }
  }

  async updateTask(req: Request, res: Response) {
    try {
      const taskId = parseInt(req.params.id, 10);
      const { title, description } = req.body;
      const [updatedRows] = await Task.update({ title, description }, { where: { id: taskId } });
      if (updatedRows > 0) {
        const updatedTask = await Task.findByPk(taskId);
        res.status(200).json(updatedTask);
      } else {
        res.status(404).json({ error: 'Task not found or update failed' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Failed to update task' });
    }
  }

  async deleteTask(req: Request, res: Response) {
    try {
      const taskId = parseInt(req.params.id, 10);
      const deletedRows = await Task.destroy({ where: { id: taskId } });
      if (deletedRows > 0) {
        res.status(204).end();
      } else {
        res.status(404).json({ error: 'Task not found or deletion failed' });
      }
    } catch (error) {
      res.status(500).json({ error: 'Failed to delete task' });
    }
  }
}

export default TaskController;

These modifications incorporate Sequelize for database interaction, simplifying the CRUD operations by using Sequelize methods. Make sure to adjust the model and routes accordingly to fit your application's needs.

In summary, transitioning from raw SQL queries with SQLite3 to Sequelize involves configuring Sequelize, defining models, and adapting the CRUD operations in your controller to use Sequelize methods. This change brings advantages such as improved code organization, automatic query generation, and support for multiple database dialects.