{"id":20393154,"url":"https://github.com/aichbauer/express-rest-api-boilerplate","last_synced_at":"2025-04-05T03:09:58.305Z","repository":{"id":45229381,"uuid":"93336780","full_name":"aichbauer/express-rest-api-boilerplate","owner":"aichbauer","description":"Express REST API with JWT Authentication and support for sqlite, mysql, and postgresql","archived":false,"fork":false,"pushed_at":"2019-07-24T16:43:56.000Z","size":164,"stargazers_count":497,"open_issues_count":15,"forks_count":187,"subscribers_count":27,"default_branch":"master","last_synced_at":"2025-03-29T02:06:33.664Z","etag":null,"topics":["authentication","ava","boilerplate","express","jwt","mysql","nodejs","postgresql","sqlite"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aichbauer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-04T19:33:00.000Z","updated_at":"2025-03-15T06:00:11.000Z","dependencies_parsed_at":"2022-09-16T14:26:47.932Z","dependency_job_id":null,"html_url":"https://github.com/aichbauer/express-rest-api-boilerplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aichbauer%2Fexpress-rest-api-boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aichbauer%2Fexpress-rest-api-boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aichbauer%2Fexpress-rest-api-boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aichbauer%2Fexpress-rest-api-boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aichbauer","download_url":"https://codeload.github.com/aichbauer/express-rest-api-boilerplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247280272,"owners_count":20912967,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","ava","boilerplate","express","jwt","mysql","nodejs","postgresql","sqlite"],"created_at":"2024-11-15T03:47:41.483Z","updated_at":"2025-04-05T03:09:58.289Z","avatar_url":"https://github.com/aichbauer.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# express-rest-api-boilerplate\n\n\u003e Express REST API with JWT Authentication and support for sqlite, mysql, and postgresql\n\n- authentication via [JWT](https://jwt.io/)\n- routes mapping via [express-routes-mapper](https://github.com/aichbauer/express-routes-mapper)\n- support for [sqlite](https://www.sqlite.org/), [mysql](https://www.mysql.com/), and [postgresql](https://www.postgresql.org/)\n- environments for `development`, `testing`, and `production`\n- linting via [eslint](https://github.com/eslint/eslint)\n- integration tests running with [Jest](https://github.com/facebook/jest)\n- built with [npm sripts](#npm-scripts)\n- example for User model and User controller, with jwt authentication, simply type `npm i` and `npm start`\n\n## Table of Contents\n\n- [Install \u0026 Use](#install-and-use)\n- [Folder Structure](#folder-structure)\n- [Controllers](#controllers)\n  - [Create a Controller](#create-a-controller)\n- [Models](#models)\n  - [Create a Model](#create-a-model)\n- [Policies](#policies)\n  - [auth.policy](#authpolicy)\n- [Services](#services)\n- [Config](#config)\n  - [Connection and Database](#connection-and-database)\n- [Routes](#routes)\n  - [Create Routes](#create-routes)\n- [Test](#test)\n  - [Setup](#setup)\n- [npm Scripts](#npm-scripts)\n\n## Install and Use\n\nStart by cloning this repository\n\n```sh\n# HTTPS\n$ git clone https://github.com/aichbauer/express-rest-api-boilerplate.git\n```\n\nthen\n\n```sh\n# cd into project root\n$ yarn\n# to use mysql\n$ yarn add mysql2\n# to use postgresql\n$ yarn add pg pg-hstore\n# start the api\n$ yarn start\n```\n\nor\n\n```sh\n# cd into project root\n$ npm i\n# to use mysql\n$ npm i mysql2 -S\n# to use postgresql\n$ npm i -S pg pg-hstore\n# start the api\n$ npm start\n```\n\nsqlite is supported out of the box as it is the default.\n\n## Folder Structure\n\nThis boilerplate has 4 main directories:\n\n- api - for controllers, models, services, etc.\n- config - for routes, database, etc.\n- db - this is only a dir for the sqlite db, the default for NODE_ENV development\n- test - using [Jest](https://github.com/facebook/jest)\n\n## Controllers\n\n### Create a Controller\n\nControllers in this boilerplate have a naming convention: `ModelnameController.js` and uses an object factory pattern.\nTo use a model inside of your controller you have to require it.\nWe use [Sequelize](http://docs.sequelizejs.com/) as ORM, if you want further information read the [Docs](http://docs.sequelizejs.com/).\n\nExample Controller for all **CRUD** oparations:\n\n```js\nconst Model = require('../models/Model');\n\nconst ModelController = () =\u003e {\n  const create = async (req, res) =\u003e {\n    // body is part of a form-data\n    const { value } = req.body;\n\n    try {\n      const model = await Model.create({\n        key: value\n      });\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' });\n      }\n\n      return res.status(200).json({ model });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const getAll = async (req, res) =\u003e {\n    try {\n      const model = await Model.findAll();\n\n      if(!models){\n        return res.status(400).json({ msg: 'Bad Request: Models not found' });\n      }\n\n      return res.status(200).json({ models });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const get = async (req, res) =\u003e {\n    // params is part of an url\n    const { id } = req.params;\n\n    try {\n      const model = await Model.findOne({\n        where: {\n          id,\n        },\n      });\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' });\n      }\n\n      return res.status(200).json({ model });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const update = async (req, res) =\u003e {\n    // params is part of an url\n    const { id } = req.params;\n\n    // body is part of form-data\n    const { value } = req.body;\n\n    try {\n      const model = await Model.findById(id);\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' });\n      }\n\n      const updatedModel = await model.update({\n        key: value,\n      )};\n\n      return res.status(200).json({ updatedModel });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const destroy = async (req, res) =\u003e {\n    // params is part of an url\n    const { id } = req.params;\n\n    try {\n      const model =  Model.findById(id);\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' })\n      }\n\n      await model.destroy();\n\n      return res.status(200).json({ msg: 'Successfully destroyed model' });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  // IMPORTANT\n  // don't forget to return the functions\n  return {\n    create,\n    getAll,\n    get,\n    update,\n    destroy,\n  };\n};\n\nmodel.exports = ModelController;\n```\n\n## Models\n\n### Create a Model\n\nModels in this boilerplate have a naming convention: `Model.js` and uses [Sequelize](http://docs.sequelizejs.com/) to define our Models, if you want further information read the [Docs](http://docs.sequelizejs.com/).\n\nExample User Model:\n\n```js\nconst Sequelize = require('sequelize');\n\n// for encrypting our passwords\nconst bcryptSevice = require('../services/bcrypt.service');\n\n// the DB connection\nconst sequelize = require('../../config/database');\n\n// hooks are functions that can run before or after a specific event\nconst hooks = {\n  beforeCreate(user) {\n    user.password = bcryptSevice.password(user);\n  },\n};\n\n// naming the table in DB\nconst tableName = 'users';\n\n// the actual model\nconst User = sequelize.define('User', {\n  username: {\n    type: Sequelize.STRING,\n    unique: true,\n  },\n  password: {\n    type: Sequelize.STRING,\n  },\n}, { hooks, tableName });\n\n// instead of using instanceMethod\n// in sequelize \u003e 4 we are writing the function\n// to the prototype object of our model.\n// as we do not want to share sensitive data, the password\n// field gets ommited before sending\nUser.prototype.toJSON = function () {\n  const values = Object.assign({}, this.get());\n\n  delete values.password;\n\n  return values;\n};\n\n// IMPORTANT\n// don't forget to export the Model\nmodule.exports = User;\n```\n\n## Policies\n\nPolicies are middleware functions that can run before hitting a apecific or more specified route(s).\n\nExample policy:\n\nOnly allow if the user is marked as admin.\n\n\u003e Note: this is not a secure example, only for presentation puposes\n\n```js\nmodule.exports = (req, res, next) =\u003e {\n  if(req.body.userrole === 'admin') {\n    // do some verification stuff\n    const verified = verifyAdmin(req.body.userid);\n\n    if(verified) {\n      return next();\n    }\n\n    return res.status(401).json({ msg: 'Unauthorized' });\n  }\n\n  return res.status(401).json({ msg: 'Unauthorized' });\n};\n```\n\nTo use this policy on all routes that only admins are allowed:\n\napi.js\n\n```js\nconst adminPolicy = require('./policies/admin.policy');\n\napp.all('/admin/*', (req, res, next) =\u003e adminPolicy(req,res,next));\n```\n\nOr for one specific route\n\napi.js\n\n```js\nconst adminPolicy = require('./policies/admin.policy');\n\napp.get('/admin/myroute',\n  (req, res, next) =\u003e adminPolicy(req,res,next),\n  (req, res) =\u003e {\n  //do some fancy stuff\n});\n```\n\n## auth.policy\n\nThe `auth.policy` checks wether a `JSON Web Token` ([further information](https://jwt.io/)) 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]`.\nThe policy runs default on all api routes that are are prefixed with `/private`. To map multiple routes read the [docs](https://github.com/aichbauer/express-routes-mapper/blob/master/README.md) from `express-routes-mapper`.\n\nTo use this policy on all routes of a specific prefix:\n\napp.js\n\n```js\napp.use('/prefix', yourRoutes);\napp.all('/prefix', (req, res, next) =\u003e auth(req, res, next));\n```\n\nor to use this policy on one specific route:\n\napp.js\n\n```js\napp.get('/specificRoute',\n  (req, res, next) =\u003e auth(req, res, next),\n  (req, res) =\u003e {\n  // do some fancy stuff\n});\n```\n\n## Services\n\nServices are little useful snippets, or calls to another API that are not the main focus of your API.\n\nExample service:\n\nGet comments from another API:\n\n```js\nconst commentService = () =\u003e {\n  const getComments = async () =\u003e {\n    try {\n      const res = await fetch('https://jsonplaceholder.typicode.com/comments', {\n        method: 'get'\n      });\n\n      // do some fancy stuff with the response\n    } catch (err) {\n      // handle a error\n    }\n  };\n\n  return {\n    getComments,\n  };\n};\n\nmodule.exports = commentService;\n```\n\n## Config\n\nHolds all the server configurations.\n\n## Connection and Database\n\n\u003e Note: if you use msql make sure mysql server is running on the machine\n\n\u003e Note: if you use postgres make sure postgres server is running on the machine\n\nThis two files are the way to establish a connaction to a database.\n\nYou 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.\n\n\u003e Note: to run a mysql db install these package with: `yarn add mysql2` or `npm i mysql2 -S`\n\n\u003e Note: to run a postgres db run these package with: `yarn add pg pg-hstore` or `npm i -S pg pg-hstore`\n\nNow simple configure the keys with your credentials.\n\n```js\n{\n  database: 'databasename',\n  username: 'username',\n  password: 'password',\n  host: 'localhost',\n  dialect: 'sqlite' || 'mysql' || 'postgres',\n}\n```\n\nTo not configure the production code.\n\nTo start the DB, add the credentials for production. add `environment variables` by typing e.g. `export DB_USER=yourusername` before starting the api.\n\n## Routes\n\nHere 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.\n\n## Create Routes\n\nFor further information read the [docs](https://github.com/aichbauer/express-routes-mapper/blob/master/README.md) of express-routes-mapper.\n\nExample for User Model:\n\n\u003e Note: Only supported Methods are **POST**, **GET**, **PUT**, and **DELETE**.\n\nuserRoutes.js\n\n```js\nconst userRoutes = {\n  'POST /user': 'UserController.create',\n  'GET /users': 'UserController.getAll',\n  'GET /user/:id': 'UserController.get',\n  'PUT /user/:id': 'UserController.update',\n  'DELETE /user/': 'UserController.destroy',\n};\n\nmodule.exports = userRoutes;\n```\n\nTo use these routes in your application, require them in the config/index.js and export them.\n\n```js\nconst userRoutes = require('./userRoutes');\n\nconst config = {\n  allTheOtherStuff,\n  userRoutes,\n};\n\nmodule.exports = config;\n```\n\napi.js\n\n```js\nconst mappedUserRoutes = mapRoutes(config.userRoutes, 'api/controllers/');\n\napp.use('/prefix', mappedUserRoutes);\n\n// to protect them with authentication\napp.all('/prefix/*', (req, res, next) =\u003e auth(req, res, next));\n```\n\n## Test\n\nAll test for this boilerplate uses [Jest](https://github.com/facebook/jest) and [supertest](https://github.com/visionmedia/superagent) for integration testing. So read their docs on further information.\n\n### Setup\n\nThe 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.\n\n### Controller\n\n\u003e Note: those request are asynchronous, we use `async await` syntax.\n\n\u003e Note: As we don't use import statements inside the api we also use the require syntax for tests\n\nTo test a Controller we create `fake requests` to our api routes.\n\nExample `GET /user` from last example with prefix `prefix`:\n\n```js\nconst request = require('supertest');\nconst {\n  beforeAction,\n  afterAction,\n} = require('../setup/_setup');\n\nlet api;\n\nbeforeAll(async () =\u003e {\n  api = await beforeAction();\n});\n\nafterAll(() =\u003e {\n  afterAction();\n});\n\ntest('test', async () =\u003e {\n  const token = 'this-should-be-a-valid-token';\n\n  const res = await request(api)\n    .get('/prefix/user')\n    .set('Accept', /json/)\n    // if auth is needed\n    .set('Authorization', `Bearer ${token}`)\n    .set('Content-Type', 'application/json')\n    .expect(200);\n\n  // read the docs of jest for further information\n  expect(res.body.user).toBe('something');\n});\n```\n\n### Models\n\nAre usually automatically tested in the integration tests as the Controller uses the Models, but you can test them separatly.\n\n## npm scripts\n\nThere are no automation tool or task runner like [grunt](https://gruntjs.com/) or [gulp](http://gulpjs.com/) used for this boilerplate. These boilerplate only uses npm scripts for automatization.\n\n### npm start\n\nThis is the entry for a developer. This command:\n\nBy default it uses a sqlite databse, if you want to migrate the sqlite db by each start, disable the `prestart` and `poststart` command. Also mind if you are using a sqlite database to delete the `drop-sqlite-db` in the prepush hook.\n\n- runs **nodemon watch task** for the all files conected to `.api/api.js`\n- sets the **environment variable** `NODE_ENV` to `development`\n- opens the db connection for `development`\n- starts the server on 127.0.0.1:2017\n\n### npm test\n\nThis command:\n\n- runs `npm run lint` ([eslint](http://eslint.org/)) with the [airbnb styleguide](https://github.com/airbnb/javascript) without arrow-parens rule for **better readability**\n- sets the **environment variable** `NODE_ENV` to `testing`\n- creates the `database.sqlite` for the test\n- runs `jest --coverage` for testing with [Jest](https://github.com/facebook/jest) and the coverage\n- drops the `database.sqlite` after the test\n\n## npm run production\n\nThis command:\n\n- sets the **environment variable** to `production`\n- opens the db connection for `production`\n- starts the server on 127.0.0.1:2017 or on 127.0.0.1:PORT_ENV\n\nBefore running on production you have to set the **environment vaiables**:\n\n- DB_NAME - database name for production\n- DB_USER - database username for production\n- DB_PASS - database password for production\n- DB_HOST - database host for production\n- JWT_SECERT - secret for json web token\n\nOptional:\n\n- PORT - the port your api on 127.0.0.1, default to 2017\n\n### other commands\n\n- `npm run dev` - simply start the server withou a watcher\n- `npm run create-sqlite-db` - creates the sqlite database\n- `npm run drop-sqlite-db` - drops **ONLY** the sqlite database\n- `npm run lint` - linting with [eslint](http://eslint.org/)\n- `npm run nodemon` - same as `npm start``\n- `npm run prepush` - a hook wich runs before pushing to a repository, runs `npm test` and `npm run dropDB`\n- `pretest` - runs linting before `npm test`\n- `test-ci` - only runs tests, nothing in pretest, nothing in posttest, for better use with ci tools\n\n## LICENSE\n\nMIT © Lukas Aichbauer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faichbauer%2Fexpress-rest-api-boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faichbauer%2Fexpress-rest-api-boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faichbauer%2Fexpress-rest-api-boilerplate/lists"}