/express-mongo-api-boilerplate

A rock solid project template for building Node.js APIs

Primary LanguageJavaScriptMIT LicenseMIT

Rock Solid Express Application Architecture

This is a battle tested application architecture that I've been using for a while now on medium to large projects at work. Although not perfect, so far it has proved itself to be a reliable, scalable and manageable project architecture.

I've also tried to comply with many popular Node.js best practices as long as they were within my limits. There are some best practices that I left off intentionally such as the usage of Node.js specific plugins for ESLint. I've been using the amazing airbnb/javascript plugin for all my projects and I'm happy with it. Another practice that I avoid is wrapping common utilities in a separate package. Well I do opt-in for a separate package when I have large number of shared utilities but for simple stuff like the authenticate or authorize middleware, I stick with simple require() statements.

There are some other best practices that I haven't yet picked up but will soon. Such as the practice of tagged tests, higher test coverage or testing my middleware in isolation. Also the production oriented best practices have been left off of this project but we do follow the common best practices at work when actually putting someting in production.

  • Project Structure Practices
    • Structure your solution by components
    • Layer your components, keep the web layer within its boundaries
    • Wrap common utilities as npm packages
    • Separate Express 'app' and 'server'
    • Use environment aware, secure and hierarchical config
  • Error Handling Practices
    • Use Async-Await or promises for async error handling
    • Use only the built-in Error object
    • Distinguish operational vs programmer errors
    • Handle errors centrally, not within a middleware
    • Document API errors using Swagger or GraphQL
    • Exit the process gracefully when a stranger comes to town
    • Use a mature logger to increase error visibility
    • Test error flows using your favorite test framework
    • Discover errors and downtime using APM products
    • Catch unhandled promise rejections
    • Fail fast, validate arguments using a dedicated library
    • Always await promises before returning to avoid a partial stacktrace
  • Code Style Practices
    • Use ESLint
    • Node.js specific plugins
    • Start a Codeblock's Curly Braces on the Same Line
    • Separate your statements properly
    • Name your functions
    • Use naming conventions for variables, constants, functions and classes
    • Prefer const over let. Ditch the var
    • Require modules first, not inside functions
    • Require modules by folders, as opposed to the files directly
    • Use the === operator
    • Use Async Await, avoid callbacks
    • Use arrow function expressions (=>)
  • Testing And Overall Quality Practices
    • At the very least, write API (component) testing
    • Include 3 parts in each test name
    • Structure tests by the AAA pattern
    • Detect code issues with a linter
    • Avoid global test fixtures and seeds, add data per-test
    • Constantly inspect for vulnerable dependencies
    • Tag your tests
    • Check your test coverage, it helps to identify wrong test patterns
    • Inspect for outdated packages
    • Use production-like environment for e2e testing
    • Refactor regularly using static analysis tools
    • Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world)
    • Test your middlewares in isolation
  • Docker Best Practices
    • Use multi-stage builds for leaner and more secure Docker images
    • Bootstrap using node command, avoid npm start
    • Let the Docker runtime handle replication and uptime
    • Use .dockerignore to prevent leaking secrets
    • Clean-up dependencies before production
    • Shutdown smartly and gracefully
    • Set memory limits using both Docker and v8
    • Plan for efficient caching
    • Use explicit image reference, avoid latest tag
    • Prefer smaller Docker base images
    • Clean-out build-time secrets, avoid secrets in args
    • Scan images for multi layers of vulnerabilities
    • Clean NODE_MODULE cache
    • Generic Docker practices
    • Lint your Dockerfile

If you wish to learn more about this architecture and how you may use or extend this project according to your needs, checkout my blog post on https://farhan.dev and as usual, quality contributions are welcomed.