Project Structure

tsdevstack uses a monorepo structure where all your applications, packages, and infrastructure live together. This makes it easy to share code, manage dependencies, and deploy consistently.

Directory overview

my-project/
├── apps/
│   ├── auth-service/       # Backend NestJS service
│   ├── bff-service/        # Backend-for-frontend service
│   ├── offers-service/     # Another backend service
│   ├── frontend/           # Next.js application
│   └── react-app/          # SPA (Rsbuild/Vite)
├── packages/               # Shared code
│   ├── auth-service-client/  # Generated HTTP clients + DTOs
│   └── nest-common/
├── infrastructure/
│   ├── kong/               # Generated gateway image (Dockerfile) + per-env cloud kong.yml
│   └── terraform/          # Cloud deployment configs
│       └── dev/            # Per-environment Terraform
├── kong-plugins/           # Your custom Kong plugins (optional)
├── .tsdevstack/            # Framework configuration
│   ├── config.json         # Main project config
│   └── infrastructure.json # Infrastructure settings
├── .secrets.tsdevstack.json  # Framework-generated secrets (git-ignored)
├── .secrets.user.json        # Your custom secrets (git-ignored)
├── .secrets.local.json       # Merged output (git-ignored)
├── docker-compose.yml        # Generated - local orchestration
├── kong.tsdevstack.yml       # Generated - gateway config
├── kong.user.yml             # Your gateway overrides
├── kong.yml                  # Merged gateway config
└── package.json              # Root workspace config (npm workspaces)

Key directories

apps/

All applications live directly under apps/. Backend services, workers, and frontends are all peers:

apps/
├── auth-service/           # NestJS backend
├── bff-service/            # NestJS backend
├── offers-service/         # NestJS backend
├── frontend/               # Next.js app
└── react-app/              # SPA

Backend services (NestJS)

Each backend service is a standalone NestJS application using a module-based structure:

apps/auth-service/
├── prisma/
│   ├── schema.prisma       # Database schema
│   ├── migrations/         # Database migrations
│   └── seed.ts             # Seed data
├── docs/
│   └── openapi.json        # Generated OpenAPI spec
├── scripts/
│   └── generate-openapi.ts # OpenAPI generation script
├── src/
│   ├── main.ts             # Application entry point
│   ├── app.module.ts       # Root NestJS module
│   ├── auth/               # Feature module
│   │   ├── auth.module.ts
│   │   ├── auth.controller.ts
│   │   ├── auth.service.ts
│   │   └── dto/            # Module-specific DTOs
│   ├── user/               # Another feature module
│   ├── prisma/             # Prisma service module
│   ├── processors/         # BullMQ job processors
│   ├── worker.ts           # Worker entry point
│   └── worker.module.ts    # Worker module
├── test/                   # E2E tests
├── package.json
└── tsconfig.json

NestJS services use feature modules - each module (auth, user, etc.) contains its own controllers, services, and DTOs. Services are exposed through Kong gateway with route paths generated from OpenAPI decorators.

Workers

Workers handle background job processing within a service. They live inside the service folder and share its codebase:

apps/auth-service/
├── src/
│   ├── main.ts             # Service entry point
│   ├── app.module.ts       # Main service module
│   ├── worker.ts           # Worker entry point
│   ├── worker.module.ts    # Worker module
│   └── processors/         # Job processing logic
├── package.json
└── tsconfig.json

By default, workers run in the same container as the service. For independent scaling, register them as detached workers:

npx tsdevstack register-detached-worker --name email-worker --base-service auth-service

Workers can process queues, handle scheduled tasks, or run long operations without blocking API responses.

Frontends

Web applications (Next.js or SPA) live alongside services:

apps/frontend/
├── src/
│   ├── app/                # Next.js app directory
│   ├── components/         # React components
│   └── lib/                # Utilities
├── public/                 # Static assets
├── package.json
└── next.config.js

Frontends can use generated DTOs from services for type-safe API calls.

packages/

Shared code and generated clients:

packages/
├── auth-service-client/    # Generated from auth-service
│   ├── src/                # HTTP client + types
│   ├── dto/                # DTO classes with decorators
│   └── dist/               # Built output
└── nest-common/            # (Optional) Shared backend utilities

Service client packages (*-client/) are auto-generated when you run npx tsdevstack generate-openapi. The nest-common package is an example of a shared library you can create for code reused across services — it's not generated by the framework.

Import generated clients in any app:

// HTTP client for API calls
import { Api } from '@shared/auth-service-client';

// DTOs for NestJS services
import { UserDto } from '@shared/auth-service-client/dto';

infrastructure/

Cloud deployment configurations organized by environment:

infrastructure/
├── kong/
│   ├── Dockerfile          # Gateway image (generated, local and cloud)
│   ├── .dockerignore       # Generated
│   └── dev/
│       └── kong.yml        # Cloud gateway config for dev (generated, committed)
└── terraform/
    └── dev/                # Development environment
        ├── main.tf
        ├── variables.tf
        └── outputs.tf

Terraform files are generated per environment. See Infrastructure for details. The Kong files are generated by generate-kong (image) and infra:generate-kong (per-environment config); generate-kong also stages kong-plugins/ and declarative/ folders here, which are gitignored.

kong-plugins/

Optional. One folder per custom Kong plugin (handler.lua, schema.lua), built into the gateway image locally and in the cloud. Commit it. See Kong Plugins.

.tsdevstack/

Framework configuration directory:

.tsdevstack/
├── config.json             # Main project configuration
├── config.schema.json      # JSON schema for config validation
├── infrastructure.json     # Infrastructure settings (tiers, domains, scaling)
├── infrastructure.schema.json  # JSON schema for infrastructure validation
├── ci.json                 # CI/CD configuration
├── ci.schema.json          # JSON schema for CI config
├── secret-map.json         # Which secrets are assigned to which services
├── lint-staged-check.mjs   # Pre-commit lint-staged configuration
└── .credentials.*.json     # Cloud credentials (git-ignored)

This is where the framework stores its configuration. config.json contains your project name, services, and settings. infrastructure.json controls per-service scaling tiers, domains, and cloud-specific options. Schema files provide IDE auto-completion and validation.

Secrets files

Local development secrets are JSON files at the project root (all git-ignored):

.secrets.tsdevstack.json    # Framework-generated secrets (do not edit)
.secrets.user.json          # Your custom secrets (edit this one)
.secrets.local.json         # Merged output (do not edit)

The three-file merge strategy ensures your customizations persist across framework updates. See Local Secrets for more.

Generated files

These files are created and managed by tsdevstack:

FilePurposeRegenerate with
docker-compose.ymlLocal container orchestrationnpx tsdevstack generate-docker-compose
kong.tsdevstack.ymlFramework gateway routesnpx tsdevstack generate-kong
kong.ymlMerged gateway confignpx tsdevstack generate-kong
infrastructure/kong/DockerfileGateway image definitionnpx tsdevstack generate-kong
.secrets.tsdevstack.jsonFramework secretsnpx tsdevstack generate-secrets
.secrets.local.jsonMerged secretsnpx tsdevstack generate-secrets

Do not edit generated files directly. Use override files instead:

  • kong.user.yml - Custom Kong routes (merged into kong.yml)
  • docker-compose.user.yml - Custom Docker config
  • .secrets.user.json - Your custom secrets

Configuration files

.tsdevstack/config.json

Framework configuration managed by CLI commands. This file is updated automatically when you add or remove services - you shouldn't need to edit it directly.

The exception is optional settings you set by hand, such as framework.apiKeys.ipLimitPerMinute (the per-IP ceiling on partner API routes, see API Keys).

npm workspaces

The monorepo uses npm workspaces with Lerna for task orchestration. Workspaces are defined in the root package.json:

{
  "workspaces": [
    "apps/*",
    "packages/*"
  ]
}

Finding things

Looking for...Location
Service business logicapps/{name}/src/{module}/{module}.service.ts
API endpointsapps/{name}/src/{module}/{module}.controller.ts
Database schemaapps/{name}/prisma/schema.prisma
Database migrationsapps/{name}/prisma/migrations/
OpenAPI specapps/{name}/docs/openapi.json
Shared typespackages/{service}-client/ (generated)
Cloud deploymentinfrastructure/terraform/{env}/
Your secrets.secrets.user.json
Framework config.tsdevstack/config.json
Gateway configkong.yml (merged), kong.user.yml (your overrides)
Custom Kong pluginskong-plugins/{plugin}/
Environment setupdocker-compose.yml (generated)