Every previous lesson introduced one capability in isolation. This chapter assembles them into TaskFlow, a multi-user task management API with projects, tasks, attachments and real-time updates. After this lesson you will have a reference architecture to clone for your own products and a checklist for judging whether an Express API is ready to ship.
| Area | Requirement | Lessons applied | | --- | --- | --- | | Accounts | Register, log in, refresh JWT, Google sign-in | Sessions and JWT, Passport | | Projects and tasks | CRUD, members, ownership rules, admin role | MVC, Mongoose, Authorization | | Lists | Pagination, filters by status and assignee, sorting, search | Pagination | | Attachments | Image upload per task, size and type limits | Multer | | Real time | Task changes pushed to project members | Socket.IO | | Quality | Validation, consistent errors, structured logs, tests, docs | Zod, Errors, Pino, Jest, Swagger | | Operations | Config, security headers, rate limits, Redis cache, Docker, Nginx | Config, Security, Redis, Docker |
taskflow-api/
src/
app.js # composition root (shown above)
server.js # DB connect, Socket.IO, listen, graceful shutdown
config/index.js # validated environment
lib/{logger,redis}.js
routes/index.js # mounts auth, projects, tasks, uploads
modules/
auth/ {auth.routes,auth.controller,auth.service,auth.schema}.js
projects/ {project.routes,project.controller,project.service,project.model}.js
tasks/ {task.routes,task.controller,task.service,task.model,task.schema}.js
middleware/ {auth,authorize,validate,upload,cache,error-handler}.js
docs/openapi.yaml
tests/ # supertest suites per module + tests/setup.js
Dockerfile compose.yaml nginx/default.conf .env.exampleGrouping by feature keeps a task's routes, controller, service and schema together; the layer discipline from the MVC lesson still applies inside each folder.
The task routes compose authentication, authorization, validation, caching and upload handling in one readable line per endpoint:
// src/modules/tasks/task.routes.js
import { Router } from "express";
import { requireAuth } from "../../middleware/auth.js";
import { loadProjectMember } from "../../middleware/authorize.js";
import { validate } from "../../middleware/validate.js";
import { cacheResponse } from "../../middleware/cache.js";
import { upload } from "../../middleware/upload.js";
import { createTaskSchema, updateTaskSchema, listTasksSchema } from "./task.schema.js";
import * as c from "./task.controller.js";
const router = Router({ mergeParams: true }); // mounted at /projects/:projectId/tasks
router.use(requireAuth, loadProjectMember); // sets req.user and req.project
router.get("/", validate(listTasksSchema), cacheResponse(30), c.list);
router.post("/", validate(createTaskSchema), c.create);
router.patch("/:id", validate(updateTaskSchema), c.update);
router.post("/:id/attachment", upload.single("file"), c.attach);
router.delete("/:id", c.remove);
export default router;// src/modules/tasks/task.controller.js
import * as tasks from "./task.service.js";
export async function create(req, res) {
const task = await tasks.create(req.validated.body, req.project.id, req.user.id);
req.app.get("io").to(`project:${req.project.id}`).emit("task:created", task);
res.status(201).json({ data: task });
}The controller is four lines because every other concern lives elsewhere: the schema validated the body, loadProjectMember proved access, the service enforces rules and clears the Redis cache, and the error middleware handles anything thrown.
config module throws on missing variables; .env.example is committed, .env is not.helmet, CORS allowlist, general and login rate limits, body size limit, trust proxy.httpOnly cookies for sessions.lean() reads, capped limit.AppError for operational failures, one error handler, no stack traces in production, a 404 route.pino-http with request IDs, a /health endpoint, errors logged under the err key./docs.SIGTERM handling.Why is the task controller only a few lines long?
app.js.Next lesson: What to learn next — you have completed the Express.js course; continue with the MongoDB course for aggregation and indexing, the REST APIs course for API design depth, or the Docker course to run this project in production with confidence.