Repository navigation
feat: foundation for auth — environment, database, e-mail and error format (LUI-135) - #2
Merged
argentinaluiz merged 14 commits intoOct 9, 2026
Conversation
The auth slices need a database and an inbox from the first run. The api container now prepares its own .env, RS256 keys and migrations on start, so a fresh clone needs no manual step, and healthchecks let 'up --wait' block until that is done. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
Every auth slice relies on these: routes under /v1, one filter that shapes all errors with a stable code, env vars validated at boot, an e-mail interface with an SMTP driver, and e2e/integration suites that run against a real test database and Mailpit. The example controller is gone, so the unit suite must pass empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…body Keeps each fact in its owner: environment in the root guide, code practices and commands in the API guide, error contract, env vars and the Prisma 7 pin in the LLD. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
A third-party SDK error carrying statusCode 404 was answered as a client error and never logged. The filter now takes the status only from HttpException and body-parser errors, keeps deliberate 5xx statuses instead of flattening them to 500, and drops the connection when the response has already started. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
Subject configs re-read raw process.env, so conversion and defaults lived outside the schema and a value changed after boot skipped validation. The PEM regex also accepted unparsable keys and refused valid PKCS#1 ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
nodemailer defaults keep a send hanging for minutes on a stuck server, so the timeouts are now explicit and configurable. The Mailpit helper returned as soon as any message existed, which would hand a resend test the first e-mail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
pg_isready over the unix socket also passes on the temporary init server, letting the api start migrating too early. The production image cannot apply migrations; that is out of scope here, so the open decision is written down in the HLD. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…ne place The three Vitest configs, the env stub cleanup and the problem+json assertions were copied across files, and the env schema ran through two separate mechanisms. Each now has a single owner, so the next feature copies one pattern instead of four. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
With the pg adapter, $connect() opens no connection, so the API booted against a dead database and only failed on the first request. The env setup script also replaced a key the developer had put in .env when the other half of the pair was missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
The test database URL is now derived once per run and handed to the test files, so a development database whose name already ends in _test can no longer be truncated. The env is validated once per set of values, SMTP connections are pooled, tests read mail from the server SMTP_URL points to, and the scaffold leftovers are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…ecks A database that accepts the connection and never answers held the boot forever, because pg waits without a limit by default. The env schema now also refuses a JWT public key that is not the pair of the private one and a MAIL_FROM without an address, which used to fail only at runtime. The test base hands the Mailpit address to the test files once, so a test that swaps SMTP_URL keeps reading from the right server, and a test app that fails to boot is closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
…he SMTP idle wait A HttpException carrying a 2xx/3xx status went out as a problem document with a success status. The SMTP socket timeout doubled as the pool's idle limit, so pooled connections were dropped before being reused; the inactivity wait now has its own, longer setting. Also documents how to repair the environment when the API container exits during setup, and the Prisma CLI config as the second place that reads the environment directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
There was a problem hiding this comment.
🟡 Changes recommended
The error filter can misclassify third-party errors as trusted framework errors and expose their status codes.
1 open finding
What changed in this PR
Establishes the API foundation required by upcoming authentication features.
Changes:
- Adds PostgreSQL, Prisma, Mailpit, validated configuration, and SMTP infrastructure.
- Standardizes validation and RFC 9457 error responses under
/v1. - Introduces unit, integration, and end-to-end test suites with real dependencies.
| File | Description |
|---|---|
skills-lock.json |
Registers new agent skills. |
docs/lld.md |
Documents infrastructure and API contracts. |
docs/hld.md |
Updates environments and open decisions. |
compose.dev.yaml |
Adds PostgreSQL, Mailpit, and healthchecks. |
apps/web/Dockerfile.dev |
Signals dependency readiness. |
apps/web/app/layout.tsx |
Localizes metadata and language. |
apps/api/vitest.shared.ts |
Defines shared test configuration. |
apps/api/vitest.config.ts |
Configures unit tests. |
apps/api/vitest.config.int.ts |
Configures integration tests. |
apps/api/vitest.config.e2e.ts |
Configures end-to-end tests. |
apps/api/test/test-base.e2e-spec.ts |
Verifies the test foundation. |
apps/api/test/support/test-env.ts |
Derives test dependency URLs. |
apps/api/test/support/setup.ts |
Initializes each test file. |
apps/api/test/support/problem.ts |
Asserts problem responses. |
apps/api/test/support/mailpit.ts |
Reads captured test mail. |
apps/api/test/support/global-setup.ts |
Migrates and provides test services. |
apps/api/test/support/database.ts |
Cleans the test database. |
apps/api/test/support/create-test-app.ts |
Builds complete test applications. |
apps/api/test/problem-details.e2e-spec.ts |
Tests validation and error responses. |
apps/api/test/app.e2e-spec.ts |
Removes scaffold end-to-end tests. |
apps/api/src/main.ts |
Applies configuration and shutdown hooks. |
apps/api/src/infra/mail/smtp-mail-sender.ts |
Implements bounded SMTP delivery. |
apps/api/src/infra/mail/smtp-mail-sender.int-spec.ts |
Tests real SMTP delivery. |
apps/api/src/infra/mail/mail.module.ts |
Registers the mail provider. |
apps/api/src/infra/mail/mail-sender.ts |
Defines the mail abstraction. |
apps/api/src/infra/database/prisma.service.ts |
Adds the Prisma database client. |
apps/api/src/infra/database/database.module.ts |
Exposes database infrastructure. |
apps/api/src/config/mail.config.ts |
Maps mail configuration. |
apps/api/src/config/env.ts |
Caches validated environment values. |
apps/api/src/config/env.schema.ts |
Validates environment and JWT keys. |
apps/api/src/config/database.config.ts |
Maps database configuration. |
apps/api/src/config/config.module.ts |
Centralizes application configuration. |
apps/api/src/config/config.module.int-spec.ts |
Tests configuration validation. |
apps/api/src/config/auth.config.ts |
Maps JWT configuration. |
apps/api/src/config/app.config.ts |
Maps application configuration. |
apps/api/src/common/validation/input-validation.pipe.ts |
Adds global DTO validation. |
apps/api/src/common/errors/problem-details.filter.ts |
Formats global error responses. |
apps/api/src/common/errors/input-validation.error.ts |
Models field validation errors. |
apps/api/src/common/errors/error-code.ts |
Defines stable error codes. |
apps/api/src/common/errors/domain-error.ts |
Defines the domain error base. |
apps/api/src/app.setup.ts |
Applies the /v1 prefix. |
apps/api/src/app.service.ts |
Removes scaffold service. |
apps/api/src/app.module.ts |
Wires foundation modules and providers. |
apps/api/src/app.controller.ts |
Removes scaffold controller. |
apps/api/src/app.controller.spec.ts |
Removes scaffold unit test. |
apps/api/scripts/setup-env.mjs |
Creates local environment and JWT keys. |
apps/api/README.md |
Replaces scaffold documentation. |
apps/api/prisma/schema.prisma |
Initializes the Prisma schema. |
apps/api/prisma/migrations/migration_lock.toml |
Locks the migration provider. |
apps/api/prisma/migrations/20261009000000_enable_extensions/migration.sql |
Enables PostgreSQL extensions. |
apps/api/prisma.config.ts |
Configures Prisma CLI behavior. |
apps/api/pnpm-workspace.yaml |
Allows Prisma install scripts. |
apps/api/package.json |
Adds infrastructure dependencies and scripts. |
apps/api/Dockerfile.dev |
Automates development setup. |
apps/api/Dockerfile |
Generates Prisma client during builds. |
apps/api/AGENTS.md |
Documents API practices and commands. |
apps/api/.prettierignore |
Excludes generated Prisma code. |
apps/api/.oxlintrc.json |
Excludes generated Prisma code. |
apps/api/.gitignore |
Ignores generated Prisma code. |
apps/api/.env.example |
Supplies working local defaults. |
apps/api/.dockerignore |
Excludes generated client from context. |
AGENTS.md |
Documents Compose-based development. |
.claude/skills/implement/SKILL.md |
Adds implementation workflow. |
.claude/skills/implement/agents/openai.yaml |
Configures implementation skill UI. |
.claude/skills/implement-spec/SKILL.md |
Adds spec implementation workflow. |
.claude/skills/implement-spec/agents/openai.yaml |
Configures spec skill UI. |
.claude/skills/handoff/SKILL.md |
Adds session handoff workflow. |
.claude/skills/handoff/agents/openai.yaml |
Configures handoff skill UI. |
Files not reviewed (1)
- apps/api/pnpm-lock.yaml: Generated file
🧠 Review effort: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
The filter took any thrown object with expose, a string type and a 4xx statusCode as an Express body-reader error, so a third-party error with that shape leaked its status instead of becoming a 500. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces broad cross-cutting infrastructure and API contracts that warrant final human validation despite comprehensive tests.
0 open findings
1 resolved since last review
Files not reviewed (1)
- apps/api/pnpm-lock.yaml: Generated file
🧠 Review effort: Balanced
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
The base every authentication slice of feature 01 builds on (LUI-135). It delivers no user-facing behaviour: it leaves the environment and the API ready for the first slice (LUI-137).
What changed
up -d --build --waitleaves the four services healthy, with.envcreated from.env.example, the RS256 JWT key pair generated and the migrations applied, with no manual step.citextandpg_trgm; domain tables come with the next tickets./v1, input is validated, and every failure goes out as RFC 9457application/problem+jsonwith a stablecode. Unexpected errors become a 500 with no internal detail.MailSenderinterface with an SMTP driver, with bounded waits.pnpm test,pnpm test:int,pnpm test:e2e). Integration and e2e run against the real PostgreSQL (a separate_testdatabase, cleaned before each test file) and the real Mailpit, with no doubles. The scaffold's sample controller and its tests are gone.AGENTS.mddescribes the new services and bring-up;apps/api/AGENTS.mdlists the three suites and the test base;docs/lld.mdgains the error body (3.1) and the new variables (6);docs/hld.mdrecords three open questions about production.Verification
Run on a fresh clone of this branch (no
.env, nonode_modules, no volumes), following only the rootAGENTS.md:up -d --build --wait: the four services healthy in 19 s; migration applied; web, API and Mailpit answering from the host.pnpm test: no test files, exits 0.pnpm test:int: 38 passed.pnpm test:e2e: 24 passed.pnpm exec tsc --noEmitandpnpm lint: clean.git statusstays empty after the bring-up:.env, the keys and the generated Prisma client are ignored.All twelve acceptance criteria of the ticket were checked this way. The per-file database cleanup was also proven by hand (rows planted in
app_testwere gone after running another test file); that proof is not an automated test.Notes for review
feature/projeto-web, notmain: that branch still needs its own PR. It was one commit behind its local copy when this PR was opened, sod50401b(the handoff and implement skills) shows up here too..envis created by a script instead of copied by hand;up --waitwith healthchecks replaces "watch the logs"; the test database has no variable of its own (it isDATABASE_URLplus_test); the env schema only covers the variables this ticket uses.docs/hld.md, with no code for them here: how migrations reach staging and production (the production image does not apply them), whetherDATABASE_URLin production uses the Cloud SQL socket form (the schema rejects it today), and how the JWT keys are delivered as secrets (only PEM with real line breaks is accepted).MAIL_FROMaccepts a display name with an unquoted comma; the minimum RSA key size is not enforced.Dockerfile.dev(readiness file for the healthcheck) and in the root layout (lang="pt-BR"and the page title).🤖 Generated with Claude Code
https://claude.ai/code/session_01RgE8sJLFxENgwV47XrwDGP