# Resolver & Executor Patterns

## Resolver Patterns

All resolvers export a default `createResolver()` from `@tailor-platform/sdk`.

### Verify command input types

Before implementing a resolver, read the command's type definition:

```
node_modules/@tailor-platform/erp-kit/src/modules/<module>/command/<commandName>.ts
```

The command source is the ground truth. Do not invent fields that don't exist, and preserve the required/optional distinction.

### SDK type system

See [Resolver docs](https://raw.githubusercontent.com/tailor-platform/sdk/refs/heads/main/packages/sdk/docs/services/resolver.md) for the `t` namespace.

`t.array()` and `t.boolean()` do **not exist**. Never attempt to use them.

Use `t.uuid()` for fields that hold UUIDs (e.g. entity IDs, foreign keys). Do not use `t.string()` for UUID fields.

### Command-based mutation

The most common pattern. Key points:

```ts
import { createContext } from "@tailor-platform/erp-kit/app";

body: async (context) => {
  const db = getDB("main-db");
  const result = await imCommands.createItem(db, { ...context.input }, createContext(context));
  // error handling — see below
  if (!result.ok) { /* switch on result.error.code */ }
  return { id: result.value.item.id, ... };
},
```

- `context.input.optionalField ?? undefined` — Convert null to undefined for command inputs
- `result.value.entity.nullableField ?? ""` — Handle nullable return values
- `createContext(context)` wraps the resolver context for erp-kit commands
- `getDB("main-db")` — Namespace must match `tailor.config.ts`
- Do not directly mutate module-owned tables via Kysely — always use module commands

### Error handling

Resolver specs document error codes the command can return. Add a `case` for every documented code and a `default` with `satisfies never` for exhaustiveness.

**Do this:**

```ts
if (!result.ok) {
  switch (result.error.code) {
    case "INSUFFICIENT_STOCK":
      throw new Error(`Stock insufficient for item ${context.input.itemId}`);
    case "ORDER_ALREADY_CONFIRMED":
      throw new Error("Cannot modify a confirmed order");
    default:
      throw result.error satisfies never;
  }
}
```

**Not this:**

```ts
if (!result.ok) throw result.error;
```
