Capstone topics from the end of the program: how to set up a full-stack backend project, work with npm and Git, test APIs with Postman or curl, apply layering and REST habits, and prepare for deployment and the mock interview. These patterns tie Express, SQL, and deployment lessons into day-to-day engineering practice.

Misc / Other

Capstone topics from the end of the program: how to set up a full-stack backend project, work with npm and Git, test APIs with Postman or curl, apply layering and REST habits, and prepare for deployment and the mock interview. These patterns tie Express, SQL, and deployment lessons into day-to-day engineering practice.

Select a function type from the sidebar, or browse topics below.

How to organize a Node/Express project — Basic

Separate app entry, routers, controllers, services, and database code into predictable folders.
Learn: A consistent folder layout helps you find code quickly and mirrors how professional teams organize Express APIs. Each resource gets router + controller + service files; shared pieces (db, errors, config) live at the top level.

Supplementary examples

Adding a new resource (tags)

src/tags/
  tags.router.js      # URLs only
  tags.controller.js  # req/res
  tags.service.js     # Knex queries

Course example

src/
  app.js                 # Express app + global middleware
  server.js              # listen(PORT)
  config/                # logger, env helpers
  errors/                # notFound, errorHandler
  db/                    # knex connection, migrations, seeds
  users/
    users.router.js
    users.controller.js
    users.service.js

Additional references & examples

How to use npm scripts in package.json — Basic

Standard scripts to start the server, run migrations, and seed the database.
Learn: npm scripts are the team’s shared commands—everyone runs the same start and migrate steps. Document them in README so onboarding takes minutes, not hours.

Supplementary examples

Run migrations before start in dev

# package.json
"scripts": {
  "dev:reset": "knex migrate:rollback --all && knex migrate:latest && knex seed:run && nodemon src/server.js"
}

Course example

{
  "scripts": {
    "start": "node src/server.js",
    "dev": "nodemon src/server.js",
    "migrate": "knex migrate:latest",
    "seed": "knex seed:run"
  }
}

Additional references & examples

How to work from a starter repository — Basic

Clone the program starter, install dependencies, copy .env.example, and run locally before customizing.
Learn: Starters encode conventions the course expects. Clone, install, configure .env, migrate, and seed before changing behavior—prove the baseline works, then implement features incrementally.

Supplementary examples

.env.example fields (typical)

DATABASE_URL=postgresql://user:pass@localhost:5432/dbname
PORT=5001
LOG_LEVEL=debug
NODE_ENV=development

Course example

git clone <starter-repo-url>
cd <project-folder>
npm install
cp .env.example .env   # then fill in DATABASE_URL, PORT, etc.
npm run migrate
npm run seed
npm start

Additional references & examples

How to stage and commit backend changes — Basic

Use Git to save incremental work; commit working slices (feature, fix, migration) with clear messages.
Learn: Small, focused commits make code review and debugging easier. Commit when a vertical slice works (e.g. ‘users list endpoint returns 200’), not only at the end of the week.

Supplementary examples

Check what will be committed

git diff
git diff --staged
git commit -m "feat(users): add GET /users list endpoint"

Course example

git status
git add src/users/users.service.js
git commit -m "Add list users service function"
git push origin main

Additional references & examples

How to use NODE_ENV — Intermediate

Switch behavior between development (verbose logs, pretty output) and production (structured logs, no secrets in responses).
Learn: NODE_ENV signals runtime context. Development favors readable logs; production favors performance and security (no stack traces to clients, stricter log levels).

Supplementary examples

Branch config in knexfile

module.exports = {
  development: { connection: process.env.DATABASE_URL, ... },
  production: { connection: process.env.DATABASE_URL, pool: { min: 2, max: 10 }, ... },
};

Course example

// config/logger.js
const isDev = process.env.NODE_ENV !== "production";

module.exports = pino(
  isDev
    ? { transport: { target: "pino-pretty" } }
    : { level: process.env.LOG_LEVEL || "info" }
);

Additional references & examples

How to test APIs with Postman — Basic

Send GET/POST/PUT/DELETE requests with JSON bodies and headers; verify status codes and response shape.
Learn: Postman is the GUI you used throughout the program to exercise REST endpoints. Build a collection per resource with example bodies so regressions are easy to spot after refactors.

Supplementary examples

Headers for JSON APIs

Content-Type: application/json
Accept: application/json
# Optional when using cookies/sessions:
# Cookie: session=...

Course example

# Example workflow (Postman UI):
# 1. Method: POST  URL: http://localhost:5001/users
# 2. Body (raw JSON): { "username": "demo_user" }
# 3. Expect: 201 + JSON body with created user
# 4. Follow-up GET http://localhost:5001/users to list all

Additional references & examples

How to test APIs with curl — Basic

Quick command-line checks without a GUI—useful in terminals, CI, and remote servers.
Learn: curl is ideal for scripts, documentation, and quick smoke tests. Pair with jq or a JSON formatter when responses are large.

Supplementary examples

PUT with JSON body

curl -s -X PUT http://localhost:5001/users/1 \
  -H "Content-Type: application/json" \
  -d '{"username":"updated"}'

Course example

curl -s http://localhost:5001/health

curl -s -X POST http://localhost:5001/users \
  -H "Content-Type: application/json" \
  -d '{"username":"demo"}'

Additional references & examples

How to debug a failing request — Intermediate

Trace the path: client → middleware → router → controller → service → database; read logs and status codes at each step.
Learn: When a request fails, resist random changes—follow the pipeline. Confirm the server port, route match, validation, service/SQL, then global error handler. Logs with request IDs from pino make this faster.

Supplementary examples

Temporary route-level log

router.post("/", (req, res, next) => {
  console.log("POST /users body", req.body);
  return controller.create(req, res, next);
});

Course example

// Typical order to inspect:
// 1. Server running? Correct PORT in .env?
// 2. Request URL and HTTP method match a route?
// 3. Validation middleware rejecting body? (400)
// 4. Service/SQL error? (500 + logs)
// 5. notFound if no route matched (404)

req.log.info({ params: req.params, body: req.body }, "debug");

Additional references & examples

How to separate concerns across layers — Intermediate

Routers wire URLs, controllers handle HTTP, services own data—avoid SQL or business rules in route files.
Learn: Layers exist so each file has one reason to change. If SQL appears in a router, move it to a service. If HTTP status logic sprawls across services, return errors and map them in the controller.

Supplementary examples

Controller maps service error to 404

try {
  const row = await service.read(id);
  res.json({ data: row });
} catch (err) {
  next(err); // errorHandler reads err.status
}

Course example

// Good: router only connects URL to controller
router.route("/").get(controller.list).post(controller.create);

// Avoid: database logic inline in router
// router.get("/", async (req, res) => {
//   const rows = await knex("users").select();  // move to service
// });

Additional references & examples

HTTP status codes — when to use each — Reference

Quick reference for common API response codes used in the backend program: what each code means and which HTTP verb / situation it pairs with.
Learn: Status codes tell the client what happened without reading the body. In this program you most often use: 200 for successful reads/updates that return JSON, 201 when POST creates a row, 204 when DELETE succeeds with no body, 400 when validation fails, 404 when the id or route does not exist, and 500 when an unexpected error reaches errorHandler. Match the code to the HTTP verb and outcome—not every success is 200.

Supplementary examples

2xx — success responses

// 200 — GET /users, GET /users/3, PUT /users/3 (with JSON body)
res.status(200).json({ data: users });

// 201 — POST /users (resource created)
res.status(201).json({ data: newUser });

// 204 — DELETE /users/3 (nothing to return)
res.status(204).end();

4xx — client / request problems

// 400 — validation middleware or controller check
return res.status(400).json({ error: "username is required" });

// 404 — service found no row, or notFound middleware
const err = new Error("User not found");
err.status = 404;
throw err;

5xx — server failures

// 500 — default in errorHandler when err.status is missing
// Avoid sending 500 for validation (use 400) or missing rows (use 404)

function errorHandler(err, req, res, next) {
  const status = err.status || err.statusCode || 500;
  res.status(status).json({ error: err.message });
}

Verb → status cheat sheet (typical REST API)

GET    /resources      200  (list)
GET    /resources/:id  200  (found)  | 404 (missing)
POST   /resources      201  (created)
PUT    /resources/:id  200  (updated) | 404 (missing)
DELETE /resources/:id  204  (deleted) | 404 (missing)

Common codes (program reference)

CodeNameWhen to use in your API
200OKGET success; PUT success when returning updated resource
201CreatedPOST created a new resource (include body or Location header)
204No ContentDELETE success; no response body (use .end())
400Bad RequestValidation failed, missing/invalid fields, bad JSON
404Not FoundUnknown route (notFound) or id not in database
500Internal Server ErrorUnhandled exception; default in errorHandler

Course example

# 2xx Success
200 OK           GET list/detail, PUT update (return body)
201 Created      POST created a new resource
204 No Content   DELETE succeeded (no response body)

# 4xx Client error (caller or request is wrong)
400 Bad Request  Validation failed, malformed JSON, missing field
404 Not Found    Resource id does not exist; unknown route (notFound)

# 5xx Server error (bug, database, unhandled exception)
500 Internal     Unhandled error; errorHandler default

Additional references & examples

How to follow REST conventions — Basic

Use nouns for resources, HTTP verbs for actions, and meaningful status codes (200, 201, 204, 400, 404, 500).
Learn: REST is a convention, not a framework feature. Collections use plural nouns; IDs live in the path; verbs express intent via HTTP methods—not via path names like /getUsers.

Supplementary examples

Nested resource path

GET /users/42/pastes     # pastes belonging to user 42
POST /users/42/pastes     # create paste for user 42

Course example

GET    /users       -> 200 list
POST   /users       -> 201 created
GET    /users/:id   -> 200 one | 404
PUT    /users/:id   -> 200 updated | 404
DELETE /users/:id   -> 204 no content | 404

Additional references & examples

How to return consistent API errors — Intermediate

Use JSON error objects and appropriate status codes; centralize formatting in error middleware.
Learn: Clients and frontends depend on predictable error JSON. Pick a shape (e.g. { error: string }) and use it for validation, not found, and server errors.

Supplementary examples

errorHandler shape

function errorHandler(err, req, res, next) {
  const status = err.status || 500;
  res.status(status).json({
    error: err.message,
    ...(process.env.NODE_ENV === "development" && { stack: err.stack }),
  });
}

Course example

// Validation
res.status(400).json({ error: "username is required" });

// Not found (in service or controller)
const err = new Error("User not found");
err.status = 404;
throw err;

// errorHandler sends: { error: err.message }

Additional references & examples

How to write readable backend code — Basic

Use descriptive names, small functions, early returns, and comments only for non-obvious business rules.
Learn: Readable code reduces bugs in team settings. Prefer const, async/await over nested callbacks, and functions under ~30 lines. Rename until intent is obvious without comments.

Supplementary examples

Extract validation helper

function requireString(value, field) {
  if (typeof value !== "string" || !value.trim()) {
    const err = new Error(`${field} is required`);
    err.status = 400;
    throw err;
  }
  return value.trim();
}

Course example

// Prefer explicit names
async function listActiveUsers() {
  return knex("users").where({ is_active: true });
}

// Prefer early return over deep nesting
if (!req.body.name) {
  return res.status(400).json({ error: "name is required" });
}

Additional references & examples

How to prepare for the backend mock interview — Basic

Review REST, Express layers, Knex CRUD, error handling; practice explaining decisions aloud; use official docs for syntax only during the assessment.
Learn: The mock interview combines live coding (Qualified) with verbal explanation. Practice narrating your structure out loud; passing tests is necessary but interviewers also assess how you think through tradeoffs.

Supplementary examples

Explain-aloud template

# 1. Restate the endpoint requirement
# 2. Name files you will touch (router, controller, service)
# 3. Describe data model / migration if needed
# 4. Implement smallest test-passing slice
# 5. Mention edge cases (404, 400, empty list)

Course example

# Preparation checklist
# - All Qualified challenge tests pass
# - Can explain router vs controller vs service
# - Can write a migration + Knex query from scratch
# - Know when to use 201 vs 204 vs 404
# - Behavioral stories ready (STAR format)

Additional references & examples

How to use AI tools responsibly in backend work — Basic

Appropriate uses: brainstorming structure, explaining errors, documentation lookup. Verify output; disclose use when required; do not substitute for understanding in graded interviews.
Learn: AI can accelerate learning when you verify suggestions against docs and run tests. For graded interviews, follow program rules: official docs for syntax, honest disclosure if you used AI while practicing.

Supplementary examples

Good prompt for learning

# "Explain why this Knex query returns undefined when no row exists"
# Then fix it yourself using .first() and an explicit 404 check.

Course example

# Responsible uses
# - Explain an error message or stack trace
# - Suggest test cases for an endpoint
# - Draft commit message or PR description

# Avoid for graded work unless allowed
# - Generating full solutions without understanding
# - Copying code you cannot explain in the interview

Additional references & examples

How to deploy a Node API (high level) — Intermediate

Push to GitHub, connect host (e.g. Render), set env vars, run migrations, confirm health endpoint and CORS for the frontend origin.
Learn: Deployment connects Git, hosting, database, and frontend CORS. The same repo runs locally with .env and in production with host-managed secrets—never commit real credentials.

Supplementary examples

Production env on Render (typical)

NODE_ENV=production
DATABASE_URL=<render-postgres-url>
PORT=10000
FRONTEND_URL=https://your-app.onrender.com

Course example

# Deployment checklist
# 1. .env values set on host (DATABASE_URL, PORT, NODE_ENV=production)
# 2. Build command: npm install
# 3. Start command: npm start
# 4. Run knex migrate:latest against production DB
# 5. Smoke test: GET /health or GET /

Additional references & examples