How to use TypeScript with Sequelize

Rafael picture Rafael · Feb 1, 2020 · Viewed 18.6k times · Source

I already have my server application written in Node, PostgreSQL, Sequelize using Fastify.

Now I would like to use TypeScript. Can anyone tell me how to begin rewriting my Server application using TypeScript.

Answer

Ernesto picture Ernesto · Feb 24, 2020

Using Decorators is something you should avoid as much as possible, they are not ECMAScript standard. They are even consider legacy. It is why I'm going to show you how to use sequelize with typescript.

we just need to follow the docs: https://sequelize.org/v5/manual/typescript.html but as it is not very clear, or at least to me. It took me a while understand it.

There it says that you need to install this tree things

 * @types/node
 * @types/validator // this one is not need it
 * @types/bluebird

npm i -D @types/node @types/bluebird

then let's assume your project looks like so:

myProject
--src
----models
------index.ts
------user-model.ts
------other-model.ts
----controllers
----index.ts
--package.json

Let's create the user model first

`./src/models/user-model.ts`
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";

export interface UserAttributes {
    id: number;
    name: string;
    email: string;
    createdAt?: Date;
    updatedAt?: Date;
}
export interface UserModel extends Model<UserAttributes>, UserAttributes {}
export class User extends Model<UserModel, UserAttributes> {}

export type UserStatic = typeof Model & {
    new (values?: object, options?: BuildOptions): UserModel;
};

export function UserFactory (sequelize: Sequelize): UserStatic {
    return <UserStatic>sequelize.define("users", {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        email: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true,
        },
        name: {
            type: DataTypes.STRING,
            allowNull: false,
        },
        createdAt: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: DataTypes.NOW,
        },
        updatedAt: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: DataTypes.NOW,
        },
    });
}

Now just to play arrow let's create another-model.ts

`./src/models/another-model.ts`

import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";

export interface SkillsAttributes {
    id: number;
    skill: string;
    createdAt?: Date;
    updatedAt?: Date;
}
export interface SkillsModel extends Model<SkillsAttributes>, SkillsAttributes {}
export class Skills extends Model<SkillsModel, SkillsAttributes> {}

export type SkillsStatic = typeof Model & {
    new (values?: object, options?: BuildOptions): SkillsModel;
};

export function SkillsFactory (sequelize: Sequelize): SkillsStatic {
    return <SkillsStatic>sequelize.define("skills", {
        id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true,
        },
        skill: {
            type: DataTypes.STRING,
            allowNull: false,
            unique: true,
        },
        createdAt: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: DataTypes.NOW,
        },
        updatedAt: {
            type: DataTypes.DATE,
            allowNull: false,
            defaultValue: DataTypes.NOW,
        },
    });
}

Our Entities are done. now the db connection.

open ./src/models/index.ts there is where we gonna place the seqelize instance

`./src/models/index.ts`

import * as sequelize from "sequelize";
import {userFactory} from "./user-model";
import {skillsFactory} from "./other-model";

export const dbConfig = new sequelize.Sequelize(
    (process.env.DB_NAME = "db-name"),
    (process.env.DB_USER = "db-user"),
    (process.env.DB_PASSWORD = "db-password"),
    {
        port: Number(process.env.DB_PORT) || 54320,
        host: process.env.DB_HOST || "localhost",
        dialect: "postgres",
        pool: {
            min: 0,
            max: 5,
            acquire: 30000,
            idle: 10000,
        },
    }
);

// SOMETHING VERY IMPORTANT them Factory functions expect a
// sequelize instance as parameter give them `dbConfig`

export const User = userFactory(dbConfig);
export const Skills = skillsFactory(dbConfig);

// Users have skills then lets create that relationship

User.hasMay(Skills);

// or instead of that, maybe many users have many skills
Skills.belongsToMany(Users, { through: "users_have_skills" });

// the skill is the limit!

on our index.ts add, if you just want to open connection

  db.sequelize
        .authenticate()
        .then(() => logger.info("connected to db"))
        .catch(() => {
            throw "error";
        });

or if you want to create them tables

  db.sequelize
        .sync()
        .then(() => logger.info("connected to db"))
        .catch(() => {
            throw "error";
        });

some like this

 
import * as bodyParser from "body-parser";
import * as express from "express";
import { dbConfig } from "./models";
import { routes } from "./routes";
import { logger } from "./utils/logger";
import { timeMiddleware } from "./utils/middlewares";

export function expressApp () {
    dbConfig
        .authenticate()
        .then(() => logger.info("connected to db"))
        .catch(() => {
            throw "error";
        });

    const app: Application = express();
    if (process.env.NODE_ENV === "production") {
        app.use(require("helmet")());
        app.use(require("compression")());
    } else {
        app.use(require("cors")());
    }

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
    app.use(timeMiddleware);
    app.use("/", routes(db));

    return app;
}

Once again the sky is the limit. If you do this you'll have all the power of the autocomplete. here an example: https://github.com/EnetoJara/resume-app