Pular para o conteúdo
PrumoPrumo
    Busca em todos os títulos da documentação.

    Este documento vive no repositório e é mantido em inglês. O site não o traduz.

    Ler este arquivo no GitHub

    The locked stack

    Settled on 2026-09-09, one question at a time. Every entry has a matching entry in DECISIONS.md recording the options considered and what the choice costs.

    Treat this as given. If you believe an entry is wrong, say so once, clearly, then follow it.


    Structural

    Multi-tenancy Only when the project is multi-tenant, because it is a third axis beside Type and Architecture, and a single-tenant project receives none of it. Then: tenant_id on every tenant-scoped table, isolated by a MikroORM filter. No schema-per-tenant. RLS is stronger and unmeasured; see docs/OPEN-QUESTIONS.md.
    Primary keys UUID v7, generated by the database: DEFAULT uuidv7(). Not by the application.
    Delete Hard delete is the default. deleted_at requires a stated reason: audit, user-facing recovery, legal retention.
    Timezone UTC in the database. timestamptz on every time column, without exception. Conversion at the edge only.
    Database PostgreSQL 18 or later. Not a variable.
    Errors Exceptions, handled Nest's way, with HttpException and exception filters. No Result type.

    Version floors

    These accumulate. Every one is a machine a generated project will not run on.

    Floor Required by
    PostgreSQL 18 uuidv7(), which shipped in September 2025
    Node 22.17 MikroORM v7
    TypeScript 6.0 nestjs/typescript-starter, which declares ^6.0.2. It supersedes the 5.8 floor MikroORM v7 required

    Core

    Area Choice
    Monorepo pnpm workspaces, with native catalog: for versions. No Turborepo. Applies only when the architecture is monorepo.
    Language TypeScript 6, based on nestjs/typescript-starter: module and moduleResolution nodenext, target ES2023, strict on, strictPropertyInitialization off. Plus noUncheckedIndexedAccess. Not exactOptionalPropertyTypes.
    Quality Biome. Pre-commit is .githooks/pre-commit running biome check --staged --write, wired by "prepare": "node .githooks/install.mjs", which sets core.hooksPath inside a Git repository and does nothing outside one. No husky, no lint-staged.
    API NestJS. DTOs are classes validated by class-validator + class-transformer through ValidationPipe; @nestjs/swagger reads the same decorators.
    ORM MikroORM v7, with @mikro-orm/postgresql, @mikro-orm/migrations, @mikro-orm/nestjs. Entities use defineEntity with a class: the schema is in the defineEntity call and carries no ORM decorators. The class carries @Exclude and @ApiProperty, for serialization and documentation.
    Auth Better Auth, mounted directly on a controller with @All('*path') passing the request to toNodeHandler(auth), plus a hand-written global guard. Own Postgres schema, own connection, own migration CLI, generateId: 'uuid'. httpOnly cookie on web and mobile, kept in expo-secure-store. No community Nest bridge.
    Observability Structured logs and a health check. No error reporting and no tracing by default. See Documented, not installed below.
    Testing Vitest, Testcontainers, Testing Library.
    Web Vite, React, TanStack Router, TanStack Query, Tailwind, shadcn/ui, react-hook-form with Zod as the resolver.
    Mobile Expo with prebuild, Expo Router, NativeWind, Reanimated, MMKV, expo-secure-store.
    Site Next with the App Router, never the Pages Router. Tailwind and shadcn/ui. Static by default, revalidated where content changes, dynamic only with a stated reason. Data is fetched on the server.

    shadcn/ui is not a dependency, because components are copied into the project. Templates ship its plumbing and only the components their own screens use, installed by its CLI, on Base UI, which is shadcn's default primitive library.


    Documented, not installed

    Prumo never installs a tool requiring a commercial account. Such a tool is documented (how it is wired, what it needs, what it changes) so it is ready the day the developer wants it, and absent until then. Installing a service that needs an account, a DSN and eventually a bill is a commercial decision made on the developer's behalf, inside a project they own.

    Capability Documented adapter Default
    Error reporting and tracing @sentry/nestjs, see api/observability.md not installed

    Deliberately dropped

    Sixteen entries left the previous stack. Each was removed for a stated reason, not trimmed for taste.

    Dropped Because
    Result / neverthrow The restart removed Result. Nest's error model is exceptions, and two error models in one codebase is worse than either.
    ts-pattern Existed for the Result union. switch with a never default gives the same exhaustiveness for free.
    dependency-cruiser Enforced the hexagonal layering, which is gone. The restart measured it producing a useless file to satisfy no-circular.
    Fastify + type provider + Scalar + Pino Replaced by NestJS.
    The porting rule Removed explicitly by the restart.
    Drizzle + drizzle-kit Replaced by MikroORM. Still on 0.x with an unreleased breaking v1, and absent from Nest's documentation.
    Turborepo Its value is caching, and a two-package monorepo does not hurt yet.
    syncpack pnpm catalogs do the job natively.
    knip Finds nothing on a day-zero project.
    husky + lint-staged biome check --staged and core.hooksPath replace both, with no dependency.
    @t3-oss/env-core Zod-based, and Zod left the API. Its distinctive value, keeping server secrets out of a client bundle, does not apply to an API with no bundle.
    Zustand TanStack Query owns server state; what remains is usually context-sized.
    FlashList FlatList handles small lists, and the swap is local when they grow.
    MSW Mocks a network that does not exist yet.
    Playwright Downloads a browser to walk screens that do not exist yet.
    exactOptionalPropertyTypes The only strictness flag whose friction lands in library types rather than your own code.

    The two rules that decided most of this

    The day-zero test. Does this have work on the day the project is born? It removed knip, MSW, Playwright, ts-pattern and Zustand.

    Its limit. The test does not apply to anything that owns data, because arriving late is then a migration rather than a swap. That kept MMKV and Better Auth. Whatever owns data is decided at the start; whatever draws a screen can wait.