Capstone: A REST API
A capstone is a project that ties everything together — here, a complete tasks REST API that combines config, a SQLite database, Express routes, middleware, and a test into one small, well-structured application.
Learn Capstone: A REST API in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll have a project laid out across db.js , app.js , and server.js , with CRUD endpoints, a logger and error handler, environment-driven config, and a test — the shape of a real Node back-end.
What You'll Build in This Capstone
1️⃣ The Data Layer (db.js)
Start with the database. db.js opens a SQLite file whose path comes from the environment , creates the tasks table, and exposes a handful of small, focused query functions that use ? placeholders. Keeping data access here means routes never write raw SQL.
2️⃣ The HTTP Layer (app.js)
Next, the Express app. app.js wires up JSON parsing, a request logger, the CRUD routes (each calling a db.js helper), and a catch-all error handler. Crucially it exports the app without calling listen() — so tests can import it.
3️⃣ Starting Up and Testing
Finally, server.js imports the app and starts listening on a port from the environment, and a small test file checks the data layer. Separating "define the app" from "start the server" is what makes the whole thing testable.
Your turn. Fill in the two ___ blanks to add an "update" route that marks a task done, then run it.
Extend the capstone with a query-param filter, a /health route, a second test, and a .env file. Run it with node --env-file=.env server.js .
📋 Quick Reference — Project Layout
Congratulations — you've finished the entire Node.js course!
You started by running your very first console.log outside the browser, and you've finished by building a complete, tested REST API. Along the way you mastered modules and npm, the event loop and async/await, the file system, HTTP servers, Express routing and middleware, a real database, environment config, and the built-in test runner.
🚀 What's next? Rebuild this API from scratch without looking, then add authentication, pagination, and a deploy. You now have every tool a Node back-end developer needs — go build and ship something real.
Practice quiz
Why does app.js export the Express app without calling listen()?
- Because listen() does not exist in Express
- To make the app run faster
- So tests can import the app without starting a real server
- Because Express forbids it
Answer: So tests can import the app without starting a real server. Keeping app.listen() out of app.js means tests can import the app and exercise routes without opening a port.
Which file actually starts the server listening on a port?
- server.js
- app.js
- db.js
- tasks.test.js
Answer: server.js. server.js imports the app and calls app.listen(), separating 'define the app' from 'start the server'.
What is the job of db.js in this capstone?
- Start the HTTP server
- Parse JSON request bodies
- Generate JWTs
- The data layer: open the DB and expose small query helpers
Answer: The data layer: open the DB and expose small query helpers. db.js opens the database and exports small, testable query functions, keeping data access in one module.
Where does the database file path come from?
- A hard-coded absolute path
- process.env.DB_FILE with a default
- A command-line prompt
- It is random each run
Answer: process.env.DB_FILE with a default. The path is read from the environment, process.env.DB_FILE ?? 'tasks.db', applying the config lesson.
What status code does a successful POST /tasks return?
- 201 Created
- 200 OK
- 204 No Content
- 404 Not Found
Answer: 201 Created. Creating a resource returns 201 with the new resource in the body.
What does GET /tasks/:id return when the task is missing?
- 200 with null
- 500 server error
- 404 with a Not found error
- An empty 204
Answer: 404 with a Not found error. If queries.get returns nothing, the route responds 404 with { error: 'Not found' }.
How many arguments does the catch-all Express error handler take?
- Two: req, res
- Four: err, req, res, next
- Three: req, res, next
- One: err
Answer: Four: err, req, res, next. Express recognizes an error-handling middleware by its four parameters (err, req, res, next).
What status code does a successful DELETE return here?
- 200 OK
- 201 Created
- 202 Accepted
- 204 No Content
Answer: 204 No Content. The delete route ends with res.status(204).end() to signal success with no body.
Why use ? placeholders in the SQL queries?
- To make queries shorter
- To safely bind values and avoid SQL injection
- Because SQLite requires them
- To speed up the server start
Answer: To safely bind values and avoid SQL injection. Parameterized ? placeholders bind values safely instead of concatenating user input into SQL.
Which middleware parses incoming JSON request bodies?
- express.static()
- express.urlencoded() only
- express.json()
- body-parser.raw()
Answer: express.json(). app.use(express.json()) parses JSON bodies so req.body is populated for POST routes.