Standards Docs
Tutorial — Build a CRM

Chapter 2 — backend

Wire a NestJS API with SchemaModule and the Supabase adapter.

The backend is a NestJS app that mounts SchemaModule from @stndrds/adapter-nestjs and wires a Supabase database adapter. The schema package from chapter 1 is consumed as a workspace dependency.

Install backend packages

pnpm add @nestjs/common @nestjs/core @nestjs/config @stndrds/adapter-nestjs @stndrds/adapter-supabase @stndrds/adapter-meilisearch @supabase/supabase-js

@stndrds/adapter-meilisearch is required because the Supabase adapter mandates a search adapter for full-text indexing. For local development you can run Meilisearch with Docker.

Wire the SchemaModule

Create src/modules/schema/schema.module.ts. The snippet below keeps only the Supabase and Meilisearch wiring — all other optional adapters are omitted.

// @noverify
import { Module } from "@nestjs/common";
import { SchemaModule as StandardSchemaModule } from "@stndrds/adapter-nestjs";
import { MeilisearchSearchAdapter } from "@stndrds/adapter-meilisearch";
import { SupabaseDatabaseAdapter } from "@stndrds/adapter-supabase";
import { createClient } from "@supabase/supabase-js";
import { registry } from "@crm-tutorial/schema";

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!,
);

const search = new MeilisearchSearchAdapter({
  host: process.env.MEILISEARCH_HOST!,
  apiKey: process.env.MEILISEARCH_API_KEY!,
  indexPrefix: "crm",
});

export const SchemaModule = StandardSchemaModule.forRoot({
  registry,
  adapter: new SupabaseDatabaseAdapter(supabase, { search }),
  autoSync: true,
  global: true,
});

autoSync: true reconciles your object definitions with the database on startup. global: true makes SchemaModule providers available throughout the app without re-importing.

Mount in the app

Import SchemaModule in your root AppModule:

// @noverify
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { SchemaModule } from "./modules/schema/schema.module";

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    SchemaModule,
  ],
})
export class AppModule {}

Env vars

The minimum required variables for this tutorial:

  • SUPABASE_URL — your Supabase project URL
  • SUPABASE_SERVICE_ROLE_KEY — service-role key (server-only, never expose to the client)
  • MEILISEARCH_HOST — Meilisearch instance URL (e.g. http://localhost:7700)
  • MEILISEARCH_API_KEY — Meilisearch master key

For production wiring with all five adapters (Redis cache, BullMQ queue, E2B sandbox), see the NestJS backend guide.

Auth

A real app needs an auth guard. standards-platform uses a custom Supabase JWT guard for user-facing requests and ApiKeyAuthGuard from @stndrds/adapter-nestjs for service-to-service calls. Pick the scheme that fits your app; the full setup is covered in the NestJS backend guide.

Next: Chapter 3 — views.

On this page