Understanding Express Application Lifecycle
1. Understanding Application Initialization
| Stage | What happens |
1. express() | Creates app object (function + EventEmitter) |
| 2. Settings | app.set(), app.enable() |
| 3. Middleware | app.use() registers in order |
| 4. Routes | app.get/post/...() |
| 5. Error handlers | Last (4-arg signature) |
6. app.listen() | Bind port, start accepting connections |
2. Understanding Middleware Registration Order
Warning: Order is execution order. Logging, body parsing, CORS, helmet must run BEFORE routes. Error middleware MUST be registered LAST.
| Position | Middleware |
| 1 | Security (helmet, cors) |
| 2 | Compression |
| 3 | Request logging (morgan) |
| 4 | Body parsers (json, urlencoded) |
| 5 | Cookie/session parsers |
| 6 | Authentication (passport) |
| 7 | Static files |
| 8 | Routers |
| 9 | 404 handler |
| 10 | Error handler (4-arg) |
3. Understanding Request-Response Cycle
Client ──HTTP request──> Node http.Server
│
▼
app(req, res)
│
▼
┌── middleware 1 ──┐
│ next() │
▼ │
middleware 2 │ next(err) skips
│ │ to error handler
▼ │
router/route │
│ │
▼ │
res.send() ◄──────────┘
│
▼
Response sent → cycle ends
| Termination | Triggers |
res.send/json/end/sendFile/render/redirect | Response written, cycle ends |
next() | Pass to next middleware/route |
next(err) | Skip to error middleware |
next("route") | Skip remaining handlers in current route |
next("router") | Skip remaining middleware in current Router |
4. Using Application Events
| Event | Source | Use |
mount | app | Sub-app mounted on parent |
listening | http.Server | Server bound to port |
error | http.Server | Bind/runtime errors |
connection | http.Server | New TCP connection |
close | http.Server | Server stopped accepting |
Example: Lifecycle hooks
const server = app.listen(3000);
server.on("listening", () => console.log("Bound to port"));
server.on("error", (err) => {
if (err.code === "EADDRINUSE") console.error("Port in use");
process.exit(1);
});
process.on("SIGTERM", () => server.close(() => process.exit(0)));
5. Handling Startup Initialization
Example: Async bootstrap
async function bootstrap() {
await db.connect();
await cache.connect();
await loadConfig();
const app = createApp();
const server = app.listen(env.PORT, () =>
console.log(`Ready on :${env.PORT}`)
);
return server;
}
bootstrap().catch((err) => {
console.error("Startup failed:", err);
process.exit(1);
});
| Pattern | When to use |
| Lazy connection | Connect on first query (serverless) |
| Pre-warm | Connect before listen (long-running servers) |
| Health gate | Don't accept traffic until deps ready |
6. Understanding Route Matching Order
| Rule | Detail |
| First match wins | Express tries routes in registration order |
| Specificity matters | Define /users/me BEFORE /users/:id |
| Methods filter | app.get only matches GET |
| Mount path strips | app.use("/api", router) → router sees /users not /api/users |
7. Implementing Graceful Startup
Example: Readiness gate
let ready = false;
app.get("/healthz", (_, res) => res.sendStatus(200));
app.get("/readyz", (_, res) => res.sendStatus(ready ? 200 : 503));
await Promise.all([db.connect(), cache.connect()]);
ready = true;
| Probe | Purpose |
Liveness (/healthz) | Process running? K8s restarts on failure |
Readiness (/readyz) | Ready to serve? K8s removes from load balancer |
8. Understanding Middleware Stack Execution
req ─▶ MW1 ─next()─▶ MW2 ─next()─▶ ROUTE ─res.json()─▶ resp
│
(if error thrown) │
▼
ERROR MW (err, req, res, next)
| Concept | Detail |
| Stack | Linear array of {path, method, handler} |
| Cursor | next() advances; cannot go back |
| Branching | Routers form sub-stacks |
9. Implementing Application Bootstrap Patterns
Example: Modular factory
// app.js — pure factory, no side effects
export function createApp({ db, cache, logger }) {
const app = express();
app.use(helmet());
app.use(express.json());
app.use("/api/v1", createApiRouter({ db, cache }));
app.use(errorHandler(logger));
return app;
}
// server.js — composition root
const app = createApp({ db, cache, logger });
app.listen(env.PORT);
// app.test.js — supertest without listen()
const app = createApp({ db: mockDb, cache: mockCache, logger: noopLogger });
await request(app).get("/api/v1/users").expect(200);
10. Understanding Express Settings
| API | Description |
app.set(key, val) | Store setting |
app.get(key) | Read setting (no callback) Overloaded with route! |
app.enable(key) | Set boolean true |
app.disable(key) | Set boolean false |
app.enabled(key) | Read boolean |
Note: app.get(key) reads a setting if called with one arg, or registers a GET route if called with two args (path + handler).