SkillAgentSearch skills...

Workflow.ts

TypeScript + Sequelize ORM + Express.js

Install / Use

/learn @madeindjs/Workflow.ts
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

graph_api.ts

The purpose of this article is to discover an implementation of an[API][api][RESTfull][rest] using[TypeScript][typescript].

TypeScript is a free and open source programming language developed by Microsoft to improve and secure the production of JavaScript code. (...) . The TypeScript code is transcompiled into JavaScript (...) TypeScript allows optional static typing of variables and functions (...) while maintaining the non-binding approach of JavaScript. Wikipedia - TypeScript

We will therefore set up a very basic graph_api system. We will create two models:

  • a node (node) which represents a simple step. It just contains a name' and anid'.
  • a link (link) that connects only two nodes with attributes from_id' andto_id'.

It's as simple as that.

To build the API, I will use[Express.js], a minimalist framework that allows us to make APIs in JavaScript. At the end of the article, the API will be able to generate a definition of a [Mermaid][mermaid] graph which allows to convert the graph_api into a beautiful graph like the one below:

Mermaid example

Let's go!

NOTE: I'm going to go a little fast because it's a bit of a reminder for myself. All the code is available on the [repository Github graph_api.ts][github_repo]

TL;DR: Express great freedom allows us to decide for ourselves the architecture of our application and TypeScript gives us the possibility to create real design paterns.

Setup project

Let's create a brand new project using NPM and Git versionning.

$ mkdir graph_api.ts
$ cd graph_api.ts/
$ npm init
$ git init

Then we start to install somes dependencies to build the HTTP server:

$ npm install --save express body-parser
$ npm install --save-dev typescript ts-node @types/express @types/node

As we use [TypeScript][typescript] we need to create a tsconfig.json to indicate how transcript TypeScript files from lib to dist folder. Also we transpile to [ES6][es6] version:

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "pretty": true,
    "sourceMap": true,
    "target": "es6",
    "outDir": "./dist",
    "baseUrl": "./lib"
  },
  "include": ["lib/**/*.ts"],
  "exclude": ["node_modules"]
}

Now we we'll create the the lib/app.ts. This will be in charge to configure, load routes and start Express server:

// lib/app.ts
import * as express from "express";
import * as bodyParser from "body-parser";
import { Routes } from "./config/routes";

class App {
  public app: express.Application;
  public routePrv: Routes = new Routes();

  constructor() {
    this.app = express();
    this.config();
    this.routePrv.routes(this.app);
  }

  private config(): void {
    this.app.use(bodyParser.json());
    this.app.use(bodyParser.urlencoded({ extended: false }));
  }
}

export default new App().app;

As you can see, we load the routes to define the controllers and the routes to comply with the MVC model. Here is the first NodesController which will handle actions related to nodes with an action index:

// lib/controllers/nodes.controller.ts
import { Request, Response } from "express";

export class NodesController {
  public index(req: Request, res: Response) {
    res.json({
      message: "Hello boi"
    });
  }
}

We will now separate the routes into a separate file:

// lib/config/routes.ts
import { Request, Response } from "express";
import { NodesController } from "../controllers/nodes.controller";

export class Routes {
  public nodesController: NodesController = new NodesController();

  public routes(app): void {
    app.route("/").get(this.nodesController.index);

    app.route("/nodes").get(this.nodesController.index);
  }
}

And a lib/server.ts file to start App object:

// lib/server.ts
import app from "./app";
const PORT = process.env.PORT || 3000;

app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`));

And that's it. You can start server using npm run dev and try API using cURL:

$ curl http://localhost:3000/nodes
{"message":"Hello boi"}

Setup sequelize

Sequelize is an [ORM (Object Relational Mapping)][orm] which is in charge to translate TypeScript object in SQL queries to save models. The sequelize documentation about TypeScrypt is really complete but don't panics I'll show you how to implement with Express.

We start to add librairies:

$ npm install --save sequelize sqlite
$ npm install --save-dev @types/bluebird @types/validator @types/sequelize

You may notice I choose SQLite database because of simplicity but you can use MySQL or Postgres

Then we will create a lib/config/database.ts file to setup Sequelize database system. For simplicity, I create a Sqlite database in memory:

// lib/config/database.ts
import { Sequelize } from "sequelize";

export const database = new Sequelize({
  database: "some_db",
  dialect: "sqlite",
  storage: ":memory:"
});

Then we'll be able to create a model. We'll begin with Node model who extends Sequelize Model class:

// lib/models/node.model.ts
import { Sequelize, Model, DataTypes, BuildOptions } from "sequelize";
import { database } from "../config/database";

export class Node extends Model {
  public id!: number;
  public name!: string;
  public readonly createdAt!: Date;
  public readonly updatedAt!: Date;
}
// ...

You can notice that I added two fields createdAt and updatedAt that[Sequelize][sequelize] will fill automatically.

Then, we configure the SQL schema of the table and call Node.sync. This will create a table in the Sqlite database.

// lib/models/node.model.ts
// ...
Node.init(
  {
    id: {
      type: DataTypes.INTEGER.UNSIGNED,
      autoIncrement: true,
      primaryKey: true
    },
    name: {
      type: new DataTypes.STRING(128),
      allowNull: false
    }
  },
  {
    tableName: "nodes",
    sequelize: database // this bit is important
  }
);

Node.sync({ force: true }).then(() => console.log("Node table created"));

That's it!

Setup Node controller

Now we setup database, let's create simple CRUD methods in controller. This means:

  • index to show list of nodes
  • show to show a node
  • create to add a new node
  • update to edit a node
  • delete to remove a node

Index

// lib/controllers/nodes.controller.ts
import { Request, Response } from "express";
import { Node } from "../models/node.model";

export class NodesController {
  public index(req: Request, res: Response) {
    Node.findAll<Node>({})
      .then((nodes: Array<Node>) => res.json(nodes))
      .catch((err: Error) => res.status(500).json(err));
  }
}

And setup route:

// lib/config/routes.ts
import { Request, Response } from "express";
import { NodesController } from "../controllers/nodes.controller";

export class Routes {
  public nodesController: NodesController = new NodesController();

  public routes(app): void {
    // ...
    app.route("/nodes").get(this.nodesController.index);
  }
}

You can try the route using cURL:

$ curl http://localhost:3000/nodes/
[]

It's seem to work but we have not data in SQlite database yet. Let's continue to add them.

Create

We'll first define an interface which define properties we should receive from POST query. We only want to receive name property as String. We'll use this interface to cast req.body object properties. This will prevent user to inject a parameters who we not want to save in database. This is a good practice.

// lib/models/node.model.ts
// ...
export interface NodeInterface {
  name: string;
}

Now back in controller. We simply call Node.create and pass params from req.body. Then we'll use Promise to handle some errors:

// lib/controllers/nodes.controller.ts
// ...
import { Node, NodeInterface } from "../models/node.model";

export class NodesController {
  // ...
  public create(req: Request, res: Response) {
    const params: NodeInterface = req.body;

    Node.create<Node>(params)
      .then((node: Node) => res.status(201).json(node))
      .catch((err: Error) => res.status(500).json(err));
  }
}

And setup route:

// lib/config/routes.ts
// ...
export class Routes {
  // ...
  public routes(app): void {
    // ...
    app
      .route("/nodes")
      .get(this.nodesController.index)
      .post(this.nodesController.create);
  }
}

You can try the route using cURL:

$ curl -X POST --data "name=first" http://localhost:3000/nodes/
{"id":2,"name":"first","updatedAt":"2019-06-14T11:12:17.606Z","createdAt":"2019-06-14T11:12:17.606Z"}

It's seem work. Let's try with a bad request:

$ curl -X POST http://localhost:3000/nodes/
{"name":"SequelizeValidationError","errors":[{"message":"Node.name cannot be null","type":"notNull Violation",...]}

Perfect. We can now continue.

Show

The show method have a little difference because we need an id as GET parameter. This means we should have an URL like this: /nodes/1. It's simple to make this when you build the route. There the implementation.

// lib/config/routes.ts
// ...
export class Routes {
  // ...
  public routes(app): void {
    // ...
    app.route("/nodes/:id").get(this.nodesController.show);
  }
}

Now we can get the id parameter using req.params.id. THen we simply use Node.findByPk method and handle when this Promise get a null value which mean the node was not found. In this case, we return a 404 response:

// lib/controllers/nodes.controller.ts
// ...
export class NodesController {
  // ...
  public show(req: Request, res: Response) {
    const nodeId: number = req.params.
View on GitHub
GitHub Stars19
CategoryDevelopment
Updated7mo ago
Forks7

Languages

TypeScript

Security Score

72/100

Audited on Aug 20, 2025

No findings