Documentation
Documentation
Introduction

Getting Started

Getting started
Getting StartedInstallationQuick StartProject Structure

Configuration

Configuration
ConfigurationEnvironment ConfigurationEdge ConfigDatabaseAuth SecretStripeFirebaseStorageGoogle Maps And Cloud Service AccountOAuth ProvidersEmail DeliverySentryFeature Flags

Architecture

Architecture
Architecture OverviewTech StackoRPC MiddlewareDesign Principles

Patterns

Patterns
Code Patterns & ConventionsFeature ModulesError HandlingType Safety

Database

Database
DatabaseSetupSchema DefinitionDatabase OperationsMigrationsCaching
Data Tables

API

oRPCProceduresRoutersoRPC Proxy Setup
APIsOpenAPIREST Endpoints

Auth & Access

AuthenticationConfigurationOAuth ProvidersRolesSession Management
AuthorizationUser RolesPermissions

Routing & i18n

RoutingDeclarative RoutingNavigation
InternationalizationTranslationsLocale Routing

Components & UI

ComponentsButtonsFormsNavigationDialogs
StylesTailwind CSSThemingTypography

Storage

Storage
StorageConfigurationUsageBuckets
Stripe Billing

Extra

Caching

Templates

Templates
Template GuidesCreate New FeatureCreate New PageCreate Database TableCreate oRPC RouterAdd Translations

Development

Development
DevelopmentCommandsAI AgentsBest Practices
Pulling Updates

Routers

Organizing oRPC routers

Router Structure

Routers live in src/rpc/procedures/routers/ and are grouped by feature.

src/rpc/procedures/routers/
├── auth.ts
├── organizations.ts
├── members.ts
├── api-keys.ts
└── ...

Create a Router

// src/rpc/procedures/routers/my-feature.ts
import { authProcedure, createRPCRouter } from "@/rpc/procedures/rpc";
import { z } from "zod";

export const myFeatureRouter = createRPCRouter({
  list: authProcedure
    .meta({ rateLimit: "QUERY" })
    .query(async ({ ctx }) => {
      return { success: true, payload: await ctx.db.myFeature.list() };
    }),

  upsert: authProcedure
    .meta({ rateLimit: "MUTATION" })
    .input(z.object({ id: z.string().optional(), name: z.string() }))
    .mutation(async ({ ctx, input }) => {
      return { success: true, payload: await ctx.db.myFeature.upsert(input) };
    }),
});

Register in Root Router

// src/rpc/procedures/root.ts
import { createRPCRouter } from "@/rpc/procedures/rpc";
import { myFeatureRouter } from "@/rpc/procedures/routers/my-feature";

export const appRouter = createRPCRouter({
  myFeature: myFeatureRouter,
});

Best Practices

  • Keep routers feature-scoped.
  • Use shared schemas from src/features/<feature>/schema.ts.
  • Set rateLimit metadata explicitly.
  • Return ActionResponse for predictable client behavior.
  • Add .route(...) only for procedures you want exposed via REST/OpenAPI.

Next Steps

Procedures

OpenAPI

Create Router Template

On this page

Router Structure
Create a Router
Register in Root Router
Best Practices
Next Steps