A boilerplate for Node.js REST API back-ends.
- Specification-first routing & validation sourced from Swagger specification.
- Basic Sequelize ORM setup with functional, tested User model.
- Working, tested registration/login/account endpoints.
- Passport.js with Local (username + password), Bearer (JSON Web Tokens) authentication strategies.
- Configuration based on environment variables loaded from
.env.*
files. - Robust logging system with daily log rotation file.
Key pillars of chosen application stack are:
- swagger-node - specification-first routing, request validation, using Express underneath.
- Sequelize - promise-based SQL ORM, used for interacting with PostgreSQL database. Expect to use sequelize-cli for most common database-related tasks.
It's key to get properly acquainted with them before starting development of any application seeded from this repository - follow links provided above and make sure that you understand their basic usage patterns.
Additionally, several other dependencies are installed and chosen to be universally used:
- Passport - de-facto standard authentication middleware in Node.js
- node.bcrypt.js - used for hashing passwords associated with example User model
- node-jwt-simple - encoding/decoding JSON Web Tokens, used for basic route authentication
- dotenv - loading of environment variables from
.env.*
files, where environment-specific configuration options are being held. - SuperTest - request-based controller tests.
- should.js - test assertion library. (Note: other assertion styles are by no means forbidden.)
To get base application up and running, make sure you have recent versions of Node.js and PostgreSQL database installed locally, then execute following commands in your terminal:
npm install # install all dependencies listed in package.json file
npm install -g swagger sequelize-cli # install CLI tools necessary for development
cp .env.development.sample .env.development
cp .env.test.sample .env.test
Now you'll need to create development, test databases and their user(s). Using default settings from sample .env.*
files and CLI wrappers around basic PostgreSQL SQL statements (createuser, createdb):
createuser root
createdb database_development
createdb database_test
Run database migrations, using sequelize-cli:
sequelize db:migrate
NODE_ENV=test sequelize db:migrate
That's it! Now you should be able to run automated application tests using npm test
command, or start API server running locally with npm start
(by default listening on port 10010).
api/controllers/ # actual API request handlers
api/swagger/ # current API Swagger specification is held here (swagger.yaml file)
initializers/ # single-run setup operations - to be required in main app.js file upon application launch
middlewares/ # custom middlewares
config/ # files exporting configuration options - config.js being main one
models/ # Sequelize models, including index.js file properly loading and grouping them
migrations/ # Sequelize database migrations
seeders/ # Sequelize database seeds
test/ # tests for controllers, models, other logical units - within properly reflected file structure
It's strongly encouraged to follow above file structure for already defined logical units. It's perfectly allowed to extend it as seen fit during actual development. Good example of this may be service objects - an example one likely held as api/services/some-service.js
, with unit tests describing it located under test/api/services/some-service.spec.js
.
Follow Airbnb style guide. ESLint together with Airbnb base config is set-up to lint your code.
Following are some of the most common, practical scenarios that will probably happen in day-to-day development.
- Execute
npm edit
command. This will launch and open editor for Swagger definition describing your API. Assuming Prop resource, you'd most likely want to describe following endpoints:
POST /props
- create a PropGET /props
- fetch list of existing PropsGET /props/:id
- fetch specific PropPUT /props/:id
- edit an existing PropDELETE /props/:id
- delete an existing Prop
Refer to existing documentation for User-related endpoints and Swagger specification docs for more detailed information.
- Create controller file defining route handlers for described endpoints. These files should reside in
api/controllers
directory and follow resource-based naming pattern, i.e.api/controllers/props.js
. Seeapi/controllers/users.js
file for example implementation. - Create test file for your controller - i.e.
test/api/controllers/props.js
. - Add actual handler implementations (in i.e.
api/controllers/props.js
file) file together with request-based tests (in i.e.test/api/controllers/props.js
file).
- Use sequelize-cli
model:create
command to generate initial model-defining file inmodels/
directory together with corresponding database migration inmigrations/
directory. Example for basic Prop model may look like this:
sequelize model:create --name Prop --attributes 'text:string'
- Execute created database migration:
sequelize db:migrate
- Create corresponding model test file, i.e.
test/models/prop.spec.js
- Add custom model methods, tests for them as necessary.
- Created model is now available as export of
models/index.js
file:
const { Prop } = require('./models');
See Sequelize model usage docs for further details on its usage.
- Use sequelize-cli
migration:create
command to generate placeholder database migration file inmigrations/
directory:
sequelize migration:create --name "add-first-name-to-user"
- Modify created file with timestamp, i.e.
migrations/20170101111111-add-first-name-to-user.js
, to apply necessary database structure changes - see appropriate section in Sequelize documentation. - Apply created database migration, or rollback as necessary:
sequelize db:migrate # executes all migrations
sequelize db:migrate:undo # reverts latest database migration
- Create new
.js
file inmiddlewares
directory. - Define your own middleware function inside and export it.
- Globally used middleware: require that file in
app.js
file and use it like this:
app.use(customMiddleware);
- Per-route middleware: require that file in your controller file, i.e.
api/controllers/users.js
, and compose controller action using bundled compose-middleware helper:
module.exports = {
someAction: compose([
customMiddleware,
function(req, res) { res.status(200).send(...) },
]),
};
- Add appropriate key/value pair to your
.env.*
and.env.*.sample
files, i.e.:
SOME_SERVICE_TOKEN=123
- Edit
config/config.js
file to pass it through fromprocess.env
object, optionally marking as required (seeREQUIRED_KEYS
array). - Wherever you need to use your new config option - simply require it, i.e.:
const { SOME_SERVICE_TOKEN } = require('../config/config');
If something is unclear, confusing, or needs to be refactored, please let us know. Pull requests are always welcome, but note the minimalistic nature of the repository - it's designed as lean, universal starting point for actual projects, and nothing more.
The MIT License (MIT)
Copyright (c) 2017 netguru.co