Initial Commit
This commit is contained in:
124
packages/api/api-plugin-banking/src/schemas/schemas.md
Normal file
124
packages/api/api-plugin-banking/src/schemas/schemas.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Banking Zod schemas
|
||||
|
||||
## Purpose
|
||||
|
||||
This module owns every Zod schema for the public boundary of
|
||||
`@suaveplan/api-plugin-banking`: bank records, bank-account records,
|
||||
the request bodies for create / update / status / transfer / fund-op
|
||||
routes, and the literal-union status/type enums. The schemas are the
|
||||
single source of truth for both runtime validation at the route
|
||||
boundary and the TypeScript types consumed by the service and
|
||||
repository layers.
|
||||
|
||||
## Features
|
||||
|
||||
- 11 Zod schemas covering the full public surface (`BankSchema`,
|
||||
`CreateBankSchema`, `UpdateBankSchema`, `BankAccountSchema`,
|
||||
`CreateBankAccountSchema`, `UpdateBankAccountSchema`,
|
||||
`TransferFundsSchema`, `FundsOperationSchema`,
|
||||
`UpdateAccountStatusSchema`, plus `SortCodeSchema` and the three
|
||||
literal-union enums).
|
||||
- Default values applied via `.default(...)` so omitted fields land at
|
||||
their canonical state at parse time (e.g. `status` defaults to `4`
|
||||
active, `balance` defaults to `0`).
|
||||
- Numeric guards: `account_number` and `sort_code` are positive
|
||||
integers; `amount` on every fund movement is `z.number().positive()`
|
||||
so zero-/negative-amount requests reject at the boundary before
|
||||
reaching the service.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { CreateBankSchema } from "@suaveplan/api-plugin-banking";
|
||||
|
||||
const parsed = CreateBankSchema.parse({
|
||||
name: "Bank of Genesis",
|
||||
sort_code: 123456,
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
The route layer wires each schema to `@hono/zod-openapi` so the OpenAPI
|
||||
document is generated from the Zod types directly:
|
||||
|
||||
```ts
|
||||
import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
|
||||
import {
|
||||
CreateBankAccountSchema,
|
||||
BankAccountSchema,
|
||||
TransferFundsSchema,
|
||||
} from "@suaveplan/api-plugin-banking";
|
||||
|
||||
const createAccountRoute = createRoute({
|
||||
method: "post",
|
||||
path: "/",
|
||||
request: {
|
||||
body: {
|
||||
content: {
|
||||
"application/json": { schema: CreateBankAccountSchema },
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
201: {
|
||||
description: "Created",
|
||||
content: {
|
||||
"application/json": { schema: BankAccountSchema },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For typed service-layer parsing (without re-parsing in routes):
|
||||
|
||||
```ts
|
||||
import { TransferFundsSchema, type TransferFunds } from "@suaveplan/api-plugin-banking";
|
||||
|
||||
const cmd: TransferFunds = TransferFundsSchema.parse(raw);
|
||||
await service.transfer(cmd);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `BankStatusSchema` / `BankAccountStatusSchema` / `BankAccountTypeSchema`
|
||||
— literal unions over the integer status / type codes.
|
||||
- `BankSchema` / `BankAccountSchema` — full normalised record shape
|
||||
returned by repositories (RecordIds flattened to `table:id` strings).
|
||||
- `CreateBankSchema` / `CreateBankAccountSchema` — required + optional
|
||||
input fields, with `status`/`balance`/`overdraft` defaulted.
|
||||
- `UpdateBankSchema` / `UpdateBankAccountSchema` — `.partial()` over a
|
||||
whitelisted subset of writable fields. Any field outside the
|
||||
whitelist is rejected by the repository's `validateFieldName` guard
|
||||
rather than silently dropped.
|
||||
- `TransferFundsSchema`, `FundsOperationSchema`,
|
||||
`UpdateAccountStatusSchema` — fund-movement and status-change
|
||||
request bodies.
|
||||
- `SortCodeSchema` — `XX-XX-XX` UK-style sort-code string used by
|
||||
`GET /banks/sort-code/:sortCode`.
|
||||
|
||||
Every schema exports a matching `z.infer<>` alias under the same name
|
||||
prefixed with the bare type (e.g. `Bank`, `CreateBank`,
|
||||
`TransferFunds`).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Banking schemas are deliberately **local** to this package rather than
|
||||
re-exported from `@suaveplan/api-schemas`: the upstream monorepo kept
|
||||
them in the universal schemas package, but the genesis convention
|
||||
(rule 4 in `CLAUDE.md`) is "NEVER re-export external deps". Owning
|
||||
them here also lets the package evolve the schemas (e.g. multi-currency
|
||||
amount, ledger entries) without forcing a schemas-package version bump
|
||||
on every consumer.
|
||||
|
||||
The `RecordIdSchema` and `TimestampSchema` placeholders are kept
|
||||
intentionally narrow (non-empty strings) because the SurrealDB layer
|
||||
normalises both formats before they cross this boundary. Stricter
|
||||
validation would reject legitimate records.
|
||||
|
||||
`balance` is `z.number().default(0)`, not `z.number().nonnegative()` —
|
||||
the overdraft mechanism intentionally allows negative balances up to
|
||||
the per-account `overdraft` ceiling. `INSUFFICIENT_FUNDS` is enforced
|
||||
in the service layer, not at the schema boundary, because the threshold
|
||||
depends on `account.overdraft` which is not visible to the schema.
|
||||
Reference in New Issue
Block a user