Understanding Express Application Lifecycle

1. Understanding Application Initialization

StageWhat happens
1. express()Creates app object (function + EventEmitter)
2. Settingsapp.set(), app.enable()
3. Middlewareapp.use() registers in order
4. Routesapp.get/post/...()
5. Error handlersLast (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.
PositionMiddleware
1Security (helmet, cors)
2Compression
3Request logging (morgan)
4Body parsers (json, urlencoded)
5Cookie/session parsers
6Authentication (passport)
7Static files
8Routers
9404 handler
10Error 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
        
TerminationTriggers
res.send/json/end/sendFile/render/redirectResponse 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

EventSourceUse
mountappSub-app mounted on parent
listeninghttp.ServerServer bound to port
errorhttp.ServerBind/runtime errors
connectionhttp.ServerNew TCP connection
closehttp.ServerServer 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);
});
PatternWhen to use
Lazy connectionConnect on first query (serverless)
Pre-warmConnect before listen (long-running servers)
Health gateDon't accept traffic until deps ready

6. Understanding Route Matching Order

RuleDetail
First match winsExpress tries routes in registration order
Specificity mattersDefine /users/me BEFORE /users/:id
Methods filterapp.get only matches GET
Mount path stripsapp.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;
ProbePurpose
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)
        
ConceptDetail
StackLinear array of {path, method, handler}
Cursornext() advances; cannot go back
BranchingRouters 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

APIDescription
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).