standards
SDK

SDK

Build on standards with the TypeScript SDK — schema in code, NestJS backend, React frontend.

The standards SDK is a set of TypeScript packages for building applications where the schema lives in code. You declare each object once with a fluent builder. The API, validation, permissions, search, and UI views all derive from that one definition, with full type inference end to end.

Packages

PackageRole
@stndrds/schemaSchema definition: objects, attributes, views, forms, type inference. Framework-agnostic.
@stndrds/adapter-nestjsNestJS integration: the SchemaModule, REST controllers, triggers, backend data access, guards and decorators.
@stndrds/reactReact hooks over the REST API — TanStack Query under the hood.
@stndrds/uiReact components (record tables, forms, pickers) and the StandardsAppProvider app shell.
@stndrds/runtimeCore runtime services shared by the adapters.
@stndrds/adapter-supabase / -meilisearch / -bullmq / -redisInfrastructure adapters: database, search, background jobs, cache.

Project layout

Every integration follows the same layout: a monorepo where the schema lives in its own shared package, imported by both the backend and the frontend.

apps/
  web/          # Next.js frontend — @stndrds/react + @stndrds/ui
  api/          # NestJS backend — @stndrds/adapter-nestjs + infra adapters
packages/
  schema/       # your objects, views, forms — @stndrds/schema only

The packages/schema package has a single job: define every object with the fluent builders, add each one to a registry — the catalog the backend and frontend both read from — and export it along with the inferred types. Because it depends on nothing framework-specific, both apps can import it.

Install

Install the packages each workspace of your monorepo needs:

# packages/schema — the shared schema package
pnpm add @stndrds/schema

# apps/api — the NestJS backend, plus the adapters you use
pnpm add @stndrds/adapter-nestjs @stndrds/runtime @stndrds/schema @stndrds/adapter-supabase

# apps/web — the frontend
pnpm add @stndrds/react @stndrds/ui @stndrds/schema @tanstack/react-query

Swap the infrastructure adapters for the ones you actually run — the list in the table above is not a requirement.

Schema definition

An object is a fluent builder chain — name it, label it, add attributes:

// packages/schema/src/objects/contact.ts
import { object, text } from "@stndrds/schema";

export const CONTACT = object({ name: "contacts", label: "Contact" })
  .labelExpression("{{ firstName }} {{ lastName }}")
  .attribute(text({ name: "firstName", label: "First Name" }).required())
  .attribute(text({ name: "lastName", label: "Last Name" }).required());

Then register it so the rest of the platform can see it:

// packages/schema/src/registry.ts
import { registry } from "@stndrds/schema";
import { CONTACT } from "./objects/contact";

registry.register(CONTACT);

Server setup

Wire the SchemaModule into your NestJS app. The minimal setup takes a database adapter and the registries; everything else is opt-in:

import { SchemaModule } from "@stndrds/adapter-nestjs";
import { SupabaseDatabaseAdapter } from "@stndrds/adapter-supabase";
import { formRegistry, registry, viewRegistry } from "@stndrds/schema";

@Module({
  imports: [
    SchemaModule.forRoot({
      adapter: new SupabaseDatabaseAdapter(supabase),
      registry,
      viewRegistry,
      formRegistry,
      sync: { views: true, forms: true }, // sync code-defined schema to the database at startup
      global: true,
    }),
  ],
})
export class AppModule {}

The same options object accepts optional capabilities — add them only when you need them:

  • search — full-text search via the Meilisearch adapter, with index reconciliation.
  • cache — Redis caching.
  • async — the unified async engine: background jobs (via BullMQ) and outbox-backed domain events.
  • triggersserver-side code on record lifecycle events.
  • agents, auth, featureFlags — agent runtime, auth integration, feature flags.
  • tenant — multi-tenancy mode and headers (a tenant is a workspace).

With sync enabled, the app syncs registered objects, views, and forms to the database at boot. Additive changes need no migration scripts. Structural changes go through migrations.

Client setup

Two ways to wire the frontend, depending on how much UI you want from the platform.

StandardsAppProvider from @stndrds/ui is the batteries-included setup — this is what real integrations use. One provider builds the query client, the realtime connection, and the API client. It also refreshes auth tokens and supplies the platform's UI components — modals, file viewers, icons. It reads auth from your Supabase client:

// apps/web/src/providers/schema-provider.tsx
import { createSupabaseAuthBridge } from "@stndrds/react";
import { StandardsAppProvider } from "@stndrds/ui";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(supabaseUrl, supabaseAnonKey);
const auth = createSupabaseAuthBridge(supabase);

export function SchemaProvider({ children }: { children: React.ReactNode }) {
  return (
    <StandardsAppProvider
      config={{
        apiUrl: "https://api.standards.new/v1",
        auth,
      }}
    >
      {children}
    </StandardsAppProvider>
  );
}

With this setup your frontend authenticates as the signed-in user, exactly like the standards app itself — API keys stay on the server. Other config options you may need:

  • initialTenantId — workspace used for the first request. Only relevant in multi-tenant setups.
  • translations — i18n overrides, deep-merged. A French bundle ships in @stndrds/react/locales/fr.
  • realtime — live record sync over socket.io. On by default.
  • queryClient — reuse your app's existing TanStack QueryClient.
  • ui — replace the built-in modals, icons, and file viewers with your own components.

Headless

If you build the entire UI yourself, wrap the app in SchemaClientProvider from @stndrds/react instead and inject auth through headers or a custom fetch. (Here the API URL splits into baseUrl + apiPrefix — the app shell's apiUrl is the two joined.)

import { SchemaClientProvider } from "@stndrds/react";

<SchemaClientProvider
  config={{
    baseUrl: "https://api.standards.new",
    apiPrefix: "/v1",
    headers: { Authorization: `Bearer ${accessToken}` },
  }}
>
  <App />
</SchemaClientProvider>;

SchemaClientProvider bundles its own query client; pass the queryClient prop to share your app's — see Provider setup for the full config surface.

Either way, the React hooks cover records, search, schema introspection, documents, files, users, and permissions.

Typed end to end

Types flow from the schema definition to your React components — no codegen step:

import type { ExtractRecord } from "@stndrds/schema";
type Contact = ExtractRecord<typeof CONTACT>;
const { data } = useRecords<Contact>("contacts");
// data.data[0].values.firstName — string, inferred

Next steps

  • Objects — the object builder, type inference, registries, migrations.
  • Attributes — all 16 attribute types and their methods.
  • Views — detail and list view builders.
  • Relations & documents — qualified edges, bilateral sync, and file packs.
  • Forms — multi-step forms that write into one or more records.
  • Triggers — react to record lifecycle events on the server.
  • Backend data access — the injected, typed query builder for server code.
  • React hooks — fetch and mutate data from the frontend.
  • SchemaClient — the imperative client behind the hooks, for scripts and server code.
  • Migrations — safe, destructive, and ambiguous schema changes at boot.