Putnami
DocsGitHub

Licensed under FSL-1.1-MIT

Getting Started
Concepts
How To
Build A Web App
Build An Api Service
Share Code Between Projects
Configure Your App
Add Persistence
Add Authentication
Add Background Jobs
Develop With Ai
Structure Business Logic With Di
Upgrade Putnami
Principles
Tooling & Workspace
Workspace
Cli
Jobs & Caching
Extensions
Templates
Error Handling
Frameworks
Typescript
ExtensionOverviewWebReact RoutingForms And ActionsStatic FilesApiErrors And ResponsesConfigurationLoggingHttp And MiddlewareDependency InjectionPlugins And LifecycleSessionsAuthPersistenceEventsStorageCachingWebsocketsTestingHealth ChecksTelemetryProto GrpcSmart ClientSchema
Go
ExtensionOverviewHttpDependency InjectionPlugins And LifecycleConfigurationSecurityPersistenceErrorsEventsStorageCachingLoggingTelemetryGrpcService ClientsValidationOpenapiTesting
Python
Extension
Platform
Ci
  1. DocsSeparator
  2. FrameworksSeparator
  3. TypescriptSeparator
  4. Schema

Schema & Validation

The schema module (@putnami/runtime) provides type-safe schema definitions with compile-time inference and runtime validation. Schemas are used across the framework for endpoint bodies, query parameters, configuration, and domain models.

Defining a Schema

Use native constructors (String, Number, Boolean) and schema helpers to describe the shape of your data:

import { schema, Optional, Int, Email, OneOf, ArrayOf } from '@putnami/runtime';

const TaskSchema = schema({
  title: String,
  description: Optional(String),
  priority: Int,
  status: OneOf('todo', 'in_progress', 'done'),
  assignees: ArrayOf(Email),
});

The schema() helper preserves full type inference — InferSchema<typeof TaskSchema> produces the equivalent TypeScript type automatically.

Primitives

Schema TypeScript type Validation
String string Must be a string
Number number Must be a number
Boolean boolean Must be a boolean

Built-in Types

Schema TypeScript type Validation
Uuid string UUID v4 format
Email string Basic email format
Int number Integer (no decimals)
Url string Valid URL
DateIso string ISO 8601 date (2024-01-15 or 2024-01-15T10:30:00Z)

Constraints

Numeric and string constraints restrict values beyond their base type:

import { Min, Max, MinLength, MaxLength, Pattern, OneOf, Constrained } from '@putnami/runtime';

const PasswordSchema = schema({
  username: MinLength(3),
  password: Constrained(MinLength(8), MaxLength(128)),
  age: Constrained(Min(0), Max(150)),
  role: OneOf('admin', 'editor', 'viewer'),
  code: Pattern(/^[A-Z]{3}-\d{4}$/),
});
Constraint Applies to Description
Min(n) number Value must be >= n
Max(n) number Value must be <= n
MinLength(n) string Length must be >= n
MaxLength(n) string Length must be <= n
Pattern(regex) string Must match the regex
OneOf(...values) string Must be one of the listed values
Constrained(...descriptors) any Combine multiple constraints

Collections

import { ArrayOf, MapOf, Optional } from '@putnami/runtime';

const Schema = schema({
  tags: ArrayOf(String),                // string[]
  scores: ArrayOf(Int),                 // number[]
  metadata: MapOf(String, String),      // Record<string, string>
  optionalList: Optional(ArrayOf(Uuid)),// string[] | undefined
});

MapOf(keyType, valueType) accepts scalar key types (String, Number, Boolean, Int).

Nested Objects

Schemas can contain nested object schemas:

const AddressSchema = schema({
  street: String,
  city: String,
  zip: Pattern(/^\d{5}$/),
});

const UserSchema = schema({
  name: String,
  address: AddressSchema,
});

Optional & Defaults

import { Optional, Default } from '@putnami/runtime';

const ConfigSchema = schema({
  host: Default(String, 'localhost'),  // string (defaults to 'localhost')
  port: Default(Number, 3000),         // number (defaults to 3000)
  debug: Optional(Boolean),            // boolean | undefined
});

Default makes the field optional in input but always present in the validated output.

Environment & Secrets

For configuration schemas, bind values to environment variables or async resolvers:

import { Env, Resolve, Sensitive } from '@putnami/runtime';

const DbConfig = schema({
  host: Env('DB_HOST', String),
  port: Env('DB_PORT', Int),
  password: Sensitive(Env('DB_PASSWORD', String)),
  token: Resolve(() => secretManager.getSecret('db-token'), String),
});
Helper Purpose
Env(varName, type) Read from environment variable
Resolve(fn, type) Resolve asynchronously at bootstrap
Sensitive(type) Redact value in validation errors
Desc(text, type) Add description for documentation / OpenAPI

Runtime Validation

Use validateSchema() to validate data at runtime:

import { validateSchema } from '@putnami/runtime';

const result = validateSchema(TaskSchema, requestBody);

if (result.errors.length > 0) {
  // result.errors: Array<{ field: string, message: string }>
  return new Response(JSON.stringify({ errors: result.errors }), { status: 400 });
}

// result.data is validated and typed
const task = result.data;

Coercion

When validating URL parameters or query strings (which are always strings), enable coercion:

const result = validateSchema(MySchema, queryParams, { coerce: true });

With coerce: true, string values are automatically converted to numbers and booleans where the schema expects them.

Framework Integration

Schemas are used directly in endpoint definitions — validation happens automatically:

import { endpoint } from '@putnami/application';
import { Int, OneOf, Optional } from '@putnami/runtime';

export const PUT = endpoint()
  .params({ id: String })
  .body({
    title: Optional(String),
    status: Optional(OneOf('todo', 'in_progress', 'done')),
    priority: Optional(Int),
  })
  .handle(async (ctx) => {
    const body = await ctx.body(); // fully typed and validated
    // body.status is 'todo' | 'in_progress' | 'done' | undefined
  });

The same schema types are used for:

  • Endpoint params, query, and body — validated on each request
  • Configuration (useConfig) — validated at startup
  • Domain models — shared type definitions with runtime checks

On this page

  • Schema & Validation
  • Defining a Schema
  • Primitives
  • Built-in Types
  • Constraints
  • Collections
  • Nested Objects
  • Optional & Defaults
  • Environment & Secrets
  • Runtime Validation
  • Coercion
  • Framework Integration