Opinionated TypeScript Fastify starter with Hexagonal Architecture, Vertical Slicing, Zod validation, Swagger, and integration tests.
This project is an opinionated TypeScript Fastify starter that applies Hexagonal Architecture with Vertical Slicing, includes a complete Todo module (create, list, update, delete), and ships with request validation, OpenAPI docs, consistent error handling, and integration tests. The Todo module currently uses in-memory persistence as an example implementation and should be replaced with a real database adapter in production scenarios.
Current features:
- Fastify HTTP server
- Swagger/OpenAPI JSON generation
- Swagger UI at
/docs - Versioned routes under
/api/v1 - Health check endpoint
- In-memory Todo flow (
create,list,update,delete) - Environment validation with Zod
- Consistent API error shape (
error.code,error.message,error.details) - Strict TypeScript configuration
- ESLint + Prettier
- Vitest integration tests for health and Todo endpoints
- Node.js
- TypeScript
- Fastify
- Zod
- Swagger (
@fastify/swaggerand@fastify/swagger-ui) - Vitest
- ESLint + Prettier
src/
index.ts
bootstrap/
index.ts
server.ts
shared/
core/
config.ts
errors.ts
infrastructure/
persistence/
database.ts
modules/
health/
http/
health.routes.ts
todo/
application/
ports/
todo.repository.ts
todo.service.ts
domain/
todo.entity.ts
http/
todo.routes.ts
infrastructure/
in-memory-todo.repository.ts
postgres-todo.repository.ts
todo.module.ts
bootstrapcontains application startup and server composition.sharedcontains cross-cutting concerns such as config and error handling.modulesgroups each feature vertically.- Each module separates
domain,application,infrastructure, andhttpresponsibilities.
The Todo module uses in-memory storage by default as a practical example. For production usage, swap the adapter with a real database implementation.
- Add a database URL to your environment:
DB_URL=postgres://user:password@localhost:5432/app_db- Validate
DB_URLinshared/core/config.ts. - Add a shared database client in
shared/infrastructure/persistence/database.ts. - Implement a real adapter (for example
postgres-todo.repository.ts) that satisfiesTodoRepository. - Update
modules/todo/todo.module.tsto inject the real repository intoTodoService.
// Before
const todoRepository = new InMemoryTodoRepository();
// After
const todoRepository = new PostgresTodoRepository(dbClient);This keeps the application layer unchanged and replaces only the infrastructure adapter.
npm installCreate a .env file in the project root:
NODE_ENV=development
PORT=3000npm run devnpm run build
npm startnpm run dev: Start dev server with watch modenpm run build: Compile TypeScript and resolve path aliasesnpm start: Run compiled app fromdistnpm run test-native: Run entrypoint with Node experimental TypeScript strippingnpm run lint: Run ESLint checksnpm run lint:fix: Auto-fix lint issuesnpm run format: Format TypeScript files with Prettiernpm test: Run tests once with Vitestnpm run test:watch: Run Vitest in watch modenpm run test:ui: Run Vitest UI
GET /api/v1/health- Returns service health metadata (
status,version,timestamp,uptime)
- Returns service health metadata (
POST /api/v1/todos- Creates a Todo item
- Body:
{ "title": "string" }
GET /api/v1/todos- Returns all Todo items stored in memory
PATCH /api/v1/todos/:id- Updates a Todo item
- Body:
{ "title"?: "string", "completed"?: boolean }
DELETE /api/v1/todos/:id- Deletes a Todo item
- Returns
204 No Contenton success
All handled errors follow this response shape:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": []
}
}Possible application error codes currently used:
VALIDATION_ERRORTODO_NOT_FOUNDNOT_FOUNDINTERNAL_ERROR
Current integration tests cover:
- Health check endpoint
- Todo creation validation
- Todo create + list flow
- Todo update with
PATCH - Todo deletion with
DELETE
After starting the server, open:
http://localhost:3000/docs
OpenAPI server URL is generated using the current PORT value.
- The app uses ESM (
"type": "module"). - TypeScript output is generated to
dist. - Todo storage is in-memory and intended for starter/development usage.
- Replacing persistence only requires a new repository adapter + module wiring update.