Proof of Concept for a RESTful API made with Node.js LTS/Krypton (v24) and Express.js 5 in TypeScript. Manage football player data with SQLite, Sequelize ORM, Swagger documentation, and in-memory caching.
- Features
- Tech Stack
- Project Structure
- Architecture
- API Reference
- Prerequisites
- Quick Start
- Testing
- Containers
- Releases
- Environment Variables
- Command Summary
- Contributing
- Legal
- 🏗️ Modern TypeScript architecture - Native ESM, strict mode, layered architecture with interface-based contracts
- 📚 Interactive API exploration - Auto-generated OpenAPI docs with Swagger UI and
.restHTTP file for VS Code REST Client - ⚡ Performance optimizations - In-memory caching with node-cache, Sequelize ORM, and efficient SQLite operations
- 🧪 Comprehensive integration tests - Full endpoint coverage with Jest/Supertest and automated reporting to Codecov
- 📖 Token-efficient documentation - Auto-loaded Copilot instructions for AI-assisted development
- 🐳 Full containerization - Multi-stage Docker builds with Docker Compose orchestration
- 🔄 Complete CI/CD pipeline - Automated linting (ESLint/Prettier), testing, Docker publishing, and GitHub releases
- ⚽ Football-themed semantic versioning - Memorable, alphabetical release names using football terminology
| Category | Technology |
|---|---|
| Runtime | Node.js 24 (LTS/Krypton) |
| Language | TypeScript 5.9 |
| Module System | Native ECMAScript Modules (ESM) - uses tsx for execution |
| Framework | Express.js 5 |
| Database | SQLite3 with Sequelize ORM |
| Caching | node-cache |
| Documentation | Swagger (OpenAPI 3.0) |
| Security | Helmet, CORS, express-rate-limit |
| Testing | Jest 30 with Supertest |
| Containerization | Docker with multi-stage builds |
| Code Quality | ESLint, Prettier, Commitlint |
| Dev Tools | tsx (TypeScript executor), nodemon |
💡 Note: While the repository name references
ts-node(the original implementation), the project now uses tsx for faster, cleaner TypeScript execution without experimental flags.
src/
├── app.ts # Express app setup & middleware configuration
├── server.ts # HTTP server initialization & lifecycle
├── controllers/ # Request handlers with Swagger annotations
├── services/ # Business logic + caching layer
├── database/ # Sequelize DB access (interfaces + implementations)
├── models/ # Sequelize models (Player)
├── routes/ # Express Router definitions
├── docs/ # Swagger configuration & doc generation
├── middlewares/ # Custom middleware (rate limiter, validators, Swagger CSP)
└── utils/ # Pino logger configuration
rest/ # HTTP request files for VS Code REST Client
tests/ # Integration tests with supertest
scripts/ # Docker entrypoint & healthcheck scripts
storage/ # Pre-seeded SQLite database
Layered architecture with dependency injection via constructors and interface-based contracts.
%%{init: {
"theme": "default",
"themeVariables": {
"fontFamily": "Fira Code, Consolas, monospace",
"textColor": "#555",
"lineColor": "#555",
"lineWidth": 2,
"clusterBkg": "#f5f5f5",
"clusterBorder": "#999"
}
}}%%
graph RL
tests[tests]
subgraph Layer 1[" "]
server[server]
app[app]
end
subgraph Layer 2[" "]
routes[routes]
controllers[controllers]
Express[Express]
end
subgraph Layer 3[" "]
services[services]
nodeCache[node-cache]
end
subgraph Layer 4[" "]
database[database]
Sequelize[Sequelize]
end
models[models]
%% Dependencies
app --> server
routes --> app
controllers --> routes
services --> controllers
database --> services
Express --> routes
nodeCache --> services
Sequelize --> database
Express --> app
Express -.-> controllers
Sequelize -.-> models
app -.-> tests
models -.-> database
models -.-> services
models -.-> controllers
controllers --> app
services --> app
database --> app
%% Styling
classDef core fill:#b3d9ff,stroke:#6db1ff,stroke-width:2px,color:#555,font-family:monospace;
classDef deps fill:#ffcccc,stroke:#ff8f8f,stroke-width:2px,color:#555,font-family:monospace;
classDef test fill:#ccffcc,stroke:#53c45e,stroke-width:2px,color:#555,font-family:monospace;
class server,app,routes,controllers,services,database,models core
class Express,Sequelize,nodeCache deps
class tests test
Arrows follow the wiring direction: A --> B means A is provided to B. Solid arrows (-->) represent active dependencies — modules explicitly wired in app and invoked at runtime. Dotted arrows (-.->) represent structural dependencies — the consumer references types or interfaces without invoking runtime behavior.
app is the composition root: it instantiates all dependencies, configures Express middleware, and registers all routes. server is separate and owns only the HTTP lifecycle (port binding, graceful shutdown) — a conventional split in Express projects.
Four layers: Initialization (server, app), HTTP (routes, controllers), Business (services), and Data (database).
models is a cross-cutting type concern — shared types and Sequelize model definitions consumed across multiple layers, with no business logic of its own.
Blue = core application packages, red = third-party frameworks, green = tests.
Simplified, conceptual project structure and main application flow. Not all dependencies are shown.
Interactive API documentation is available via Swagger UI at http://localhost:9000/swagger/ when the server is running.
Quick Reference:
GET /players- List all playersGET /players/:id- Get player by IDGET /players/squadNumber/:squadNumber- Get player by squad numberPOST /players- Create new playerPUT /players/:id- Update playerDELETE /players/:id- Remove playerGET /health- Health check
For complete endpoint documentation with request/response schemas, explore the interactive Swagger UI. You can also access the OpenAPI JSON specification at http://localhost:9000/swagger.json.
Alternatively, use rest/players.rest with the REST Client extension for VS Code to send requests directly from the editor.
Before you begin, ensure you have the following installed:
- Node.js (see
.nvmrcfor required version) - npm (comes with Node.js)
- direnv (optional, but recommended — auto-loads the correct Node.js version via
.nvmrcon directory entry) - Docker and Docker Compose (optional, for containerized setup)
git clone https://github.com/nanotaboada/ts-node-samples-express-restful.git
cd ts-node-samples-express-restfulnpm installnpm run devThe server will start on http://localhost:9000 with the following output:
> ts-node-samples-express-restful@1.0.0 dev
> nodemon
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): src/**/*
[nodemon] watching extensions: ts
[nodemon] starting `tsx ./src/server.ts`
🚀 Running at http://localhost:9000- API:
http://localhost:9000 - Swagger Documentation:
http://localhost:9000/swagger/ - Health Check:
http://localhost:9000/health
Run the test suite with Jest:
# Run all tests
npm test
# Run tests with coverage report
npm run coverage
# Run linter
npm run lint
# Validate commit message format
npm run lint:commitTests are located in the tests/ directory and use Supertest for integration testing. Coverage reports are generated for controllers, services, and routes only.
This project includes full Docker support with multi-stage builds and Docker Compose for easy deployment.
npm run docker:build
# or
docker compose buildnpm run docker:up
# or
docker compose up💡 Note: On first run, the container copies a pre-seeded SQLite database into a persistent volume. On subsequent runs, that volume is reused and the data is preserved.
npm run docker:down
# or
docker compose downTo remove the volume and reinitialize the database from the built-in seed file:
docker compose down -vThe containerized application runs on port 9000 and includes health checks that monitor the /health endpoint every 30 seconds.
This project uses football terminology as release names ⚽
Releases follow the pattern: v{SEMVER}-{TERM} (e.g., v1.0.0-assist)
- Semantic Version: Standard versioning (MAJOR.MINOR.PATCH)
- Term Name: Alphabetically ordered codename from the football terminology list
To create a new release, follow this workflow:
Branch protection prevents direct pushes to master, so all release prep goes through a PR:
git checkout master && git pull
git checkout -b release/vX.Y.Z-termMove items from [Unreleased] to a new release section in CHANGELOG.md, then commit and push the branch:
# Move items from [Unreleased] to new release section
# Example: [2.0.0 - corner] - 2026-03-29
git add CHANGELOG.md
git commit -m "chore(release): vX.Y.Z-term"
git push origin release/vX.Y.Z-termOpen a pull request from release/vX.Y.Z-term into master and merge it. The tag must be created after the merge so it points to the correct commit on master.
After the PR is merged, pull master and create the annotated tag:
git checkout master && git pull
git tag -a vX.Y.Z-term -m "Release X.Y.Z - Term"
git push origin vX.Y.Z-termExample:
git tag -a v2.0.0-corner -m "Release 2.0.0 - Corner"
git push origin v2.0.0-cornerPushing the tag triggers the CD pipeline which automatically:
- Builds and tests the project
- Publishes Docker images to GitHub Container Registry
- Creates a GitHub Release with auto-generated changelog from commits
- Release branch created from
master -
CHANGELOG.mdupdated with release notes - Changes committed and pushed on the release branch
- Release PR merged into
master - Tag created with correct format:
vX.Y.Z-term - Term is valid (A-Z from the football terminology list)
- Tag pushed to trigger CD workflow
Each release publishes three Docker tags:
# By semantic version (recommended for production)
docker pull ghcr.io/nanotaboada/ts-node-samples-express-restful:1.0.0
# By term name (memorable, useful for staging)
docker pull ghcr.io/nanotaboada/ts-node-samples-express-restful:assist
# Latest (development/testing only)
docker pull ghcr.io/nanotaboada/ts-node-samples-express-restful:latestCreate a .env file in the root directory to customize configuration:
# Server port (default: 9000)
PORT=9000
# Database storage path (default: storage/players-sqlite3.db)
# In Docker: /storage/players-sqlite3.db
STORAGE_PATH=storage/players-sqlite3.db
# Rate limiting (all optional — defaults shown)
RATE_LIMIT_ENABLED=true # Set to 'false' to disable rate limiting entirely
RATE_LIMIT_WINDOW_MS=60000 # Time window in milliseconds (default: 1 minute)
RATE_LIMIT_MAX_GENERAL=100 # Max requests per window for all routes
RATE_LIMIT_MAX_STRICT=20 # Max requests per window for POST/PUT/DELETE| Script | Description |
|---|---|
npm run dev |
Start development server with hot reload |
npm start |
Run compiled application from dist/ |
npm run build |
Compile TypeScript to JavaScript |
npm test |
Run Jest tests with --detectOpenHandles flag |
npm run coverage |
Generate test coverage report |
npm run lint |
Run ESLint on all files |
npm run lint:commit |
Validate last commit message format |
npm run swagger:docs |
Generate swagger.json from JSDoc annotations |
npm run docker:build |
Build Docker image |
npm run docker:up |
Start Docker container |
npm run docker:down |
Stop and remove Docker volume |
Contributions are welcome! Please read CONTRIBUTING.md for details on the code of conduct and the process for submitting pull requests.
Key guidelines:
- Follow Conventional Commits for commit messages
- Ensure all tests pass (
npm test) - Run linter before committing (
npm run lint) - Keep changes small and focused
This project is provided for educational and demonstration purposes and may be used in production at your own discretion. All trademarks, service marks, product names, company names, and logos referenced herein are the property of their respective owners and are used solely for identification or illustrative purposes.