DEV Community

Akkarapon Phikulsri
Akkarapon Phikulsri

Posted on

Node.js + Fastify + Sequelize + MySQL

This course is based on this template

GitHub logo akkaraponph / nodejs-ts-fastify-mvc-starter-template

This repo node api crud template - folder-by-type

Node.js TypeScript Fastify MVC Starter Template

A production-ready starter template for building RESTful APIs with Node.js, TypeScript, and Fastify. This template follows the MVC (Model-Routes-Controllers-Services) architecture pattern, providing a clean and scalable foundation for your backend projects.

🚀 Features

  • Fastify - Fast and low overhead web framework
  • TypeScript - Type-safe development
  • Sequelize ORM - Database abstraction with PostgreSQL/MySQL support
  • JWT Authentication - Secure token-based authentication
  • Swagger Documentation - Auto-generated API documentation
  • CORS Support - Cross-origin resource sharing configured
  • Error Handling - Custom error handling middleware
  • MVC Architecture - Clean separation of concerns
  • Environment Configuration - Multi-environment support (dev, test, production)

📁 Project Structure

├── src/
│   ├── app.ts                 # Fastify app configuration
│   ├── config/                # Configuration files
│   │   ├── config.ts          # Environment variables
│   │   ├── db.config.ts       # Database configuration
│   │   └── swagger/           # Swagger documentation setup
│   ├── controllers/           # Request handlers (business logic)
│   ├──

Flag

This YouTube tutorial is in Thai. I’m keeping this post in English so everyone can quickly understand the main concepts alongside the video. For deeper understanding, feel free to ask ChatGPT or your favorite AI.

What you will learn

This course teaches you how to build an API using:

  • Node.js
  • Fastify (a web framework)
  • Sequelize (for working with databases)
  • MySQL (database)

About MRCS

MRCS means Model-Routes-Controllers-Services. This is a way to organize your code. It helps you keep your code clean and easy to understand.

BillowDev Youtube

Setup Videos

Watch these videos first to install the tools you need:

How to Install Git and using GitHub

How to Install Node.js and VS code

How to Install Laragon

Node js TypeScript Fastify EP 01 Intro

In this episode, you will learn how to start your project and create your first API.

Step 1: Start a new project

First, create a new Node.js project. Open your terminal and type:

npm init -y
Enter fullscreen mode Exit fullscreen mode

This command creates a file called package.json. This file stores information about your project.

Step 2: Using Yarn (Optional)

You can use npm or yarn. Both are package managers. They help you install libraries for your project.

If you want to use yarn, install it first:

npm install -g yarn
Enter fullscreen mode Exit fullscreen mode

Here are the yarn commands you need to know:

  • npm install is the same as yarn add
  • npm install -D is the same as yarn add -D
  • npm run is the same as yarn

Step 3: Install TypeScript tools

You need to install TypeScript and some tools. TypeScript is a language that adds types to JavaScript.

Type this command in your terminal:

npm install --save-dev ts-node @types/node typescript
Enter fullscreen mode Exit fullscreen mode

Or if you use yarn:

yarn add -D ts-node @types/node typescript
Enter fullscreen mode Exit fullscreen mode

Step 4: Create TypeScript config file

Now create a TypeScript configuration file. This file tells TypeScript how to work with your code.

Type this command:

npx tsc --init
Enter fullscreen mode Exit fullscreen mode

This creates a file called tsconfig.json. Copy this code into that file:

tsconfig.json

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */
    /* Language and Environment */
    "target": "ESNext",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */

    /* Modules */
    "module": "commonjs",                                /* Specify what module code is generated. */
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    "baseUrl": "./src",                                  /* Specify the base directory to resolve non-relative module names. */
    "outDir": "./dist",                                   /* Specify an output folder for all emitted files. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  },
  "include": [
    "**/*.ts"
, "src/config/db.config.ts"  ],
  "exclude": [
    "node_modules",
    "**/*.d.ts",
    "**/*.spec.ts"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Install Fastify

Fastify is a web framework. It helps you build APIs quickly.

Install it with this command:

npm install fastify
Enter fullscreen mode Exit fullscreen mode

Or if you use yarn:

yarn add fastify
Enter fullscreen mode Exit fullscreen mode

Step 6: Create your first file

Create a new file called index.ts in your project root folder.

Step 7: Write your first API

Now you will write code to create your first API. Follow these steps:

Step 7.1: Import Fastify

import fastify from "fastify";
Enter fullscreen mode Exit fullscreen mode

This line imports the Fastify library.

Step 7.2: Create the app

Next, create your app:

const app = fastify()
Enter fullscreen mode Exit fullscreen mode

Step 7.3: Create your first route

A route is a URL that your API can respond to. Create your first route:

app.get("/", async () => "SERVER");
Enter fullscreen mode Exit fullscreen mode

This creates a route at "/". When someone visits this URL, they will see "SERVER".

Step 7.4: Start your server

Now you need to start your server. Add this code:

    const PORT = 5000
    app.listen({port:Number(PORT)}, (err) => {
        if (err) {
            console.error(err)
            process.exit(1)
        }
        console.log(`SERVE ON ${PORT}`)
    })
Enter fullscreen mode Exit fullscreen mode

This code starts your server on port 5000.

Step 7.5: Run your API

Save your file. Then open your terminal and type:

ts-node index.ts
Enter fullscreen mode Exit fullscreen mode

You should see the message "SERVE ON 5000" in your terminal. This means your API is running!

Step 8: Make your logs look better

The logs help you see what your API is doing. Let's make them look better.

Step 8.1: Add logging options

Change your app code to this:

const app = fastify({
    logger: true
})
Enter fullscreen mode Exit fullscreen mode

This turns on logging.

Step 8.2: Update your listen code

Replace the code from step 7.4 with this:

    const PORT = 5000
    app.listen({port:Number(PORT)}, (err) => {
        if (err) {
            app.log.error(err);
            process.exit(1)
        }
        app.log.info(`SERVE ON ${PORT}`)
    })
Enter fullscreen mode Exit fullscreen mode

This uses the logger instead of console.log.

Step 8.3: See your logs

When you run your API now, you will see logs like this:

{"level":30,"time":1676779096246,"pid":25344,"hostname":"billo","msg":"Server listening at http://[::1]:5000"}
{"level":30,"time":1676779096248,"pid":25344,"hostname":"billo","msg":"Server listening at http://127.0.0.1:5000"}
{"level":30,"time":1676779096248,"pid":25344,"hostname":"billo","msg":"SERVE ON 5000"}
Enter fullscreen mode Exit fullscreen mode

Step 9: Test your API

You can test your API with Thunder Client. It is a tool for testing APIs. You can find it in the Visual Studio Code extensions.

You can also use other tools like Postman or Insomnia. These tools let you test different HTTP methods like GET, POST, PUT, PATCH, and DELETE. If your API uses GET method, you can also test it in your browser.

Node js TypeScript Fastify EP 02 Thunder client

In this episode, you will learn how to test your API using Thunder Client.

What is Thunder Client?

Thunder Client is a tool for testing APIs. It works inside Visual Studio Code. You can use it to send requests to your API and see the responses.

Step 1: Install Thunder Client

Open Visual Studio Code. Go to the Extensions section. Search for "Thunder Client" and install it.

thunder-client

Step 2: How to use Thunder Client

After installing Thunder Client, you will see a new icon in your sidebar. Click on it to open Thunder Client. You can create requests and test your API there.

how-to-use-thunder-client

Your complete index.ts file

After following all the steps in EP 01, your index.ts file should look like this:

import fastify from "fastify";

const app = fastify({
    logger: true
})

app.get("/", async () => "SERVER");

const PORT = 5000
app.listen({port:Number(PORT)}, (err) => {
    if (err) {
        app.log.error(err);
        process.exit(1)
    }
    app.log.info(`SERVE ON ${PORT}`)
})
Enter fullscreen mode Exit fullscreen mode

Node js TypeScript Fastify EP 03 App

In this episode, you will learn how to organize your code better. We will move the app logic to a separate file.

Why organize code?

When your code grows, it is better to organize it. This makes your code easier to read and maintain. We will create a src folder and put our code there.

Step 1: Create the src folder

First, create a new folder called src in your project root.

Step 2: Move app logic to src/app.ts

Create a new file called app.ts inside the src folder. Move the app creation code there.

Code for src/app.ts

import fastify, { FastifyServerOptions } from "fastify";

const App = (options: FastifyServerOptions) => {
    const app = fastify(options)

    app.get("/", async () => "SERVER");
    return app
}
export default App
Enter fullscreen mode Exit fullscreen mode

This code creates a function called App. This function takes options and creates a Fastify app. It returns the app so you can use it later.

Code for index.ts

Now update your index.ts file. Import the App function and use it to create your app:

import App from "./src/app";

const app = App({
    logger: true
})
const PORT = 5000
app.listen({port:Number(PORT)}, (err) => {
    if (err) {
        app.log.error(err);
        process.exit(1)
    }
    app.log.info(`SERVE ON ${PORT}`)
})
Enter fullscreen mode Exit fullscreen mode

What changed?

  • Before: All your code was in index.ts
  • After: The app logic is in src/app.ts, and index.ts only starts the server

This makes your code more organized. When you add more features, you will add them to the src folder.

Node js TypeScript Fastify EP 04 Routes

In this episode, you will learn how to create routes. Routes are URLs that your API responds to.

What is a route?

A route is a URL path. When someone visits that URL, your API responds. For example, /api/v1/articles is a route.

Step 1: Create the routes folder

Create a new folder called routes inside the src folder.

Step 2: Create article route file

Create a new file called article.route.ts inside the src/routes folder.

Step 3: Write the route code

import { FastifyInstance } from "fastify";

const articleRouter = async (app: FastifyInstance) => {
    // This is sample data for testing
    const article = {
        id: "1",
        name: "node.js fastify",
        desc: "Fast and easy course for building APIs"
    }

    // Create a GET route at "/"
    app.get(
        "/",
        () => {
            return {
                articles: [
                    article
                ]
            }
        }
    );
};

export default articleRouter;
Enter fullscreen mode Exit fullscreen mode

This code creates a router for articles. It has one route that returns sample data.

Step 4: Create routes index file

Create a new file called index.ts inside the src/routes folder. This file exports all your routes.

import articleRouter from "./article.route";

export { articleRouter };
Enter fullscreen mode Exit fullscreen mode

Step 5: Register the route in app.ts

Now you need to tell your app to use this route. Update your src/app.ts file.

import fastify, { FastifyServerOptions } from "fastify";
import { articleRouter } from "./routes";

const App = (options: FastifyServerOptions) => {
    const app = fastify(options)

    app.get("/", async () => "SERVER");
    // Register the article router with a prefix
    app.register(articleRouter, { prefix: "/api/v1/articles" });
    return app
}
export default App
Enter fullscreen mode Exit fullscreen mode

The prefix means all article routes will start with /api/v1/articles.

Step 6: Test your route

  1. Stop your server by pressing CTRL + C in your terminal
  2. Start it again with: ts-node index.ts
  3. Open Thunder Client and test the route: http://localhost:5000/api/v1/articles

Result

You should see the article data when you visit the route:

api-v1-aticles-get

api-v1-aticles-get

Node js TypeScript Fastify EP 05 Controllers

In this episode, you will learn about controllers. Controllers handle the logic for your routes.

What is a controller?

A controller is a function that handles requests. When someone visits a route, the controller decides what to do and what to return.

Step 1: Create controllers folder

Create a new folder called controllers inside the src folder.

Step 2: Create article controller

Create a new file called article.controller.ts inside the src/controllers folder.

export const handleGetArticle = () => {
    // This is sample data for testing
    const article = {
        id: "1",
        name: "node.js fastify",
        desc: "Fast and easy course for building APIs"
    }
    return {
        articles: [
            article
        ]
    }
}

export default {
    handleGetArticle
}
Enter fullscreen mode Exit fullscreen mode

This controller function returns article data. Later, it will get data from a service.

Step 3: Create controllers index file

Create a new file called index.ts inside the src/controllers folder.

import articlesController from "./article.controller";
export { articlesController };
Enter fullscreen mode Exit fullscreen mode

Step 4: Update the route to use the controller

Now update your src/routes/article.route.ts file to use the controller.

import { FastifyInstance } from "fastify";
import articleController from './../controllers/article.controller';

const articleRouter = async (app: FastifyInstance) => {
    // Use the controller function as the handler
    app.get(
        "/",
        articleController.handleGetArticle
    );
};

export default articleRouter;
Enter fullscreen mode Exit fullscreen mode

Now your route uses the controller. This separates the route logic from the business logic.

Node js TypeScript Fastify EP 06 Services

In this episode, you will learn about services. Services contain the business logic of your application.

What is a service?

A service is where you put your business logic. It handles things like getting data or processing information. Controllers call services.

Step 1: Create services folder

Create a new folder called services inside the src folder.

Step 2: Create article service

Create a new file called article.service.ts inside the src/services folder.

export const getArticles = () => {
    const data = {
        id: "1",
        name: "node.js fastify",
        desc: "Fast and easy course for building APIs"
    }

    return { response: data }
}

export default {
    getArticles
}
Enter fullscreen mode Exit fullscreen mode

This service function returns article data. Later, it will get data from a database.

Step 3: Create services index file

Create a new file called index.ts inside the src/services folder.

import articleService from "./article.service";
export { articleService };
Enter fullscreen mode Exit fullscreen mode

Step 4: Update the controller to use the service

Now update your src/controllers/article.controller.ts file to use the service.

import { articleService } from "../services";

export const handleGetArticle = () => {
    return articleService.getArticles()
}

export default {
    handleGetArticle
}
Enter fullscreen mode Exit fullscreen mode

Now your controller calls the service. This separates the request handling from the business logic.

Node js TypeScript Fastify EP 07 MySQL Database

In this episode, you will learn how to set up MySQL database using Laragon.

What is MySQL?

MySQL is a database. It stores your data in tables. You will use it to store articles and other information.

What is Laragon?

Laragon is a tool that helps you run MySQL on your computer. It makes it easy to start and manage MySQL.

Step 1: Install Laragon

First, install Laragon. Follow this video: How to Install Laragon

Step 2: Start Laragon server

Open Laragon and click the "Start All" button to start the MySQL server.

laragon-start

Step 3: Open MySQL

Click on the "Database" button in Laragon to open the database manager.

mysql-open

Step 4: Create a new database

  1. Click on "Database" in the left sidebar
  2. Right-click and select "Create Database"

click-database

create-database

Step 5: Name your database

Name your database node_fastify_db and click "OK".

node_fastify_db

Great! Your database is ready. In the next episode, you will learn how to connect your API to this database.

Node js TypeScript Fastify EP 08 Sequelize

In this episode, you will learn how to connect your API to the database using Sequelize.

What is Sequelize?

Sequelize is an ORM (Object-Relational Mapping). It helps you talk to your database. You use models to work with database tables.

What is dotenv?

Dotenv is a package that helps you use environment variables. Environment variables store secret information like database passwords. You put them in a .env file.

What is mysql2?

mysql2 is a driver for MySQL. It allows your code to connect to MySQL database. If you use PostgreSQL, you would use pg instead.

Step 1: Install packages

Install Sequelize, dotenv, and mysql2:

npm install sequelize dotenv mysql2
Enter fullscreen mode Exit fullscreen mode

Or if you use yarn:

yarn add sequelize dotenv mysql2
Enter fullscreen mode Exit fullscreen mode

Step 2: Create .env file

Create a new file called .env in your project root. This file stores your database settings.

Important: The default MySQL username in Laragon is root and the password is empty "".

We separate the settings for different environments: development, production, and test.

NODE_ENV=development

DB_USERNAME_DEV=root
DB_PASSWORD_DEV=""
DB_DATABASE_DEV=node_fastify_db
DB_HOST_DEV=localhost

DB_USERNAME_PROD=root
DB_PASSWORD_PROD=""
DB_DATABASE_PROD=node_fastify_db_production
DB_HOST_PROD=localhost

DB_USERNAME_TEST=root
DB_PASSWORD_TEST=""
DB_DATABASE_TEST=node_fastify_db_test
DB_HOST_TEST=localhost

DB_DIALECT=mysql

Enter fullscreen mode Exit fullscreen mode

Step 3: Create config files

We need to create config files to use the .env file. First, we import dotenv. Then we create a config that reads values from the .env file.

Step 3.1: Create config.ts

Create a new folder called config inside the src folder. Then create a file called config.ts inside src/config.

import dotenv from "dotenv";
dotenv.config();

const config = {
  env: process.env.NODE_ENV || "development",
  port: process.env.PORT || 5000,
  database: {
    dev: {
      username: process.env.DB_USERNAME_DEV,
      password: process.env.DB_PASSWORD_DEV,
      name: process.env.DB_DATABASE_DEV,
      host: process.env.DB_HOST_DEV,
    },
    production:{
      username: process.env.DB_USERNAME_PROD,
      password: process.env.DB_PASSWORD_PROD,
      name: process.env.DB_DATABASE_PROD,
      host: process.env.DB_HOST_PROD,
    },
    test: {
      username: process.env.DB_USERNAME_TEST,
      password: process.env.DB_PASSWORD_TEST,
      name: process.env.DB_DATABASE_TEST,
      host: process.env.DB_HOST_TEST,
    },
    dialect: process.env.DB_DIALECT,
  },
};

export default config;

Enter fullscreen mode Exit fullscreen mode

Step 3.2: Create db.config.ts

Create a new file called db.config.ts inside the src/config folder.

import config from "./config"; // this is important!

module.exports = {
  development: {
    username: config.database.dev.username,
    password: config.database.dev.password,
    database: config.database.dev.name,
    host: config.database.dev.host,
    dialect: config.database.dialect,
  },
  test: {
    username: config.database.test.username,
    password: config.database.test.password,
    database: config.database.test.name,
    host: config.database.test.host,
    dialect: config.database.dialect,
  },
  production: {
    username: config.database.production.username,
    password: config.database.production.password,
    database: config.database.production.name,
    host: config.database.production.host,
    dialect: config.database.dialect,
  },
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Create models

Models represent your database tables. They define what data you can store.

Step 4.1: Create types folder

Create a new folder called types inside the src folder.

Step 4.2: Create article model types

Create a new folder called articles inside the src/types folder. Then create a file called article.model.types.ts inside src/types/articles.

export interface ArticleAttributes {
  id?: string;
  title?: string;
  text?: string;
  type?: string;
  UserId?: string;
  createdAt?: Date;
  updatedAt?: Date;
}
Enter fullscreen mode Exit fullscreen mode

Step 4.3: Create models folder

Create a new folder called models inside the src folder.

Step 4.4: Create models/index.ts

Create a new file called index.ts inside the src/models folder.

Note: The code below is usually generated by sequelize-cli. But for TypeScript, we modified it to read config from ../config/db.config.ts.

"use strict";

const fs = require("fs");
const path = require("path");
const basename = path.basename(__filename);
const Sequelize = require("sequelize");
const env = process.env.NODE_ENV || "development";
const config = require(__dirname + "/../config/db.config.ts")[env];
const db: any = {};

let sequelize: any;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(
    config.database,
    config.username,
    config.password,
    config
  );
}

fs.readdirSync(__dirname)
  .filter((file: string) => {
    return (
      file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".ts"
    );
  })
  .forEach((file: any) => {
    const model = require(path.join(__dirname, file))(
      sequelize,
      Sequelize.DataTypes
    );
    db[model.name] = model;
  });

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

export default db;
Enter fullscreen mode Exit fullscreen mode

Step 4.5: Create article model

Create a new file called article.model.ts inside the src/models folder.

This file defines the Article model. The models/index.ts file will automatically read this file and create the model based on what you declare.

"use strict";
import * as Sequelize from "sequelize";
import { Model, UUIDV4 } from "sequelize";
import { ArticleAttributes } from "types/articles/article.model.types";

module.exports = (sequelize: any, DataTypes: any) => {
  class Article
    extends Model<ArticleAttributes>
    implements ArticleAttributes
  {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    id!: string;
    title!: string;
    text!: string;
    type!: string;
    static associate(models: any) {
      // define association here

    }
  }
  Article.init(
    {
      id: {
        type: DataTypes.UUID,
        defaultValue: UUIDV4,
        allowNull: false,
        primaryKey: true,
      },
      title: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      text: {
        type: DataTypes.STRING(500),
      },
    },
    {
      sequelize,
      modelName: "Article",
    }
  );
  return Article;
};
Enter fullscreen mode Exit fullscreen mode

Step 4.6: Update index.ts

Now update your index.ts file to:

  • Import the database and config files
  • Get the PORT from the config file
  • Sync the database with Sequelize (this creates the tables)
import App from "./src/app";
import db from "./src/models"
import config from "./src/config/config"

const app = App({
    logger: true
})
const PORT: string | number = config.port

db.sequelize.sync().then(() => {
app.listen({port:Number(PORT)}, (err) => {
    if (err) {
        app.log.error(err);
        process.exit(1)
    }
    app.log.info(`SERVE ON ${PORT}`)
})
})
Enter fullscreen mode Exit fullscreen mode

Step 5: Use nodemon for development

Nodemon automatically restarts your app when you change the code. This is very helpful during development.

Important: Only use nodemon in development. It will restart your app every time you save a file.

Step 5.1: Install nodemon

npm install -D nodemon
Enter fullscreen mode Exit fullscreen mode

Or if you use yarn:

yarn add -D nodemon
Enter fullscreen mode Exit fullscreen mode

Step 5.2: Add script to package.json

Open your package.json file. Find the "scripts" section and add this line:

"dev": "nodemon index.ts"
Enter fullscreen mode Exit fullscreen mode

Step 5.3: Run your app

Now you can run your app with:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Or if you use yarn:

yarn dev
Enter fullscreen mode Exit fullscreen mode

Your app will automatically restart when you change your code!

Bonus: Complete CRUD API Example

This is a bonus section. You will learn how to create a complete CRUD API. CRUD means Create, Read, Update, and Delete.

Note: This section does not have a video tutorial. Follow the steps below to build a complete Articles API.

What you will build

You will create an Articles API with these operations:

  • GET - Get all articles
  • GET - Get one article by ID
  • POST - Create a new article
  • PUT - Update an article
  • DELETE - Delete an article

How it works

The flow is: routes → controllers → services → models (Sequelize) → MySQL database

Step 1: Update routes

We will create 5 routes in the article router. Each route calls a controller function.

import { FastifyInstance } from "fastify";
import articleController from './../controllers/article.controller';

const articleRouter = async (app: FastifyInstance) => {
    // GET all articles
    app.get(
        "/",
        articleController.handleGetArticle
    );

    // GET one article by ID
    app.get(
        "/get/:id",
        articleController.handleGetArticleById
    );

    // POST create a new article
    app.post(
        "/create",
        articleController.handleCreateArticle
    );

    // PUT update an article
    app.put(
        "/update/:id",
        articleController.handleUpdateArticle
    );

    // DELETE delete an article
    app.delete(
        "/delete/:id",
        articleController.handleDeleteArticle
    );
};

export default articleRouter;
Enter fullscreen mode Exit fullscreen mode

Step 2: Create request types

We need to define types for the request data. This helps TypeScript understand what data to expect.

Create a new file called article.controller.types.ts inside src/types/articles.

import { FastifyRequest } from "fastify";
export type RequestWithIdArticle = FastifyRequest<{
    Params: { id: string };
}>;


export type UpdateArticleRequest = FastifyRequest<{
    Params: { id: string };
    Body: {
        title?: string | undefined;
        text?: string | undefined;
        type?: string | undefined;
    };
}>;

export type ArticleCreateRequest = FastifyRequest<{
    Body: {
        title?: string | undefined;
        text?: string | undefined;
        type?: string | undefined;
    };
}>;
Enter fullscreen mode Exit fullscreen mode

Step 3: Update controllers

In the article controller, we will create handler functions. Each function calls a service function.

import { ArticleCreateRequest, RequestWithIdArticle, UpdateArticleRequest } from "types/articles/article.controller.types";
import { ArticleAttributes } from "types/articles/article.model.types";
import { articleService } from "../services";


export const handleGetArticle = async () => {
    return articleService.getArticles()
}

export const handleGetArticleById = async (req: RequestWithIdArticle) => {
    const id = req.params.id;
    return articleService.getOneArticle(id)
}

export const handleCreateArticle = async (req: ArticleCreateRequest) => {
    const { title, text, type } = req.body
    return articleService.createArticle({ title, text, type })
}

export const handleUpdateArticle = async (req: UpdateArticleRequest) => {
    const { title, text, type } = req.body
    return articleService.updateArticle(req.params.id, { title, text, type })
}

export const handleDeleteArticle = async (req: RequestWithIdArticle) => {
    return articleService.deleteArticle(req.params.id)
}

export default {
    handleGetArticle,
    handleGetArticleById,
    handleCreateArticle,
    handleUpdateArticle,
    handleDeleteArticle
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Update services

In the article service, we create functions that use Sequelize to interact with the database.

import { ArticleAttributes } from "types/articles/article.model.types";
import db from "../models";
const ArticleModel = db.Article

export const getArticles = async (): Promise<ArticleAttributes[]> => {
    const response = await ArticleModel.findAll();
    return response
}

export const getOneArticle = async (id: string): Promise<ArticleAttributes> => {
    const response: ArticleAttributes = await ArticleModel.findByPk(id)
    return response
}

export const createArticle = async (body: ArticleAttributes): Promise<ArticleAttributes> => {
    const response: ArticleAttributes = await ArticleModel.create(body)
    return response
}

export const updateArticle = async (id: string, body: ArticleAttributes) => {
    const response = await ArticleModel.update({ ...body }, { where: { id } })
    return response
}

export const deleteArticle = async (id: string) => {
    const response = await ArticleModel.destroy({ where: { id } });
    return response
}

export default {
    getArticles,
    getOneArticle,
    createArticle,
    updateArticle,
    deleteArticle
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Test your API

Now you can test all the routes using Thunder Client. Here are examples of the results:

GET all articles

Visit GET http://localhost:5000/api/v1/articles to get all articles:

get-all-article

GET one article

Visit GET http://localhost:5000/api/v1/articles/get/:id to get one article (replace :id with an article ID):

get-one-article

CREATE article

Send a POST request to http://localhost:5000/api/v1/articles/create with article data in the body:

create-article

UPDATE article

Send a PUT request to http://localhost:5000/api/v1/articles/update/:id with updated data in the body:

update-article

DELETE article

Send a DELETE request to http://localhost:5000/api/v1/articles/delete/:id to delete an article:

delete-article

Congratulations! You now have a complete CRUD API working with a database!

Top comments (0)