Skip to content

Node.js Express REST API boilerplate with JWT Authentication and support for MySQL and PostgreSQL.

License

Notifications You must be signed in to change notification settings

cap-amanchar/nodejs-express-jwt

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nodejs-express-jwt

Express REST API Boilerplate with JWT Authentication and support for MySQL and PostgreSQL.

  • Compilation via Babel;
  • Authentication via JWT;
  • Routes mapping via express-routes-mapper;
  • Environments for development, testing, and production.

Table of Contents

Version notice

This project came a long way since the initial release in 2018. If you used this boilerplate before 2021, you should check a v0.x.x branch and v0 tags for the latest changes of v0.

Install and Use

Start by cloning this repository

# HTTPS
$ git clone https://github.com/MarkKhramko/nodejs-express-jwt

then use npm to

# Enter project root
$ cd nodejs-express-jwt
# Install dependencies
$ npm i
# Copy environment file 
$ cp .env.example .env
# Fill .env file
# ...
# If you want to use PostgreSQL (optional)
$ npm install -S pg pg-hstore
# Start the application (without code watcher)
$ npm start
#
# OR
#
# start development with nodemon
$ npm run dev

MySQL is supported out of the box as it is the default.

Controllers

Controllers in this boilerplate have a naming convention: ModelnameController.js and uses an object factory pattern. To use a model inside of your controller you have to require it. We use Sequelize as ORM, if you want further information read the Docs.

Folder structure

  • Controllers for your main API should be placed inside /api/ directory;
  • Controllers for HTTP requests should be placed inside /web/ directory.

Create a Controller

Example Controller for all CRUD oparations:

const Model = require('#models/Model');

model.exports = function ModelController() {
  const _create = (req, res) => {
    // body is part of a form-data
    const { value } = req.body;

    Model
      .create({
        key: value
      })
      .then((model) => {
        if(!model) {
          return res.status(400).json({ msg: 'Bad Request: Model not found' });
        }

        return res.status(200).json({ model });
      })
      .catch((err) => {
        // better save it to log file
        console.error(err);

        return res.status(500).json({ msg: 'Internal server error' });
      });
  };

  const _getAll = (req, res) => {
    Model
      .findAll()
      .then((models) => {
        if(!models){
          return res.status(400).json({ msg: 'Bad Request: Models not found' });
        }

        return res.status(200).json({ models });
      })
      .catch((err) => {
        // better save it to log file
        console.error(err);

        return res.status(500).json({ msg: 'Internal server error' });
      });
  };

  const _get = (req, res) => {
    // params is part of an url
    const { id } = req.params;

    Model
      .findOne({
        where: {
          id,
        },
      })
      .then((model) => {
        if(!model) {
          return res.status(400).json({ msg: 'Bad Request: Model not found' });
        }

        return res.status(200).json({ model });
      })
      .catch((err) => {
        // better save it to log file
        console.error(err);

        return res.status(500).json({ msg: 'Internal server error' });
      });
  };

  const _update = (req, res) => {
    // params is part of an url
    const { id } = req.params;

    // body is part of form-data
    const { value } = req.body;

    Model
      .findByPk(id)
      .then((model) => {
        if(!model) {
          return res.status(400).json({ msg: 'Bad Request: Model not found' });
        }

        return model
          .update({
            key: value,
          }).then((updatedModel) => {
            return res.status(200).json({ updatedModel });
          });
      })
      .catch((err) => {
        // better save it to log file
        console.error(err);

        return res.status(500).json({ msg: 'Internal server error' });
      });
  };

  const _destroy = (req, res) => {
    // params is part of an url
    const { id } = req.params;

    Model
      .findByPk(id)
      .then((model) => {
        if(!model) {
          return res.status(400).json({ msg: 'Bad Request: Model not found' })
        }

        model.destroy().then(() => {
          return res.status(200).json({ msg: 'Successfully destroyed model' });
        }).catch((err) => {
          // better save it to log file
          console.error(err);

          return res.status(500).json({ msg: 'Internal server error' });
        });
      })
      .catch((err) => {
        // better save it to log file
        console.error(err);

        return res.status(500).json({ msg: 'Internal server error' });
      });
  };

  // !IMPORTANT!
  // don't forget to return the functions:
  return {
    create:_create,
    getAll:_getAll,
    get:_get,
    update:_update,
    destroy:_destroy
  };
};

Models

Models in this boilerplate have a naming convention: Model.js and uses Sequelize to define Models, if you want further information read the Docs.

Create a Model

Example User Model:

const { DataTypes } = require('sequelize');
const database = require('#services/db.service');

// Password hasher.
const bcryptSevice = require('#services/bcrypt.service');


const User = database.define(
  'User',
  {
    email: {
      type: DataTypes.STRING(255),
      unique: true,
      allowNull: false
    },
    password: {
      type: DataTypes.STRING(255),
      allowNull: false
    },
  }
);

// Hooks:
User.beforeValidate((user, options) => {
  // Hash user's password.
  user.password = bcryptSevice.hashPassword(user);
})
// Hooks\

module.exports = User;

Policies

Policies are middleware functions that can run before hitting a apecific or more specified route(s).

Services

Services are little useful snippets, or calls to another API that are not the main focus of your API.

Example service:

Get comments from another API:

module.exports = {
  getComments:_getComments
};

async function _getComments() {
  try {
    const res = await fetch('https://jsonplaceholder.typicode.com/comments', {
      method: 'get'
    });
    // do some fancy stuff with the response.
  }
  catch(error) {
    // Process error.
  }
}

Configs

.env file

Database

Configure the keys with your credentials in .env file.

  DB_DIALECT=mysql
  DB_HOST=localhost
  DB_NAME=name
  DB_USER=root
  DB_PASSWORD=root
  DB_PORT=3609

Default dialect for the application is MySQL. To switch for PostgreSQL, type DB_DIALECT=postgres in .env file.

Note: if you use mysql make sure MySQL server is running on the machine

Note: to use a postgres run : npm i -S pg pg-hstore or yarn add pg pg-hstore

JWT

Set random secret access keys for your access and refresh tokens.

JWT_ACCESS_SECRET=<any random string>
JWT_REFRESH_SECRET=<any random string>

npm scripts

npm run dev

This is the entry for a developer. This command:

  • runs nodemon watch task for the all files conected to .app/app.js, except ./public directory;
  • Reads environment variable NODE_ENV from .env;
  • Opens the db connection for development;
  • Starts the server on 127.0.0.1:APP_PORT,

npm run production

This command:

  • Sets the environment variable to production;
  • Opens the db connection for production;
  • Starts the server on 127.0.0.1:APP_PORT.

Before running on production you have to set the environment vaiables:

  • APP_PORT - Port for your application (usually 80);
  • DB_DIALECT - mysql or postgresl;
  • DB_HOST - Host address of your database;
  • DB_NAME - Database name for production;
  • DB_USER - Database username for production;
  • DB_PASS - Database password for production;
  • DB_PORT - Database port for production;
  • JWT_ACCESS_SECRET - Secret for JSON web token for direct API requests;
  • JWT_REFRESH_SECRET - Secret for JSON web token to renew the Access-JWT.

npm start

This command:

  • Opens the db connection for default environment in .env file.
  • Simply start the server on 127.0.0.1:APP_PORT without a watcher.

npm run db:migrate

This command:

  • Ensures that database schemas are equivalent to the ones configured in /app/models/index.js.

npm run db:seed

This command:

  • Inserts all seeds, configured in /migrator/seeder.js into the database.

LICENSE

MIT 2018-present. By Mark Khramko

About

Node.js Express REST API boilerplate with JWT Authentication and support for MySQL and PostgreSQL.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 99.6%
  • Pug 0.4%