# Row types

Codecs choose the TypeScript value of a column, and the SQL type stays. Row types are the interfaces schema() emits for each table, or the types the checker keeps. One schema can set both.

## Codecs

`codecs` chooses the TypeScript value of `t.bigint()`, `t.identity()`, and `t.numeric()`. The SQL type stays `bigint` or `numeric`. A time column is already `Temporal`. Omit a key and that codec stays at its default.

The column `as` is what types the value. While the schema codec is `"string"`, a column may pass another `as`. When the schema codec is `"number"` or `"bigint"`, every column of that type must pass the same `as`. A bare column was built as `"string"`.

| Codec | Values | Default |
| --- | --- | --- |
| `bigint` | `"string"`, `"number"`, `"bigint"` | `"string"` |
| `numeric` | `"string"`, `"number"` | `"string"` |
| `timestamps` | `"temporal"` | `"temporal"` |

### bigint

`t.bigint()` and `t.identity()` share this codec. `"number"` is a safe integer. A value outside that range is OKM1210. See [bigint](/docs/schema/number-columns#bigint).

#### string

Omitted and `"string"` are a decimal string. The SQL value stays exact past a safe integer.

```ts title="bigint-string.ts"
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  cents: t.bigint(), // [!code focus]
});

export default schema({ tables: [payments] });
```

#### number

`"number"` is a safe integer. The column passes the same `as`.

```ts title="bigint-number.ts"
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity({ as: "number" }), // [!code focus]
  cents: t.bigint({ as: "number" }), // [!code focus]
});

export default schema({
  tables: [payments],
  codecs: { bigint: "number" }, // [!code focus]
});
```

#### native

`"bigint"` is a JavaScript bigint.

```ts title="bigint-native.ts"
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity({ as: "bigint" }),
  cents: t.bigint({ as: "bigint" }), // [!code focus]
});

export default schema({
  tables: [payments],
  codecs: { bigint: "bigint" }, // [!code focus]
});
```

<Callout type="error" title="OKM1060">
A bare column under `"number"` or `"bigint"` is `Schema codecs.bigint is number, and payments.cents was built as string. Set as: "number" on that column.` Any other schema value is `schema() codecs.bigint date must be string, number, or bigint.`
</Callout>

### numeric

`t.numeric()` is exact. `{ as: "number" }` can lose digits past a JavaScript number. See [numeric](/docs/schema/number-columns#numbers).

#### string

Omitted and `"string"` keep every digit.

```ts title="numeric-string.ts"
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  amount: t.numeric(12, 2), // [!code focus]
});

export default schema({ tables: [payments] });
```

#### number

`"number"` needs `{ as: "number" }` on the column. Precision and scale stay the SQL type.

```ts title="numeric-number.ts"
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  amount: t.numeric(12, 2, { as: "number" }), // [!code focus]
});

export default schema({
  tables: [payments],
  codecs: { numeric: "number" }, // [!code focus]
});
```

<Callout type="error" title="OKM1060">
A bare column under `"number"` is `Schema codecs.numeric is number, and payments.amount was built as string. Set as: "number" on that column.` Any other schema value is `schema() codecs.numeric bigint must be string or number.`
</Callout>

### timestamps

`"temporal"` is the only value. Omitting it is the same. A time column has no `as`. `t.timestamptz()` is `Temporal.Instant`. See [time](/docs/schema/time-columns#time).

```ts title="timestamps.ts"
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity(),
  paidAt: t.timestamptz(), // [!code focus]
});

export default schema({
  tables: [payments],
  codecs: { timestamps: "temporal" }, // [!code focus]
});
```

<Callout type="error" title="OKM1060">
Any other value is `schema() codecs.timestamps date must be temporal.`
</Callout>

### together

One schema can set every codec. Each bigint column matches `"bigint"`. The numeric column matches `"number"`.

```ts title="schema.ts"
import { schema, table, t } from "okmodel/pg";

const payments = table("payments", {
  id: t.identity({ as: "bigint" }),
  cents: t.bigint({ as: "bigint" }), // [!code focus]
  amount: t.numeric(12, 2, { as: "number" }), // [!code focus]
  paidAt: t.timestamptz(),
});

export default schema({
  tables: [payments],
  codecs: {
    bigint: "bigint", // [!code focus]
    numeric: "number", // [!code focus]
    timestamps: "temporal", // [!code focus]
  },
});
```

## Row types

`types` is stored on the schema. Queries still infer a row from `schema()`. `okm build` writes `.okm/types.d.ts` for either value, and `okm generate` writes that same file. The header is `Mode: emitted` or `Mode: inferred`. The interfaces do not change with the mode.

A table named `notes` becomes `Notes`, `NotesInsert`, and `NotesUpdate`. The file then maps those names on `Rows`, `Inserts`, and `Updates`, in the order of the [tables](/docs/schema/the-schema#tables) list. Two names that emit one interface are refused there.

`t.jsonb<{ published?: boolean }>()` keeps that argument in the file. The type parameter is erased at runtime, so the text is read from the schema source. A bare `t.jsonb()` is `jsonb<unknown>`. When either form appears, the file starts with `export type jsonb<T> = T`. `t.json<T>()` is the same, with `json`.

<Callout type="info" title="Not the catalog">
This flag is not part of the catalog hash. Swapping `"emitted"` and `"inferred"` does not change it.
</Callout>

### emitted

Omitted means `"emitted"`. This is the mode a file imports when it should not re-check the schema.

```ts title="schema.ts"
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
});

export const app = schema({
  tables: [notes],
});

export const mode = app.types;
```

`"emitted"` stores the same value.

```ts title="schema.ts"
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
});

export const app = schema({
  tables: [notes],
  types: "emitted", // [!code focus]
});

export const mode = app.types;
```

### inferred

`"inferred"` stores that mode. The client and `Row`, `Insert`, and `Update` still read the schema. Use this when the checker should see the schema, not the written interfaces.

```ts title="schema.ts"
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
});

export const app = schema({
  tables: [notes],
  types: "inferred", // [!code focus]
});

export const mode = app.types;
```

### Types together

Two tables, a typed `jsonb`, and the default mode. `okm build` writes `Notes` and `Comments`, each with an insert and an update, then `Rows`, `Inserts`, and `Updates`.

```ts title="schema.ts"
import { schema, table, t } from "okmodel/pg";

const notes = table("notes", {
  id: t.identity(),
  title: t.text(),
  meta: t.jsonb<{ published?: boolean }>(), // [!code focus]
});

const comments = table("comments", {
  id: t.identity(),
  noteId: t.bigint().references("notes"),
  body: t.text(),
});

export default schema({
  tables: [notes, comments],
});
```

<Callout type="warning" title="OKM1060">
Any other value is `schema() types local must be emitted or inferred.` The type only allows `"emitted"` and `"inferred"`, so this is also a type error.
</Callout>

| Next | You use it for |
| --- | --- |
| [Required rows](/docs/schema/required-rows) | Rows inserted when the key is missing. |

Index: [/llms.txt](/llms.txt)
