Capstone: Build a Report Card Service
By Syncfusion®, Inc. · Last updated
A complete Node.js project in five steps: a command-line tool with parseArgs and an env file, streaming a marks file with readline and saving results safely, an EventEmitter-based report builder with custom errors, an HTTP API with health checks and graceful shutdown, and tests with node:test, followed by three extension exercises.
Capstone: Build a Report Card Service
Capstone = one project that uses what the whole course taught.
You will build Report Card Service for Greenfield School:
- A command-line tool that reads a marks file and builds report cards.
- A small web API that serves the reports.
- Tests that check it all.
Simple lesson: a real project is many small lessons joined together.
A Real-World Example
At the end of every term, the class teacher at Greenfield School:
- gets a marks sheet with one row per student and subject,
- adds up each student's marks and works out the grade,
- finds the topper and the students who need help,
- shares the results with parents,
- and checks that nothing was copied wrongly.
Your program will do the same job in less than a second.
What You Will Learn
In this lesson, you will:
- Plan the project as small modules
- Build the command line with
parseArgsand a.envfile - Stream the marks file with
readlineand save results safely - Report progress with events and catch bad rows with a custom error
- Serve reports over HTTP with health checks and graceful shutdown
- Test the grades, the builder and the server with
node:test - Extend the project in three exercises
The Plan
Diagram: marks.csv is streamed by the ReportBuilder, which uses grades.ts, emits events and writes report.json, which the CLI prints and the HTTP server serves, with tests checking both marks.csv ReportBuilderstreams, events, errors grades.ts report.json CLI HTTP API tests (node:test)
Data flows from the marks file through the builder into a saved report, which the CLI and the API share.
The files:
src/grades.ts= small pure functions: total, average, grade. Easy to test.src/report.ts= theReportBuilder: reads the CSV as a stream, emits events, throwsBadRowError.src/config.ts= reads and checks settings.cli.ts= the command-line tool.server.ts= the HTTP API.test/*.test.ts= the tests.
Build one step at a time, and run it after every change.
Simple lesson: keep pure logic, input and output, and the entry points in separate modules.
Step 1: Command Line and Config
parseArgsreads--class,--termand--help(see Process, OS and CLI Tools).--env-file=.envgivesPASS_MARKandSCHOOL_NAME.loadConfigchecks the settings and fails fast.
Simple lesson: read options and settings once, check them, and stop early if anything is wrong.
Step 2: Stream the Marks and Save Safely
- The marks file can be big, so read it line by line with
readline(see Buffers, Streams and Compression). grades.tsholds tiny pure functions.- The result is saved with write-temp-then-rename (see Path and File System).
Simple lesson: stream the input, keep the maths in pure functions, and save with a rename.
Step 3: Events and Custom Errors
- The
ReportBuilderextendsEventEmitter(see Events and Utilities). - It emits
studentfor each finished card,bad-rowfor broken lines, anddoneat the end. - A broken line throws a
BadRowErrorwith a code and the line number (see Error Handling). - The CLI listens to the events, so the builder never prints anything itself.
The same ReportBuilder could feed the HTTP API, a test or an email job, because it does not print.
Simple lesson: the builder announces what happens, and the caller decides what to print.
Step 4: The HTTP API
GET /health= liveness for the platform (see Production Readiness).GET /reports/:class= the saved report as JSON, or 404.SIGTERM= close the server gracefully.
Simple lesson: a small API needs routes, JSON errors, a health check and a clean shutdown.
Step 5: Tests
- Pure functions are tested directly.
- The builder is tested with a temp file and its events.
- Run them all with
node --test(see Testing with the Built-in Test Runner).
Simple lesson: test the pure parts directly, and test the stream parts with temp files.
Running It on Your Own Computer
- Make a folder
report-cardand copy the files from the steps into it. - Run
node --env-file=.env cli.ts --class 8to build the report. - Run
node --testto check everything. - Run
node server.tsand openhttp://localhost:3000/reports/8. - Add
"bin"topackage.jsonand runnpm link, soreport-cardworks anywhere.
Running .ts files directly needs a recent Node.js version. On older versions, run the JavaScript files, or use npx tsx.
What You Used From Each Chapter
- Foundations: ES modules,
.tsfiles,package.jsonwithtypeandbin. - Asynchronous Node.js:
await, events, custom errors. - System and Files:
parseArgs, env files,fs/promises,readlinestreams, safe writes. - Networking: an HTTP API with JSON and 404s.
- Quality and Production: tests, health checks, graceful shutdown.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| One big file for everything | Small projects need no structure | Split pure logic, I/O and entry points | Hard to test and change |
| Printing inside the builder | Every part can log | Emit events, let the caller print | The builder cannot be reused in the API |
| Stopping at the first bad row | Bad data must stop everything | Skip it, report it and keep going | One typo blocks the whole class report |
Reading the whole CSV with readFile | Files are always small | Stream it with readline | Big files fill memory |
| No test for bad input | Happy paths are enough | Test broken rows too | Bugs hide in the edge cases |
TL;DR
- Plan = pure functions, a builder, config, CLI, server, tests.
- CLI =
parseArgs+--env-file+ fail-fast config. - Data =
readlinestream in, write-temp-then-rename out. - Builder =
EventEmitter+BadRowErrorwith a code and line. - API = routes, JSON errors,
/health, SIGTERM shutdown. - Tests =
node:testfor functions, temp files for streams.
Check Yourself
Exercises
What to Remember
- Split a project into pure logic, I/O, entry points and tests.
- Check options and settings at start-up.
- Stream input, report bad data with custom errors, and keep going.
- Emit events from reusable parts, and let callers decide what to show.
- Give every API a health check and a graceful shutdown.
- Test the pure parts directly and the I/O parts with temp files.
Next: Node.js Cheat Sheet. Keep it open while you build your own projects.