{"id":14969289,"url":"https://github.com/marcoturi/fastify-boilerplate","last_synced_at":"2026-04-09T07:01:05.159Z","repository":{"id":243575272,"uuid":"812793594","full_name":"marcoturi/fastify-boilerplate","owner":"marcoturi","description":"Fastify 5 application boilerplate based on clean architecture, domain-driven design, CQRS, functional programming, vertical slice architecture for building production-grade applications 🚀","archived":false,"fork":false,"pushed_at":"2026-03-02T01:18:57.000Z","size":5420,"stargazers_count":402,"open_issues_count":5,"forks_count":44,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-03-02T04:52:46.284Z","etag":null,"topics":["agents","backend","bdd","clean-architecture","cqrs","cucumber","ddd","docker","fastify","functional-programming","graphql","hexagonal-architecture","mercurius","nodejs","onion-architecture","opentelemetry","typescript","vitest"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcoturi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2024-06-09T22:20:44.000Z","updated_at":"2026-03-02T01:18:21.000Z","dependencies_parsed_at":"2024-06-09T23:35:03.419Z","dependency_job_id":"e24f1132-1021-460e-ba4d-b23fbe6b536d","html_url":"https://github.com/marcoturi/fastify-boilerplate","commit_stats":{"total_commits":279,"total_committers":3,"mean_commits":93.0,"dds":"0.28673835125448033","last_synced_commit":"151cead22fe07bf092e95d314c6b4e95ac1f8739"},"previous_names":["marcoturi/fastify-boilerplate"],"tags_count":217,"template":true,"template_full_name":null,"purl":"pkg:github/marcoturi/fastify-boilerplate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoturi%2Ffastify-boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoturi%2Ffastify-boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoturi%2Ffastify-boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoturi%2Ffastify-boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcoturi","download_url":"https://codeload.github.com/marcoturi/fastify-boilerplate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcoturi%2Ffastify-boilerplate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30071895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T03:25:38.285Z","status":"ssl_error","status_checked_at":"2026-03-04T03:25:05.086Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","backend","bdd","clean-architecture","cqrs","cucumber","ddd","docker","fastify","functional-programming","graphql","hexagonal-architecture","mercurius","nodejs","onion-architecture","opentelemetry","typescript","vitest"],"created_at":"2024-09-24T13:41:30.075Z","updated_at":"2026-03-04T06:23:38.236Z","avatar_url":"https://github.com/marcoturi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Fastify Boilerplate Logo](doc/images/fastify_logo.png)\n\n[![Biome](https://img.shields.io/badge/Biome-60a5fa?logo=biome\u0026logoColor=fff)](https://biomejs.dev/) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) [![MIT License](https://img.shields.io/github/license/marcoturi/fastify-boilerplate)](https://github.com/marcoturi/fastify-boilerplate/blob/main/LICENSE) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/marcoturi/fastify-boilerplate/codeql-analysis.yml) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/marcoturi/fastify-boilerplate/release.yml)\n\nA production-ready [Fastify 5](https://github.com/fastify/fastify) boilerplate built on Clean Architecture, CQRS, DDD, and functional programming. Designed as a starting point for real-world applications, the architecture is framework-agnostic at its core — the patterns and boundaries translate to any language or framework.\n\n## Table of Contents\n\n- [Features](#features)\n- [Prerequisites](#prerequisites)\n- [Getting Started](#getting-started)\n- [Available Scripts](#available-scripts)\n- [Running with Docker](#running-with-docker)\n- [API Endpoints](#api-endpoints)\n- [Architecture](#architecture)\n  - [Principles](#principles)\n  - [Modules](#modules)\n  - [Module Components](#module-components)\n- [Folder Structure](#folder-structure)\n- [OpenTelemetry](#opentelemetry)\n- [Testing](#testing)\n- [Client Types Package](#client-types-package)\n- [CI/CD Pipeline](#cicd-pipeline)\n- [AI-Assisted Development](#ai-assisted-development)\n- [Why Biome over ESLint + Prettier?](#why-biome-over-eslint--prettier)\n- [Useful Resources](#useful-resources)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Features\n\n| Category | Details |\n|---|---|\n| **Runtime** | Native TypeScript via [Node.js \u003e= 24](https://nodejs.org/en/blog/release/v24.0.0) type stripping — no build step, no transpiler |\n| **Framework** | [Fastify 5](https://github.com/fastify/fastify) with [Awilix](https://github.com/jeffijoe/awilix) DI and [Pino](https://github.com/pinojs/pino) logging |\n| **API** | REST ([TypeBox](https://github.com/sinclairzx81/typebox) schemas, Swagger UI) + GraphQL ([Mercurius](https://github.com/mercurius-js/mercurius), GraphiQL in dev) |\n| **Database** | [Postgres.js](https://github.com/porsager/postgres) client + [DBMate](https://github.com/amacneil/dbmate) migrations \u0026 seeds |\n| **Security** | [@fastify/helmet](https://github.com/fastify/fastify-helmet), [@fastify/under-pressure](https://github.com/fastify/under-pressure) for back-pressure |\n| **Telemetry** | Vendor-agnostic [OpenTelemetry](https://opentelemetry.io/) with auto-instrumentation (disabled by default) |\n| **Linting** | [Biome](https://biomejs.dev/) — single tool for linting, formatting, and import sorting |\n| **Architecture** | [dependency-cruiser](https://github.com/sverweij/dependency-cruiser) validates layer boundaries at CI time |\n| **Release** | [Husky](https://github.com/typicode/husky) + [Commitlint](https://commitlint.js.org/) + [Semantic Release](https://github.com/semantic-release/semantic-release) |\n| **Client types** | REST (OpenAPI) and GraphQL types [auto-generated and published to npm](#client-types-package) on every release |\n| **Testing** | E2E with [Cucumber](https://cucumber.io/docs/installation/javascript/) (Gherkin), unit/integration with `node:test`, load tests with [k6](https://github.com/grafana/k6) |\n| **Docker** | Production-ready multi-stage [Dockerfile](Dockerfile) (Alpine, non-root, health check) + [Docker Compose](docker-compose.yml) |\n| **AI-Ready** | [AGENTS.md](AGENTS.md) — architecture rules and coding conventions for AI assistants |\n\n## Prerequisites\n\n| Tool | Notes |\n|---|---|\n| **Node.js** | \u003e= 24 — required for native TypeScript execution. A `.nvmrc` is included — run `fnm use` or `nvm use` |\n| **pnpm** | \u003e= 10 — package manager (`corepack enable` to activate) |\n| **Docker** | Used to run PostgreSQL via Docker Compose. Alternatively, use a local Postgres install |\n\n## Getting Started\n\n```bash\n# 1. Scaffold from the template\nnpx degit marcoturi/fastify-boilerplate my-app\ncd my-app\n\n# 2. Install dependencies\npnpm install\n\n# 3. Create your .env file\npnpm create:env          # copies .env.example → .env\n\n# 4. Start PostgreSQL (pick one)\ndocker compose up postgres -d   # via Docker Compose\n# — or use a local Postgres and adjust .env values —\n\n# 5. Run database migrations\npnpm db:migrate\n\n# 6. Start the dev server (with watch mode and pretty logs)\npnpm start\n```\n\nThe server starts at **http://localhost:3000** by default. See [API Endpoints](#api-endpoints) for what's available.\n\n## Available Scripts\n\n### Development\n\n| Script | Description |\n|---|---|\n| `pnpm start` | Start dev server with watch mode and pretty-printed logs |\n| `pnpm start:prod` | Start production server (no watch, no pretty-print) |\n| `pnpm create:env` | Copy `.env.example` to `.env` (fails if `.env` already exists) |\n\n### Code Quality\n\n| Script | Description |\n|---|---|\n| `pnpm check` | Run lint + format check + type check (use this before committing) |\n| `pnpm check:fix` | Same as `check` but auto-fixes lint and format issues |\n| `pnpm format` | Auto-format all files with Biome |\n| `pnpm lint` | Run Biome linter with auto-fix |\n| `pnpm type:check` | TypeScript type checking (`tsc --noEmit`) |\n| `pnpm deps:validate` | Validate architecture layer boundaries with dependency-cruiser |\n| `pnpm deps:graph` | Generate a dependency graph SVG in `doc/` |\n\n### Testing\n\n| Script | Description |\n|---|---|\n| `pnpm test` | Run unit tests (alias for `test:unit`) |\n| `pnpm test:unit` | Run unit and integration tests with `node:test` |\n| `pnpm test:coverage` | Run unit tests with c8 coverage |\n| `pnpm test:e2e` | Run E2E tests with Cucumber (requires running Postgres) |\n\n### Database\n\n| Script | Description |\n|---|---|\n| `pnpm db:migrate` | Apply pending migrations |\n| `pnpm db:create-migration` | Create a new migration file |\n| `pnpm db:seed` | Run database seeds |\n| `pnpm db:create-seed` | Create a new seed file |\n\n### Other\n\n| Script | Description |\n|---|---|\n| `pnpm generate:types` | Generate REST and GraphQL client types (requires running server + DB) |\n\n## Running with Docker\n\n### Full stack (app + Postgres)\n\n```bash\npnpm create:env       # create .env from .env.example (if not done already)\ndocker compose up     # builds the app image and starts all services\n```\n\n### Standalone image\n\n```bash\ndocker build -t fastify-boilerplate .\ndocker run -p 3000:3000 --env-file .env -e HOST=0.0.0.0 fastify-boilerplate\n```\n\n### Dockerfile highlights\n\n- **Multi-stage build** — dependencies installed in an isolated stage for optimal layer caching\n- **Node Alpine** — small footprint, native TypeScript execution (no build step)\n- **Non-root user** — runs as an unprivileged `fastify` user (UID 1001)\n- **dumb-init** — proper PID 1 signal forwarding for graceful shutdown\n- **HEALTHCHECK** — built-in Docker health check against `/health` every 30 seconds\n\n## API Endpoints\n\n| Endpoint | Description |\n|---|---|\n| `GET /health` | Health check ([@fastify/under-pressure](https://github.com/fastify/under-pressure)) |\n| `/api/...` | All REST routes are prefixed with `/api` (e.g. `/api/v1/users`) |\n| `GET /api-docs` | Swagger UI (interactive API documentation) |\n| `GET /api-docs/json` | OpenAPI 3.1.0 JSON spec |\n| `POST /graphql` | GraphQL endpoint ([Mercurius](https://github.com/mercurius-js/mercurius)) |\n| `GET /graphql` | GraphiQL IDE (development only) |\n\n## Architecture\n\n![Architecture Diagram](doc/images/fastify-boilerplate.png)\n\u003csup\u003eDiagram adapted from [Domain-Driven Hexagon](https://github.com/Sairyss/domain-driven-hexagon)\u003c/sup\u003e\n\n### Principles\n\n**Project-level:**\n\n- **Adaptable complexity** — the structure scales up or down by adding or removing layers to match the application's actual needs.\n- **Future-proofing** — framework code and business logic are separated. Dependencies are well-established and minimal.\n- **Functional programming first** — composition and factory functions over classes and inheritance.\n- **Microservices-ready** — vertical slices, path aliases, and CQRS make it straightforward to extract a module into its own service later.\n\n**Code-level:**\n\n- **Framework-agnostic core** — business logic has no Fastify dependency. Fastify concerns stay in routes.\n- **Protocol-agnostic handlers** — command/query handlers serve REST, GraphQL, gRPC, or CLI equally.\n- **Database-agnostic domain** — SQL stays in repository files. Handlers interact with data through repository ports (interfaces).\n- **Inward dependency flow** — outer layers depend on inner layers, never the reverse: `Route → Handler → Domain → Repository`.\n\nBased on:\n\n- [Domain-Driven Design (DDD)](https://en.wikipedia.org/wiki/Domain-driven_design)\n- [Hexagonal (Ports and Adapters) Architecture](\u003chttps://en.wikipedia.org/wiki/Hexagonal_architecture_(software)\u003e)\n- [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)\n- [Onion Architecture](https://herbertograca.com/2017/09/21/onion-architecture/)\n- [SOLID Principles](https://en.wikipedia.org/wiki/SOLID)\n- [Vertical Slice Architecture](https://www.jimmybogard.com/vertical-slice-architecture/)\n- [Common Closure Principle (CCP)](https://en.wikipedia.org/wiki/Common_closure_principle)\n\n### Modules\n\nEach module maps to a domain concept and lives in its own folder under `src/modules/`. Modules follow the [vertical slice architecture](https://www.jimmybogard.com/vertical-slice-architecture/) — everything a feature needs is co-located.\n\n**Key rules:**\n\n- **No direct imports** between modules. Cross-module communication uses the CQRS buses (commands/queries for request-response, events for fire-and-forget).\n- **Extractable** — any module can be pulled into a separate microservice. The CQRS handler boundary becomes the network boundary.\n- **If two modules are too \"chatty\"**, they probably belong together — merge them.\n\n### Module Components\n\nEach layer has a single responsibility:\n\n**Route** — handles the HTTP/GraphQL/gRPC request. Validates input, formats the response. No business logic.\n\n\u003e All REST routes are prefixed with `/api` (configured in `src/server/index.ts`).\n\nExample: [find-users.route.ts](src/modules/user/queries/find-users/find-users.route.ts)\n\n**Command/Query Handler** — orchestrates the use case. Receives a command or query, calls domain services and repositories through ports, returns a result. One handler per use case (e.g. `CreateUser`, `FindUsers`).\n\nBenefits of the CQRS bus pattern:\n- **Middlewares** — cross-cutting concerns (auth, caching, tracing, rate limiting) plug in between route and handler. Middleware targeting is pattern-based (e.g. `users/*` for all user commands, `users/create` for a specific one). See [middlewares.ts](src/shared/cqrs/middlewares.ts).\n- **Decoupling** — modules communicate through the bus instead of direct imports, making future extraction to microservices trivial.\n\nExample: [find-users.handler.ts](src/modules/user/queries/find-users/find-users.handler.ts)\n\n**Domain Service** — pure business logic. Computes properties, enforces invariants, composes entities. No infrastructure dependencies.\n\nExample: [user.domain.ts](src/modules/user/domain/user.domain.ts)\n\n**Repository** — data access. Converts between domain models and database rows. All SQL lives here. Implements a port (interface) defined alongside it.\n\nExample: [user.repository.ts](src/modules/user/database/user.repository.ts)\n\n\u003e **Guideline:** use as many or as few layers as needed. Not every feature requires a domain service — simpler CRUD operations can go straight from handler to repository.\n\n## Folder Structure\n\n```\n.\n├── db/\n│   ├── migrations/                → SQL migration files (DBMate)\n│   └── seeds/                     → SQL seed files (DBMate)\n├── tests/\n│   ├── \u003cfeature\u003e/\n│   │   ├── \u003cscenario\u003e.feature     → Gherkin E2E scenarios\n│   │   └── \u003cscenario\u003e.k6.ts      → k6 load test scripts\n│   ├── shared/                    → Shared step definitions\n│   └── support/                   → Test server, hooks, custom world\n├── client/                        → Generated REST + GraphQL client types (npm package)\n├── scripts/                       → Type generation scripts\n└── src/\n    ├── instrumentation.ts         → OpenTelemetry setup (loaded via --import)\n    ├── config/                    → Environment validation (env-schema + TypeBox)\n    ├── modules/\n    │   └── \u003cfeature\u003e/\n    │   │   ├── commands/\n    │   │   │   └── \u003ccommand\u003e/\n    │   │   │       ├── command.handler.ts         → Command handler\n    │   │   │       ├── command.route.ts           → REST route\n    │   │   │       ├── command.resolver.ts        → GraphQL resolver\n    │   │   │       ├── command.graphql-schema.ts  → GraphQL type definitions\n    │   │   │       └── command.schema.ts          → TypeBox request/response schemas\n    │   │   ├── queries/\n    │   │   │   └── \u003cquery\u003e/\n    │   │   │       ├── query.handler.ts           → Query handler\n    │   │   │       ├── query.route.ts             → REST route\n    │   │   │       ├── query.resolver.ts          → GraphQL resolver\n    │   │   │       ├── query.graphql-schema.ts    → GraphQL type definitions\n    │   │   │       └── query.schema.ts            → TypeBox request/response schemas\n    │   │   ├── database/\n    │   │   │   ├── feature.repository.port.ts     → Repository interface (port)\n    │   │   │   └── feature.repository.ts          → Repository implementation (adapter)\n    │   │   ├── domain/\n    │   │   │   ├── feature.domain.ts              → Domain service\n    │   │   │   ├── feature.errors.ts              → Domain-specific errors\n    │   │   │   └── feature.types.ts               → Domain types\n    │   │   ├── dtos/\n    │   │   │   ├── feature.graphql-schema.ts      → Shared GraphQL schema\n    │   │   │   └── feature.response.dto.ts        → Shared response DTO\n    │   │   ├── index.ts                           → Action creators, DI declarations\n    │   │   └── feature.mapper.ts                  → Entity ↔ DB model ↔ DTO mapper\n    ├── server/\n    │   ├── index.ts               → Fastify instance setup\n    │   └── plugins/               → Fastify plugins (swagger, CORS, error handler, CQRS, etc.)\n    └── shared/\n        ├── cqrs/                  → Command/Query/Event bus, middlewares\n        ├── db/                    → Postgres connection, transaction helpers, repository base\n        ├── exceptions/            → Base exception classes\n        └── utils/                 → Cross-cutting utilities\n```\n\n## OpenTelemetry\n\nThe project ships with a vendor-agnostic [OpenTelemetry](https://opentelemetry.io/) setup in [`src/instrumentation.ts`](src/instrumentation.ts). It uses the standard [OTLP](https://opentelemetry.io/docs/specs/otlp/) protocol, so it works with any backend (Grafana, Datadog, Honeycomb, Jaeger, etc.) without code changes.\n\n**How it works:**\n\n- **HTTP + Fastify** — the SDK registers the [ESM loader hook](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/esm-support.md) and initialises with `instrumentation-http` and [`@fastify/otel`](https://github.com/fastify/otel). Every request gets a trace span with route, method, status code, and lifecycle hooks.\n- **CQRS** — a tracing middleware in [`src/shared/cqrs/otel-middleware.ts`](src/shared/cqrs/otel-middleware.ts) wraps every command, query, and event in a span. Spans include the action type, bus kind, and correlation ID.\n- **Disabled by default** (`OTEL_SDK_DISABLED=true`). When disabled, `@opentelemetry/api` returns noop implementations — zero overhead.\n\nTo enable, set the standard OTel environment variables in your `.env`:\n\n```bash\nOTEL_SDK_DISABLED=false\nOTEL_SERVICE_NAME=fastify-boilerplate\nOTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318   # your OTLP collector\n```\n\nAll configuration uses [standard OTel environment variables](https://opentelemetry.io/docs/languages/sdk-configuration/general/) — no vendor lock-in.\n\n## Testing\n\n### Unit and integration tests\n\nRun with `node:test`. Test files live next to their source files as `*.spec.ts`.\n\n```bash\npnpm test:unit           # run tests\npnpm test:coverage       # run with c8 coverage\n```\n\n### E2E tests\n\nWritten in [Gherkin](https://cucumber.io/docs/gherkin/) and executed with [Cucumber.js](https://cucumber.io/docs/installation/javascript/). Scenarios live in `tests/\u003cfeature\u003e/\u003cscenario\u003e.feature`, step definitions in `tests/\u003cfeature\u003e/\u003cfeature\u003e.steps.ts`.\n\n```bash\n# Requires a running Postgres with migrations applied\npnpm test:e2e\n```\n\nThe E2E test server is created via `buildApp()` (in `tests/support/server.ts`) — it boots a full Fastify instance without binding to a port, so tests run fast and don't conflict with a running dev server.\n\n### Load tests\n\n[k6](https://github.com/grafana/k6) scripts live alongside their feature's E2E tests.\n\nExample: [create-user.k6.ts](tests/user/create-user/create-user.k6.ts)\n\n## Client Types Package\n\nThe release pipeline automatically generates **REST** (OpenAPI) and **GraphQL** client types and publishes them as the [`@marcoturi/fastify-boilerplate`](https://www.npmjs.com/package/@marcoturi/fastify-boilerplate) npm package. The version is kept in sync with the backend via semantic-release.\n\n### Install\n\n```bash\npnpm add -D @marcoturi/fastify-boilerplate\n```\n\n### Usage\n\n```typescript\n// REST types (generated by openapi-typescript)\nimport type { paths, components } from '@marcoturi/fastify-boilerplate/rest';\n\n// GraphQL types (generated by graphql-codegen)\nimport type { User, Query, Mutation } from '@marcoturi/fastify-boilerplate/graphql';\n```\n\n### Generate locally\n\nTo regenerate the types against a local server (requires running Postgres with migrations applied):\n\n```bash\npnpm generate:types\n```\n\nThis starts the server, fetches the OpenAPI and GraphQL schemas, writes the type files to `client/`, and stops the server.\n\n## CI/CD Pipeline\n\nThe project uses GitHub Actions with two workflows:\n\n**[release.yml](.github/workflows/release.yml)** — runs on every push to `main`:\n\n1. Install dependencies (`pnpm install --frozen-lockfile`)\n2. Code quality checks (`pnpm check`)\n3. Unit tests (`pnpm test`)\n4. E2E tests (`pnpm test:e2e`) against a Postgres service container\n5. Generate client types (`pnpm generate:types`)\n6. Publish release via semantic-release (changelog, GitHub release, npm client package)\n\n**[codeql-analysis.yml](.github/workflows/codeql-analysis.yml)** — runs on pushes and PRs to `main`:\n\n- GitHub CodeQL security analysis for JavaScript/TypeScript\n\n## AI-Assisted Development\n\nThis project ships with an [`AGENTS.md`](AGENTS.md) file — a comprehensive guide for AI coding assistants. It documents the architecture, CQRS patterns, coding conventions, and common pitfalls so that tools like [Cursor](https://cursor.com/), [Claude Code](https://docs.anthropic.com/en/docs/claude-code), and [GitHub Copilot](https://github.com/features/copilot) can generate code that follows the project's established patterns.\n\nAI assistants automatically pick up `AGENTS.md` and apply the conventions without manual prompting.\n\n## Why Biome over ESLint + Prettier?\n\nThis project uses [Biome](https://biomejs.dev/) as a single tool for linting, formatting, and import sorting:\n\n- **One tool, zero plugins** — no `@typescript-eslint/parser`, `eslint-config-prettier`, `eslint-plugin-import`, or other ecosystem packages to keep in sync.\n- **Fast** — written in Rust, orders of magnitude faster than ESLint + Prettier. Noticeable in CI and pre-commit hooks.\n- **Stable** — no more breakage from mismatched plugin versions or peer dependency conflicts across the ESLint ecosystem.\n- **Mature** — covers the vast majority of rules that ESLint + typescript-eslint provide, with a growing community and clear roadmap.\n\n## Useful Resources\n\n- [Domain-Driven Hexagon](https://github.com/Sairyss/domain-driven-hexagon) — the primary inspiration for this project's architecture (adapted toward functional programming)\n- [react-redux boilerplate](https://github.com/marcoturi/react-redux-boilerplate) — companion frontend boilerplate by the same author\n\n## Contributing\n\nContributions are welcome! This project uses [Conventional Commits](https://www.conventionalcommits.org/) enforced by Commitlint and Husky.\n\n1. Fork and clone the repo\n2. Create a branch: `git checkout -b your-feature`\n3. Make your changes\n4. Run `pnpm check` to validate lint, format, and types\n5. Run `pnpm test` (and `pnpm test:e2e` if your change touches API behavior)\n6. Commit using [Conventional Commits](https://www.conventionalcommits.org/) format (e.g. `feat: add user roles`)\n7. Open a Pull Request\n\n## License\n\n[MIT](https://choosealicense.com/licenses/mit/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcoturi%2Ffastify-boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcoturi%2Ffastify-boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcoturi%2Ffastify-boilerplate/lists"}