• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

aichbauer/express-rest-api-boilerplate: Express REST API with JWT Authentication ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

aichbauer/express-rest-api-boilerplate

开源软件地址:

https://github.com/aichbauer/express-rest-api-boilerplate

开源编程语言:

JavaScript 100.0%

开源软件介绍:

express-rest-api-boilerplate

Express REST API with JWT Authentication and support for sqlite, mysql, and postgresql

  • authentication via JWT
  • routes mapping via express-routes-mapper
  • support for sqlite, mysql, and postgresql
  • environments for development, testing, and production
  • linting via eslint
  • integration tests running with Jest
  • built with npm sripts
  • example for User model and User controller, with jwt authentication, simply type npm i and npm start

Table of Contents

Install and Use

Start by cloning this repository

# HTTPS
$ git clone https://github.com/aichbauer/express-rest-api-boilerplate.git

then

# cd into project root
$ yarn
# to use mysql
$ yarn add mysql2
# to use postgresql
$ yarn add pg pg-hstore
# start the api
$ yarn start

or

# cd into project root
$ npm i
# to use mysql
$ npm i mysql2 -S
# to use postgresql
$ npm i -S pg pg-hstore
# start the api
$ npm start

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

Folder Structure

This boilerplate has 4 main directories:

  • api - for controllers, models, services, etc.
  • config - for routes, database, etc.
  • db - this is only a dir for the sqlite db, the default for NODE_ENV development
  • test - using Jest

Controllers

Create a Controller

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.

Example Controller for all CRUD oparations:

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

const ModelController = () => {
  const create = async (req, res) => {
    // body is part of a form-data
    const { value } = req.body;

    try {
      const model = await Model.create({
        key: value
      });

      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 = async (req, res) => {
    try {
      const model = await Model.findAll();

      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 = async (req, res) => {
    // params is part of an url
    const { id } = req.params;

    try {
      const model = await Model.findOne({
        where: {
          id,
        },
      });

      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 = async (req, res) => {
    // params is part of an url
    const { id } = req.params;

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

    try {
      const model = await Model.findById(id);

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

      const updatedModel = await model.update({
        key: value,
      )};

      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 = async (req, res) => {
    // params is part of an url
    const { id } = req.params;

    try {
      const model =  Model.findById(id);

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

      await model.destroy();

      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' });
    }
  };

  // IMPORTANT
  // don't forget to return the functions
  return {
    create,
    getAll,
    get,
    update,
    destroy,
  };
};

model.exports = ModelController;

Models

Create a Model

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

Example User Model:

const Sequelize = require('sequelize');

// for encrypting our passwords
const bcryptSevice = require('../services/bcrypt.service');

// the DB connection
const sequelize = require('../../config/database');

// hooks are functions that can run before or after a specific event
const hooks = {
  beforeCreate(user) {
    user.password = bcryptSevice.password(user);
  },
};

// naming the table in DB
const tableName = 'users';

// the actual model
const User = sequelize.define('User', {
  username: {
    type: Sequelize.STRING,
    unique: true,
  },
  password: {
    type: Sequelize.STRING,
  },
}, { hooks, tableName });

// instead of using instanceMethod
// in sequelize > 4 we are writing the function
// to the prototype object of our model.
// as we do not want to share sensitive data, the password
// field gets ommited before sending
User.prototype.toJSON = function () {
  const values = Object.assign({}, this.get());

  delete values.password;

  return values;
};

// IMPORTANT
// don't forget to export the Model
module.exports = User;

Policies

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

Example policy:

Only allow if the user is marked as admin.

Note: this is not a secure example, only for presentation puposes

module.exports = (req, res, next) => {
  if(req.body.userrole === 'admin') {
    // do some verification stuff
    const verified = verifyAdmin(req.body.userid);

    if(verified) {
      return next();
    }

    return res.status(401).json({ msg: 'Unauthorized' });
  }

  return res.status(401).json({ msg: 'Unauthorized' });
};

To use this policy on all routes that only admins are allowed:

api.js

const adminPolicy = require('./policies/admin.policy');

app.all('/admin/*', (req, res, next) => adminPolicy(req,res,next));

Or for one specific route

api.js

const adminPolicy = require('./policies/admin.policy');

app.get('/admin/myroute',
  (req, res, next) => adminPolicy(req,res,next),
  (req, res) => {
  //do some fancy stuff
});

auth.policy

The auth.policy checks wether a JSON Web Token (further information) is send in the header of an request as Authorization: Bearer [JSON Web Token] or inside of the body of an request as token: [JSON Web Token]. The policy runs default on all api routes that are are prefixed with /private. To map multiple routes read the docs from express-routes-mapper.

To use this policy on all routes of a specific prefix:

app.js

app.use('/prefix', yourRoutes);
app.all('/prefix', (req, res, next) => auth(req, res, next));

or to use this policy on one specific route:

app.js

app.get('/specificRoute',
  (req, res, next) => auth(req, res, next),
  (req, res) => {
  // do some fancy stuff
});

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:

const commentService = () => {
  const getComments = async () => {
    try {
      const res = await fetch('https://jsonplaceholder.typicode.com/comments', {
        method: 'get'
      });

      // do some fancy stuff with the response
    } catch (err) {
      // handle a error
    }
  };

  return {
    getComments,
  };
};

module.exports = commentService;

Config

Holds all the server configurations.

Connection and Database

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

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

This two files are the way to establish a connaction to a database.

You only need to touch connection.js, default for development is sqlite, but it is easy as typing mysql or postgres to switch to another db.

Note: to run a mysql db install these package with: yarn add mysql2 or npm i mysql2 -S

Note: to run a postgres db run these package with: yarn add pg pg-hstore or npm i -S pg pg-hstore

Now simple configure the keys with your credentials.

{
  database: 'databasename',
  username: 'username',
  password: 'password',
  host: 'localhost',
  dialect: 'sqlite' || 'mysql' || 'postgres',
}

To not configure the production code.

To start the DB, add the credentials for production. add environment variables by typing e.g. export DB_USER=yourusername before starting the api.

Routes

Here you define all your routes for your api. It doesn't matter how you structure them. By default they are mapped on privateRoutes and publicRoutes. You can define as much routes files as you want e.g. for every model or for specific use cases, e.g. normal user and admins.

Create Routes

For further information read the docs of express-routes-mapper.

Example for User Model:

Note: Only supported Methods are POST, GET, PUT, and DELETE.

userRoutes.js

const userRoutes = {
  'POST /user': 'UserController.create',
  'GET /users': 'UserController.getAll',
  'GET /user/:id': 'UserController.get',
  'PUT /user/:id': 'UserController.update',
  'DELETE /user/': 'UserController.destroy',
};

module.exports = userRoutes;

To use these routes in your application, require them in the config/index.js and export them.

const userRoutes = require('./userRoutes');

const config = {
  allTheOtherStuff,
  userRoutes,
};

module.exports = config;

api.js

const mappedUserRoutes = mapRoutes(config.userRoutes, 'api/controllers/');

app.use('/prefix', mappedUserRoutes);

// to protect them with authentication
app.all('/prefix/*', (req, res, next) => auth(req, res, next));

Test

All test for this boilerplate uses Jest and supertest for integration testing. So read their docs on further information.

Setup

The setup directory holds the _setup.js which holds beforeAction which starts a test express application and connects to your test database, and a afterAction which closes the db connection.

Controller

Note: those request are asynchronous, we use async await syntax.

Note: As we don't use import statements inside the api we also use the require syntax for tests

To test a Controller we create fake re


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
jaimeMF/youtube-dl-api-server: A youtube-dl REST API server发布时间:2022-04-03
下一篇:
timotheus/ebaysdk-python: eBay API SDK for Python发布时间:2022-04-03
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap