Skip to content

Models (DTOs)

model() returns a named, extendable schema-token class for contracts, services, and standalone parsing.

AI generated, pending review Updated 10 days ago · Flare 0.3

model() returns a ModelTokenBuilder<T>: an extendable class whose instances are typed as T, with the same safeParse and optional() surface as a schema() token. Static safeParse yields a plain object typed as T, not a class instance.

Use model() when a shape needs a named identifier (DTOs, contract types, shared test fixtures) instead of an anonymous schema({...}) value. Validation rules and wire parsing match schema() exactly.

CapabilityMeaning
Extendable classSubclass class User extends model({...}) {} or assign const User = model({...})
SchemaToken<T>Usable on contract body / response, nested in descriptors, and in safeParse
Static safeParseReturns a plain object on success; failures use the same FieldError[] paths as schema()
Static optional()Returns a SchemaToken<T> (not another model builder) for optional nested fields
Response serializationWhen the token is on a contract response entry, outbound plain objects serialize with schema-aware rules

The model class has no argument-taking constructor. new MyModel({ ... }) does not type-check, and new MyModel() returns an empty object, not a parsed one. Call MyModel.safeParse(input) instead.

model() has two public forms. It does not support the top-level array or record overloads that schema() accepts. Build those with schema([Item]) or schema([{ $record: Value }]) first, then pass the token to model(existingToken).

import { model, str, int, optional } from "@flare-ts/lib/schema";
class CreateUser extends model({
name: str.min(1),
email: str,
age: optional(int),
}) {}
import { schema, model, uuid, str } from "@flare-ts/lib/schema";
const UserSchema = schema({ id: uuid, name: str });
class UserModel extends model(UserSchema) {}

For top-level arrays or records:

const World = schema({ id: uuid, name: str });
class Worlds extends model(schema([World])) {}

model() has no union form. A class cannot carry a union instance type, so the extendable face model() exists for is unavailable to unions; passing a union-typed schema token to model() is a type error. Declare the union with schema(), and when you want one name in both the value and type positions, give the token and the type the same name:

import { schema, int, str } from "@flare-ts/lib/schema";
type Pet = { kind: "cat"; lives: number } | { kind: "dog"; breed: string };
const Pet = schema<Pet, "union">("kind", {
cat: { lives: int },
dog: { breed: str },
});
const cat = Pet.safeParse({ kind: "cat", lives: 9 });

Pet now works exactly like a model class name: value position (Pet.safeParse, contract entries) and type position (function feed(pet: Pet)). See Define a DTO.

Accepted input matches schema(): a JSON string, an ArrayBuffer (UTF-8 JSON from the HTTP pipeline), or a plain object / array JsonValue.

const result = CreateUser.safeParse(requestBody);
if (!result.success) {
// result.error.fields: FieldError[]
return;
}
const user = result.data; // plain object typed as CreateUser's shape

Returns an optional schema token for nesting:

const Envelope = schema({
user: UserModel.optional(),
});

For optional primitive fields, use optional(int) on the primitive, not on the model.

Models slot into httpContract body and response the same way as schema(...) tokens. Controller methods read parsed inbound values with this.ctx.extract(entry). The pipeline validates once before the handler runs; failures become 400 with field errors.

Outbound: declare the model on the contract response map and return a plain object via this.ok(body), this.created(body), or new FlareResponse(status, body). Flare serializes with the per-status schema. See Serialization and HTTP responses.

schema({...})model({...})
Runtime shapePlain token objectExtendable class + token
TypeScript nameInferred from variableClass name
extendsNot supportedSupported
Discriminated unionSupportedNo union form; use schema()
Top-level array / recordSupportedUse schema first, then model(token)
Contract body / responseYesYes
Manual parseToken.safeParse(raw)ModelClass.safeParse(raw)

Same validation rules and parsing. model() wraps a schema() token in an extendable class.