SyntaxStudy
Sign Up
Express.js Project Structure and Best Practices
Express.js Beginner 1 min read

Project Structure and Best Practices

A well-organized Express project separates concerns across directories: `routes/` for route definitions, `controllers/` for handler logic, `middleware/` for reusable middleware, `models/` for data-access code, and `config/` for environment-specific settings. Keeping `app.js` lean and delegating to these modules makes testing and maintenance far easier. Environment variables should be loaded with the `dotenv` package at the very top of the entry point before any other `require` calls. Never hard-code secrets or connection strings. Use `process.env.NODE_ENV` to gate behavior like verbose error messages. For production readiness, always set appropriate HTTP security headers (use the `helmet` package), enable CORS only for known origins, and validate all incoming data before passing it to business logic. These habits established early prevent the most common security vulnerabilities in Express applications.
Example
// project layout
// ├── app.js
// ├── server.js          (entry point – calls app.listen)
// ├── config/
// │   └── index.js
// ├── routes/
// │   ├── index.js
// │   └── users.js
// ├── controllers/
// │   └── userController.js
// └── middleware/
//     └── auth.js

// config/index.js
require('dotenv').config();

module.exports = {
    port:    process.env.PORT    || 3000,
    nodeEnv: process.env.NODE_ENV || 'development',
    dbUrl:   process.env.DATABASE_URL,
    jwtSecret: process.env.JWT_SECRET,
};

// app.js
const express = require('express');
const helmet  = require('helmet');
const cors    = require('cors');
const routes  = require('./routes');

const app = express();

app.use(helmet());
app.use(cors({ origin: process.env.ALLOWED_ORIGIN }));
app.use(express.json());
app.use('/api', routes);

module.exports = app;