This course teaches you how to build a backend service by focusing on what it does, rather than how it works under the hood.
We will use Test-Driven Development (TDD) to build a URL shortener API. This means writing a failing test before writing any production code. Our tech stack includes TypeScript, Fastify, PostgreSQL, Jest, and Docker.
By the end of the course, you will know how to take a feature from a failing test all the way to production-ready code without losing control of your system.
Early in my career, I constantly heard that tests mattered. But on real projects, they were usually the first thing cut when deadlines got tight. That gap between what teams say and what they actually do is a big reason I made this course.
Node.js is a great place to learn TDD because the feedback loop is incredibly short:
This speed makes the Red → Green → Refactor cycle a physical habit rather than just a theory.
The course stays highly practical. We build one service from start to finish, keep the scope clear, and introduce real problems only when we actually need to solve them.
A quick warning: TDD does feel slower at first. You will probably wonder if writing the test first is really worth the effort. That is completely normal. The goal here is to push through that awkward phase through repeated practice on a single project.
This course is for engineers who know backend basics and want a better testing workflow.
You do not need prior Fastify or PostgreSQL experience, but you should already be comfortable with:
async / awaitclone, , and checkoutcommitIf you are brand new to Node.js, this material will move a bit too fast. But if you can already read backend code and want a more disciplined way to write it, you are in the right place.
All source code lives on GitHub:
Most hands-on chapters have two branches:
| Branch | What it is |
|---|---|
xx-chapter-name-start | The starting point, before we write the chapter's code |
xx-chapter-name-finish | The completed state, after we finish the chapter |
For the best experience, start from the start branch and write the code yourself. Use the finish branch only to compare your work or to recover if you get stuck.
A few notes on how the repository is organized:
01-what-is-tdd-start.main branch holds the final version of the project.We are building a URL shortener API. This is a service that:
The idea is simple, which is exactly what makes it a great TDD project. The behavior is easy to describe, but building it still leaves plenty of room to practice real backend work:
We will start with a tiny example in plain Node.js so the TDD loop is easy to see. Then we move into the real project: a Fastify API in TypeScript, backed by PostgreSQL.
The progression looks like this:
The project stays understandable from start to finish. We add complexity in layers, and the test suite tells us if each step was safe.
We will not write code first and bolt tests on later. The test comes first whenever the behavior is clear enough to describe.
Expect to see:
This is exactly how the process is supposed to work. In a production environment, this approach gives you much more than just test coverage. It gives you fast feedback. When a change breaks your app, you know immediately. When a refactor is safe, you know immediately.
Next, we will make TDD concrete with a tiny Node.js example before moving into the main Fastify application.