Install all dependencies using npm.
npm installEnsure that you have MongoDB running and listening to port 27017.
For ease of development, use the following docker-compose command.
docker-compose up mongoRun the app by running the following command.
node bin/www.jsFor 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.jsUse the following docker-compose command to up the entire application stack.
docker-compose upThe 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-appThe 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