Pema and Stores
A Primate Store schema needs both validation behavior and a persistent datatype. Pema calls a schema with that contract Storable.
import db from "@/config/db";
import p from "pema";
import store from "primate/store";
export default store({
table: "article",
db,
schema: {
id: store.key.primary(p.uuid),
title: p.string.max(200),
published: p.boolean.default(false),
tags: p.json(p.array(p.string)),
},
});Pema parses and validates each value before a Store write. The database adapter
uses each field's datatype to map it to its own storage representation.
Compatibility
| Schema family | Direct Store field? | Datatype |
|---|---|---|
p.string |
Yes | string |
p.boolean |
Yes | boolean |
| Number and integer families | Yes | f32, f64, signed/unsigned widths |
p.date |
Yes | datetime |
p.url |
Yes | url |
p.uuid, .v4(), .v7() |
Yes | UUID datatype for that form |
p.blob, p.file |
Yes | blob |
p.enum(...) |
Yes | u8 |
p.json(...) |
Yes | json |
optional() / default() |
If the inner schema is Storable | Inner datatype |
| Object, partial, omit and async schemas | No | Use p.json(inner) if appropriate |
| Array, tuple, record and dictionary | No | Use p.json(inner) if appropriate |
| Union, literal, predicate and constructor | No | Validation-only |
| Pure, unknown, null, undefined, function, symbol | No | Validation-only |
derive() result |
No | Validation-only |
Do not infer Store suitability merely because a schema has parse() or a useful
TypeScript type. OptionalType and DefaultType forward the inner datatype; a
wrapper around a validation-only schema is still unsuitable and can fail when a
Store asks for its datatype.
Structured JSON
Use p.json(inner) to validate structured JSON and persist it as one field:
const Metadata = p.json(p({
labels: p.array(p.string),
featured: p.boolean.default(false),
}));The parsed output must be JSON-compatible. p.json does not turn Date, URL,
bigint or arbitrary class instances into JSON values.
Identifiers
Primate Store keys support unsigned integers, UUIDs and manually supplied strings:
id: store.key.primary(p.u32)
id: store.key.primary(p.uuid)
id: store.key.primary(p.uuid.v4())
id: store.key.primary(p.string, { generate: false })Generic p.uuid and explicit p.uuid.v7() generate UUIDv7 keys; explicit
p.uuid.v4() generates UUIDv4. See Primary keys for generation and foreign
key rules.
Integer sequences
Use .sequence() when a Store field needs a persistent, increasing unsigned
integer:
const Event = store({
table: "event",
db,
schema: {
id: store.key.primary(p.uuid),
number: p.u32.sequence(),
},
});Sequences are available on p.u8, p.u16, p.u32, p.u64 and p.u128.
The field is optional when inserting but required on the returned and stored
record. Ordinary Pema parsing still requires a value: .sequence() is Store
metadata, not a context-free default.
Generated values begin at 1. The Store keeps a persistent high-water mark,
allocates batches in input order and coordinates concurrent writers so generated
values do not collide. Deleting a row never makes its value available again.
Allocation may leave gaps, including after an insert fails, so do not interpret
gaps as missing records. A sequence is unsuitable for legally gapless invoice,
receipt or similar numbering.
An explicit value remains valid input. A value above the high-water mark moves
the sequence forward; a lower value does not move it backward. Reaching the
unsigned type's upper bound rejects the next generated value instead of
wrapping. u64 and u128 sequence values remain exact bigint values.
Store-facing types
Pema exports lower-level types used by Store implementations and generic code:
| Type | Purpose |
|---|---|
Storable |
Parsed schema with a persistent datatype |
DataKey |
Union of supported datatype names |
DataType |
Datatype-to-JavaScript-value map |
StoreSchema |
Property map whose values are Storable |
StoreId<S> |
Parsed non-nullable id type |
InferStore<S> |
Parsed Store shape with undefined fields optional |
InferStoreOut<S> |
Store output with required parsed id |
Id |
String identifier helper |
pema/StoreType is the public Store-schema class entrypoint used by Primate. It
parses complete records and exposes partial() for partial record parsing;
applications normally receive Store types through primate/store rather than
constructing StoreType directly. Its module also exports the Serialized
type.
This page covers schema compatibility. Continue to Stores for primary and foreign keys, insert/update operations, atomic batches, relations, lifecycle, migrations and raw database access.