Updated AGENTS.md
This commit is contained in:
56
AGENTS.md
56
AGENTS.md
@@ -33,6 +33,62 @@ packages/api-contract/ # Shared Zod schemas, types, route definitions
|
|||||||
2. Implement handler in `apps/backend`
|
2. Implement handler in `apps/backend`
|
||||||
3. Consume from `apps/frontend` via workspace import `@repo/api-contract`
|
3. Consume from `apps/frontend` via workspace import `@repo/api-contract`
|
||||||
|
|
||||||
|
## Endpoint Pattern
|
||||||
|
|
||||||
|
Every new endpoint must follow the **Result pattern** with the **validate helper**.
|
||||||
|
|
||||||
|
### Files
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `apps/backend/src/lib/result.ts` | `Result<T>` type, `ok()`, `err()` |
|
||||||
|
| `apps/backend/src/lib/errors.ts` | `AppError` discriminated union, `Errors` factory |
|
||||||
|
| `apps/backend/src/lib/http/handle-result.ts` | `handleResult()` — bridges `Result` to HTTP |
|
||||||
|
| `apps/backend/src/lib/http/validate.ts` | `validate.json()`, `.query()`, `.param()`, `.header()`, `.form()` |
|
||||||
|
|
||||||
|
### Layers
|
||||||
|
|
||||||
|
**1. Route** — Use `validate.json(schema)`, `validate.param(schema)`, etc. as Hono middleware.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { validate } from '@/lib/http/validate';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const idParams = z.object({ id: z.string() });
|
||||||
|
router.post('/', validate.json(createSchema), createHandler);
|
||||||
|
router.get('/:id', validate.param(idParams), getHandler);
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Service** — Return `Result<T>`. Use `ok(value)` on success, `err(Errors.*(...))` on failure.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Result, ok, err } from '@/lib/result';
|
||||||
|
import { Errors } from '@/lib/errors';
|
||||||
|
|
||||||
|
export async function doSomething(input: Input): Promise<Result<Output>> {
|
||||||
|
if (conflict) return err(Errors.conflict('Already exists'));
|
||||||
|
return ok(result);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Handler** — Call the service and pass the `Result` to `handleResult`.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { handleResult } from '@/lib/http/handle-result';
|
||||||
|
import { doSomething } from './service';
|
||||||
|
|
||||||
|
export async function myHandler(c: AppContext) {
|
||||||
|
const payload = c.req.valid('json') as Input;
|
||||||
|
return handleResult(c, await doSomething(payload), 201);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Never
|
||||||
|
|
||||||
|
- ❌ Throw custom error classes from services
|
||||||
|
- ❌ Use try/catch in handlers for business logic errors
|
||||||
|
- ❌ Use `zValidator` directly — always use `validate.*`
|
||||||
|
|
||||||
## Authentication (Better Auth)
|
## Authentication (Better Auth)
|
||||||
|
|
||||||
The project uses **Better Auth** for session management, replacing the legacy Supabase Auth.
|
The project uses **Better Auth** for session management, replacing the legacy Supabase Auth.
|
||||||
|
|||||||
Reference in New Issue
Block a user