NodeJs: Simple Rest API with Fastify and Mysql

This time i will show you how to make simple REST API with Fastify as backend and Mysql for database. But before we start, what is Fastify and why should use this framework instead of Express or Koa ?

Fastify is a web framework highly focused on providing the best developer experience with the least overhead and a powerful plugin architecture. It is inspired by Hapi and Express and as far as we know, it is one of the fastest web frameworks in town. That’s what their claim and if we see benchmark result from Techempower.com yeah Fastify getting to top 7 fastest NodeJS framework. Cool.!!

Fastify and Mysql
Benchmark Result Round 19 @ 2020-05-28
Source : Techempower.com

Then why not use polkadot instead ? Yes Polkadot is super fast but they didn’t have router and much different from express. cmiiw. As far i know much nodeJs developer very familiar with express, fastify code are similar with express. So if you want to learn Fastify, it would be effortless to learning this framework.

Rest API with Fastify

In this tutorial we will make news api with 2 endpoint which is : get news list, and get detail news. Here’s for the file structure

.
├── app.js
├── config
│   └── database.js
├── controllers
│   └── news.js
├── models
│   └── newsModel.js
├── package.json
├── routes
│   └── index.js
└── server.js

For database we are using mysql with one table only. here’s for the schema

CREATE TABLE `news` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `publish_date` datetime DEFAULT NULL,
  `title` varchar(255) DEFAULT NULL,
  `content` longtext,
  `author` varchar(255) DEFAULT NULL,
  `image` text,
  `visited` decimal(10,0) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

First : Open your terminal or cmd and create new folder for this tutorial

mkdir example-rest-api-fastify-mysql
cd example-rest-api-fastify-mysql

Install package with this command

npm init -y
npm i --save fastify mysql2 dotenv

Create new file named app.js : touch app.js

const fastify = require('fastify')()
const routes = require('./routes')
fastify.register(routes);
module.exports = fastify

In the app.js file we only call the fastify package and register the routes. we will separate for the run service because if some days we want to convert this api to serverless like using Lambda, it will be eassier.

Create another file named server.js : touch server.js

'use strict'
require('dotenv').config();
const fastify = require('./app')

fastify.listen(process.env.SERVER_PORT, () =>
  console.log(`Example Rest API with Fastify listening on port ${process.env.SERVER_PORT}!`),
);

Now let’s create news controller file called ./controller/news.js

mkdir controller && touch controller/news.js

In news controller we need to import the newsModel and create 2 function called getNewsList & getNewsDetail

const newsModel = require('../models/newsModel')

async function getNewsList (request, reply) {
    var limit   = 20;
    var offset  = 0;
    var page    = 1;
    
    if (typeof request.query.limit !== "undefined") {
      if (parseInt(request.query.limit) > 0) {
        limit = parseInt(request.query.limit);
      }
    }

    if (typeof request.query.page !== "undefined") {
      if (parseInt(request.query.page) > 0) {
        page = parseInt(request.query.page);
        offset = (page-1)*limit
      }
    }

    var queryParams = { offset: offset, limit: limit }
    const newsData = await newsModel.newsList(queryParams);

    var response = {page: page, per_page: limit, data:newsData[0]}
    return reply.status(200).send(response);
}

async function getNewsDetail (request, reply) {
    const newsData = await newsModel.newsDetail(request.params.id);
    if (newsData.length > 0) {
        return reply.status(200).send({ data:newsData[0] });
    } else {
        return reply.status(500).send({ error: "News Not found!"});
    }
}

module.exports = {
    getNewsList,
    getNewsDetail
};

Then create newsModel file with command bellow, or you can create manually with code editor

mkdir models && touch models/newsModel.js

newsModel.js we create newsModel object contain 2 function and for newsList we have get offset and limit params from controller to handle simple pagination.

const mysqlPromise = require('../config/database');

const newsModel = {
  newsList: async function(params) {
    const connection = await mysqlPromise.DATABASE.getConnection();
    var res = [{}];

    try {
      res = await connection.execute(`SELECT * FROM news ORDER BY id DESC LIMIT ?, ?`, [params.offset, params.limit]);
      connection.release();
    }
    catch (err) {
      console.error(err)
      connection.release();
      return false
    }
    return res.length > 0 ? res : null;
  },
  newsDetail: async function(id) {
    const connection = await mysqlPromise.DATABASE.getConnection();
    var res = [{}];

    try {
      res = await connection.execute(`SELECT * FROM news WHERE id = ?`, [id]);
      connection.release();
    }
    catch (err) {
      console.error(err)
      connection.release();
      return false
    }
    return res.length > 0 ? res[0] : null;
  },
}

module.exports = newsModel;

after that we need create database pool connection with mysql2/promise.

mkdir config && touch config/database.js

All the credential stored in .env file so we need get the value with dotenv package

const mysql = require('mysql2/promise');
require('dotenv').config();

module.exports.DATABASE = mysql.createPool({
    "host": process.env.DB_HOST,
    "user": process.env.DB_USER,
    "password": process.env.DB_PASSWORD,
    "database": process.env.DB_NAME,
    "port": process.env.DB_PORT
});

If you need different database connection for READ and WRITE, you can modified this file.

Last thing we need to do is create routes file..

mkdir routes && touch routes/index.js

for the routes code will be like this

let news = require('../controllers/news');

async function routes (fastify, options) {
    fastify.get('/', function (request, reply) {
        reply.send({message: 'ping success', code: 200})
    })
    fastify.get('/news', news.getNewsList);
    fastify.get('/news/:id', news.getNewsDetail);
}

module.exports = routes;

Finish..!! To run the code you just need node server.js
It easy right ? for complete code you can clone repository bellow

https://github.com/omadijaya/example-rest-api-fastify-mysql

Conclusion

As you can see, making REST API with Fastify is very similar with express but we will get more power full APP. Of course this is only example.. In reality maybe you will need validation, JWT (Json Web Token), Redis, etc but in my opinion it not much different with express so it very wort to try with Fasitfy..

Read more about Fastify documentation here : https://www.fastify.io/docs/latest/

4 thoughts on “NodeJs: Simple Rest API with Fastify and Mysql”

    1. Sorry i didn’t have for now. If you want to create TDD automation test for nodeJs you can try chaiJs or Jest 🙂

    1. Hi yaya, sorry for late reply.
      You just need to create new file named .env

      SERVER_PORT=3000
      DB_HOST=localhost
      DB_USER=YOUR_DB_USER
      DB_PASSWORD=YOUR_PASSWORD
      DB_NAME=YOUR_DATABASE
      DB_PORT=3306

Leave a Comment

Your email address will not be published. Required fields are marked *