This project is a template project for my blog post on writing a Node.js Express REST API.
Install all dependencies using npm
.
npm install
Ensure that you have MongoDB running and listening to port 27017.
For ease of development, use the following docker-compose
command.
docker-compose up mongo
Run the app by running the following command.
node bin/www.js
For development, it's useful to restart the application whenever you make changes. Using nodemon
helps in this regard.
npm install --global nodemon
nodemon bin/www.js
Use the following docker-compose
command to up the entire application stack.
docker-compose up
The following table describes a standard directory structure for a web application. Refer to this blog post.
Path | Description |
---|---|
bin | Important binaries and scripts |
config | Configuration files for the project |
daos | Data access objects that couple the application to the ODM/ORM library |
models | Domain Model definitions, usually written using the ODM/ORM library |
routes | The defined REST API routes and their business logic |
services | Services used by the Express application |
util | Static utilities to store commonly used code |
To start off from scratch, you can install the Express generator.
npm install --global express-generator
express new-app
The following Express middleware are used:
Library | Purpose | npm install |
---|---|---|
morgan Docs |
Logs requests received by Express | npm install --save morgan |
multer Docs |
Processes multipart requests | npm install --save multer |
connect-rid Docs |
Adds a unique request ID to each header. Good for debugging. | npm install --save connect-rid |
serve-static Docs |
Enables Express to serve static files | npm install --save serve-static |
serve-favicon Docs |
Enables Express to serve a favicon file | npm install --save serve-favicon |
The following libraries are used:
Library | Purpose | npm install |
---|---|---|
Lodash Docs |
Utility functions for working with objects and arrays. | npm install --save lodash |
Moment.js Docs |
Utility functions for working with date and time. | npm install --save moment |
bluebird Docs |
My library of choice for working with Promises | npm install --save bluebird |
SuperAgent Docs |
Library for making HTTP requests | npm install --save superagent |
Mongoose Docs |
Object Document Mapper Library for working with MongoDB | npm install --save mongoose |
Sequelize Docs |
Object relational Mapper Libary for working with SQL databases like PostgreSQL or MySQL | npm install --save sequelize |
Passport Docs |
Authentication Library to secure your endpoints | npm install --save passport |
Winston Docs |
Logging Library | npm install --save winston |
It's good practice to have a .gitignore
that excludes node_modules
, any IDE generated files, configuration and RSA private keys.
When writing an application that connects to a database, or interacts with a secured API, all of this information is usually stored in a configuration file. I usually write a config loader and import it into my services.
When importing the config, I make sure to check environment variables for configuration overrides, as per the 12-Factor App spec. This is especially useful when packaging your web application in Docker containers as it enables you to modify configuration at runtime for different environments.
I always setup a logging service to give me flexibility to send my logs to external log collection endpoints. As I frequently use Docker containers, and as per 12-Factor Application standards, I usually send all logs to STDOUT``` and ```STDERR
by default.
I customize the Express app.js to include all my middleware. I also wire up the routes
import, where all my controller logic will be placed for each REST API endpoint I define.
app.js
I earlier setup a models
import into my services/mongooseService.js
. You can define rich models in Mongoose with validation, instance and static functions. For ease of creation of the schemas, I import and re-export them in an object that I can loop over to instantiate the Mongoose schemas.
Most of the business logic of the application goes into my routes. Each route trigger a function in the dao or services layers and returns a JSON response.
The following is an example route that exposes a REST API for working with a domain object.
I also heavily modify the Express starting script, so that I can support TLS and import all my services. I also implement a listener to exit the application when the SIGTERM
process signal is received, as per 12-Factor Application standards.
I also usually write a Dockerfile to package the application. Following this helpful guide from Node.JS
A .dockerignore``` file is useful to prevent the humongous ```node_modules
directory from being sent to the Docker host every time you build the Docker image.
It's also useful to create a docker-compose.yml
to make it easier to build and run your application services.
With the above docker-compose.yml in place, I can run the application together with a supporting MongoDB instance with the following command:
docker-compose up