Skip to content

Latest commit

 

History

58 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

API Backend Practice Lab

License: MIT

A LeetCode/Codeforces-style practice repository for backend API development — problems first, framework implementations second.

Contributions welcome — see CONTRIBUTING.md.

Why organize by problem, not by framework?

If this repo were organized as spring-boot/, go/, fastapi/, nestjs/, cpp/ at the top level, each framework folder would drift into its own vocabulary, its own endpoint shapes, and its own idea of what "done" means. Comparing two implementations of the same idea would mean jumping between unrelated trees and reverse-engineering whether they actually solve the same problem.

Organizing by problem instead means:

  • One spec, many implementations. Each problem has a single canonical description, endpoint contract, and OpenAPI spec. Every framework implementation is judged against the same contract, so implementations are genuinely comparable (same routes, same status codes, same payloads).
  • Practice mirrors how the skill is used. You don't decide "today I do Go" — you decide "today I implement JWT auth", and then pick a framework. That matches how real interview/system-design practice works.
  • Natural progression tracking. You can see at a glance which problems you've solved, in how many languages, and which concepts you've covered — like a LeetCode profile grid.
  • No forced parity. Not every problem needs every framework (see rules below). Framework-first structures implicitly pressure you to keep folders "complete"; problem-first structure makes partial coverage the default, expected state.
  • Easier to extend. Adding framework #6 means adding one new implementations/<framework>/ folder per problem you choose to redo — it never requires restructuring anything that exists.

Repository Layout

api-backend/
├── README.md                      # you are here
├── docs/                          # cross-cutting practice guidelines
│   ├── difficulty-levels.md
│   ├── concepts-glossary.md
│   ├── api-spec-guidelines.md
│   └── progress-tracker.md
├── templates/
│   ├── problem-README-template.md # copy this to scaffold a new problem
│   ├── openapi-template.yaml
│   └── frameworks/                # per-framework starter scaffolds
│       ├── spring-boot/scaffold/
│       ├── go/scaffold/
│       ├── fastapi/scaffold/
│       ├── nestjs/scaffold/
│       └── cpp/scaffold/
├── scripts/
│   └── new_problem.sh             # scaffolds a new problems/NNNN-slug/ dir
└── problems/
    ├── 0001-crud-todo-api/
    │   ├── README.md               # spec: requirements, endpoints, schema, tests
    │   ├── openapi.yaml
    │   └── implementations/
    │       ├── spring-boot/        # independent, self-contained project
    │       ├── go/
    │       ├── fastapi/
    │       ├── nestjs/
    │       └── cpp/
    ├── 0002-jwt-auth-service/
    ├── 0003-paginated-product-catalog/
    ├── 0004-file-upload-service/
    └── 0005-rate-limited-url-shortener/

Numbering convention

Problems are numbered LeetCode-style: NNNN-kebab-case-slug, e.g. 0001-crud-todo-api. Numbers are assigned once and never reused or renumbered, even if a problem is later deprecated — this keeps external references (notes, commits, branches) stable.

Rules

  1. Do not force every problem into every framework. Only implement a problem in a framework when you actually want the practice rep.
  2. Some problems may have several implementations in the same framework over time (e.g. a v2 exploring a different pattern) — put those in implementations/<framework>-v2/ rather than overwriting.
  3. Each framework implementation is fully independent: its own dependency manifest, own Dockerfile, own tests, own README with run instructions. Nothing is shared at runtime between implementations.
  4. The problem's openapi.yaml is the contract. Every implementation of that problem should conform to it so responses are diffable across frameworks with the same Postman/curl script.
  5. Every implementation ships tests. Unit and/or integration, using whatever is idiomatic for that framework (JUnit, go test, pytest, Jest, Catch2/GoogleTest).
  6. Docker is encouraged, especially for problems needing a database, cache, or message broker — a docker-compose.yml per implementation should bring up the service plus its infra dependencies.

Initializing a framework implementation

Each templates/frameworks/<name>/scaffold/ now contains a working, copy-ready starter project — not just instructions. Go, FastAPI, and Spring Boot scaffolds are locally build/test/run verified (compiles, all tests pass, live server responds correctly). The NestJS scaffold is likewise fully verified (build + unit tests + e2e tests + live server). The C++ scaffold follows the same conventions but is not locally build-verified (no C++ toolchain was available when authoring it) — build it yourself and expect to fix minor issues.

Each templates/frameworks/<name>/README.md documents both the fast path (copy the scaffold) and a from-scratch path (exact init commands/CLI flags), plus a Pre-implementation checklist (health-check route, DB/cache wiring, test harness, Swagger/OpenAPI wiring) to run through before writing real business logic:

How to use openapi.yaml

Each problem's openapi.yaml is the canonical contract every framework implementation must conform to. You can use it before, during, and instead of writing real backend code:

1. Preview it as readable docs (Swagger/Redoc UI)

npx @redocly/cli preview-docs problems/0001-crud-todo-api/openapi.yaml

Or paste the file into https://editor.swagger.io for a zero-install look.

2. Lint/validate it before implementing against it

npx @redocly/cli lint problems/0001-crud-todo-api/openapi.yaml

Catches malformed specs early. Redocly's default ruleset is stricter than bare OpenAPI validity (it'll also flag missing operationIds, missing security schemes, etc.) — those are style nits, not blockers.

3. Mock a fake server from it — test clients before any backend exists

npx @stoplight/prism-cli mock problems/0001-crud-todo-api/openapi.yaml
# serves on http://127.0.0.1:4010, validates every request/response against the spec

Useful for building a Postman collection, a frontend, or integration tests against the contract while you haven't written (or are mid-writing) the real implementation. Prism logs whether each request passed spec validation, catching contract mismatches immediately.

4. Cross-check a real implementation's generated spec against the canonical one

  • FastAPI auto-generates /openapi.json from your Pydantic models — diff it against the problem's openapi.yaml.
  • Spring Boot + springdoc-openapi serves /swagger-ui.html the same way.
  • Postman: File → Import → point at the openapi.yaml path to get every endpoint as a ready-made request collection instantly.

See docs/api-spec-guidelines.md for the conventions (error envelope, pagination shape, ID/timestamp formats) every spec follows.

Using an AI agent to add new problems

This repo ships an AGENTS.md — instructions for AI coding agents (Claude Code, Codex, Copilot, etc.) that work in this repository. Agents that support AGENTS.md auto-load it as project context; if yours doesn't, paste it into the prompt or point the agent at the file.

Use it to generate new problems without doing the spec-writing by hand:

"Add a random new practice problem to this repo."
"Add problem 0006 about webhook delivery with retries."
"Generate the next 3 problems, favoring async/messaging concepts."

The agent will: pick the next free problem number, choose or use your topic, run scripts/new_problem.sh, fully write the README (all 9 required sections + stretch goals) and openapi.yaml, validate the spec, update docs/progress-tracker.md, and commit — following the exact conventions in this README and docs/. It does not implement any solution code; it only authors the problem spec, same as problems 0001-0005.

How to add a new problem

./scripts/new_problem.sh 0006 websocket-chat-room

This scaffolds problems/0006-websocket-chat-room/ from templates/problem-README-template.md and an empty implementations/ dir. Fill in the README, add an openapi.yaml if the problem is REST-based, then implement it in whichever framework(s) you want practice in — copying the matching starter from templates/frameworks/<name>/scaffold/.

How to add a new framework

  1. Create templates/frameworks/<name>/scaffold/ with a minimal idiomatic starter (hello-world endpoint, test setup, Dockerfile, README on how to run it).
  2. From then on, any problem can grow an implementations/<name>/ folder by copying that scaffold.
  3. Document the framework's expected structure in templates/frameworks/<name>/README.md (see existing ones for the pattern) so every implementation stays idiomatic and consistent.

Tooling defaults

Stack Version
Java / Spring Boot Java 21 (LTS), Spring Boot 3.3.x
Go 1.22+
Python / FastAPI Python 3.11+
Node / NestJS Node 20 LTS, Nest 10.x
C++ C++20

Progress tracking

See docs/progress-tracker.md for a checklist grid of problems × frameworks.

About

A LeetCode/Codeforces-style practice repo for backend API development across Spring Boot, Go, FastAPI, NestJS, and C++ — organized by problem, not framework.

Topics

Resources

Contributing

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages