Documentation
Documentation
Introduction

Getting Started

Getting StartedInstallationQuick StartProject Structure

Architecture

Architecture OverviewTech StacktRPC MiddlewareDesign Principles

Patterns

Code Patterns & ConventionsFeature ModulesError HandlingType Safety

Database

DatabaseSchema DefinitionDatabase OperationsMigrationsCaching

API

tRPCProceduresRouterstRPC Proxy Setup
APIsOpenAPIREST Endpoints

Auth & Access

AuthenticationConfigurationOAuth ProvidersRolesSession Management
AuthorizationUser RolesPermissions

Routing & i18n

RoutingDeclarative RoutingNavigation
InternationalizationTranslationsLocale Routing

Components & UI

ComponentsButtonsFormsNavigationDialogs
StylesTailwind CSSThemingTypography

Storage

StorageConfigurationUsageBuckets

Configuration

ConfigurationEnvironment VariablesFeature Flags

Templates

Template GuidesCreate New FeatureCreate New PageCreate Database TableCreate tRPC RouterAdd Translations

Development

DevelopmentCommandsAI AgentsBest Practices

OpenAPI

Expose tRPC procedures as REST endpoints

Overview

Expose tRPC procedures as REST endpoints using trpc-to-openapi. This allows external clients to consume your API without using tRPC.

Enabling OpenAPI

Add openapi metadata to a procedure to expose it as a REST endpoint:

import { authProcedure } from "@/trpc/procedures/trpc";
import { z } from "zod";

export const myRouter = createTRPCRouter({
  list: authProcedure
    .meta({
      rateLimit: "QUERY",
      openapi: {
        enabled: true,          // Required
        method: "GET",          // HTTP method
        path: "/my-resources",  // REST path
        tags: ["MyResource"],   // For grouping in docs
        summary: "List resources",
        description: "Returns all resources for the user",
        protect: true,          // Require authentication
      },
    })
    .input(z.object({}))
    .output(z.object({
      success: z.boolean(),
      payload: z.array(myResourceSchema),
    }))
    .query(async ({ ctx }) => {
      return {
        success: true,
        payload: await ctx.db.myResource.list(),
      };
    }),
});

OpenAPI procedures require explicit .input() and .output() schemas.

OpenAPI Metadata

Required Fields

FieldTypeDescription
enabledbooleanEnable OpenAPI exposure
methodstringHTTP method (GET, POST)
pathstringREST endpoint path
tagsarrayTags for grouping
summarystringShort description
protectbooleanRequire authentication

Optional Fields

FieldTypeDescription
descriptionstringDetailed description
deprecatedbooleanMark as deprecated

OpenAPI Tags

Define tags in src/trpc/tags.ts:

export const OpenAPITags = {
  MyResource: "My Resource",
  AnotherResource: "Another Resource",
  Admin: "Admin Operations",
} as const;

Tags help organize endpoints in the OpenAPI documentation.

Examples

GET Endpoint

list: authProcedure
  .meta({
    rateLimit: "QUERY",
    openapi: {
      enabled: true,
      method: "GET",
      path: "/api-keys",
      tags: ["API Keys"],
      summary: "List API keys",
      protect: true,
    },
  })
  .input(z.object({}))
  .output(z.object({
    success: z.boolean(),
    payload: z.array(apiKeySchema),
  }))
  .query(async ({ ctx }) => {
    return {
      success: true,
      payload: await ctx.db.apiKeys.list(),
    };
  })

POST Endpoint

create: authProcedure
  .meta({
    rateLimit: "MUTATION",
    openapi: {
      enabled: true,
      method: "POST",
      path: "/api-keys",
      tags: ["API Keys"],
      summary: "Create API key",
      protect: true,
    },
  })
  .input(z.object({
    name: z.string().min(1),
  }))
  .output(z.object({
    success: z.boolean(),
    message: z.string().optional(),
    payload: apiKeySchema,
  }))
  .mutation(async ({ ctx, input }) => {
    const result = await ctx.db.apiKeys.create(input);
    return {
      success: true,
      message: ctx.t("toasts.saved"),
      payload: result,
    };
  })

Public Endpoint

health: publicProcedure
  .meta({
    rateLimit: false,
    openapi: {
      enabled: true,
      method: "GET",
      path: "/health",
      tags: ["System"],
      summary: "Health check",
      protect: false, // No authentication required
    },
  })
  .input(z.object({}))
  .output(z.object({
    status: z.literal("ok"),
    timestamp: z.string(),
  }))
  .query(() => {
    return {
      status: "ok",
      timestamp: new Date().toISOString(),
    };
  })

Accessing OpenAPI

OpenAPI JSON Document

The OpenAPI specification is available at:

GET /api/openapi.json

REST Endpoints

All OpenAPI-enabled procedures are accessible at:

/api/<path>

For example:

  • GET /api/api-keys - List API keys
  • POST /api/api-keys - Create API key
  • GET /api/health - Health check

Swagger UI

You can add Swagger UI using third-party packages to visualize and test the API.

Authentication

Protected endpoints (protect: true) require authentication via Bearer token:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  https://your-app.com/api/api-keys

See REST Endpoints for authentication details.

Disabling OpenAPI

To disable all OpenAPI endpoints:

// src/config/app.ts
export const AuthConfig = {
  disableOpenAPI: true,
  // ...
};

Next Steps

REST Endpoints

tRPC Procedures

On this page

Overview
Enabling OpenAPI
OpenAPI Metadata
Required Fields
Optional Fields
OpenAPI Tags
Examples
GET Endpoint
POST Endpoint
Public Endpoint
Accessing OpenAPI
OpenAPI JSON Document
REST Endpoints
Swagger UI
Authentication
Disabling OpenAPI
Next Steps