Nitrofy LogoNitrofy
Como funcionaBeneficiosIntegracoesPlanosDuvidas
Comece agora
Nitrofy Logo
Comecar
Introduction
QuickstartEnvironment SetupRunning LocallyFirst Deploy
Organizations & TenancyAuthentication & SessionsRoles & PermissionsControllers & ProceduresJobs & QueuesPlugin ManagerContent LayerBuilt-in MCP ServerBillingNotificationsFile StorageEmailWebhooksAPI KeysSEOData FetchingDesign System
Development

Controllers & Procedures

Learn how to design typed APIs with Igniter controllers, implement rigorous validation with Zod, and generate comprehensive OpenAPI documentation.

By the end of this guide, you'll understand how to create feature-scoped controllers, implement business logic through procedures, validate inputs with Zod schemas, and expose well-documented APIs through OpenAPI.

Before You Begin

Basic Knowledge: Familiarity with TypeScript, REST APIs, and basic Node.js concepts Prerequisites: Understanding of the Authentication & Sessions and Roles & Permissions concepts Environment: A running SaaS Boilerplate instance with database configured Optional: Experience with Zod for schema validation

Core Concepts

The SaaS Boilerplate uses Igniter as its API framework, providing a structured approach to building type-safe, well-documented APIs. Controllers and procedures work together to create a layered architecture that separates concerns while maintaining type safety throughout the application.

Controllers as Feature Boundaries

Controllers serve as the entry point for API endpoints within each feature. They are feature-scoped, meaning each business domain (leads, organizations, billing) has its own controller that groups related endpoints. Controllers handle:

  • Request Routing: Mapping HTTP methods and paths to handler functions
  • Input Validation: Using Zod schemas to validate and parse request data
  • Authentication Guards: Enforcing role-based access control
  • Response Formatting: Standardizing API responses
  • Procedure Orchestration: Coordinating calls to business logic procedures

Procedures as Business Logic Layer

Procedures encapsulate the business logic and data access operations. They are injected into the Igniter context, making them available to controllers while maintaining clean separation of concerns. Procedures handle:

  • Data Operations: CRUD operations on database entities
  • Business Rules: Domain-specific validation and logic
  • Side Effects: Notifications, integrations, and external service calls
  • Data Transformation: Converting between database and API formats
  • Error Handling: Business logic errors and edge cases

Schema-Driven Validation

Input validation is handled through Zod schemas that provide runtime type checking and automatic TypeScript inference. This ensures:

  • Type Safety: Compile-time guarantees about data structures
  • Runtime Validation: Automatic parsing and validation of request data
  • Documentation: Schemas generate OpenAPI documentation automatically
  • Developer Experience: IntelliSense and autocompletion in IDEs

OpenAPI Documentation

The framework automatically generates comprehensive OpenAPI documentation from your controllers and schemas. This provides:

  • API Discovery: Interactive documentation for developers
  • Type Generation: Client SDK generation for different languages
  • Testing: Built-in API testing interfaces
  • Contract Definition: Clear API contracts between frontend and backend

Data Models

The controller and procedure system defines several key interfaces and types that govern API structure and validation.

Prop

Type

A Practical Example

Let's explore how the Lead feature is implemented in the SaaS Boilerplate as a complete example of controllers and procedures working together. This feature demonstrates the full lifecycle of creating a business domain with proper validation, authentication, and organization scoping.

Feature Directory Structure

The Lead feature is organized under src/features/lead/ with the following structure:

lead.interface.ts
lead.controller.ts
lead.procedure.ts

Feature Interfaces

The lead interfaces define the data models and Zod validation schemas:

// src/features/lead/lead.interface.ts
// Feature interfaces define data models and validation schemas
export interface Lead {
  id: string
  email: string
  name: string | null
  phone: string | null
  metadata: any | null
  organizationId: string
  createdAt: Date
  updatedAt: Date
}

// Zod schemas for runtime validation
export const LeadCreationSchema = z.object({
  email: z.string().email('Invalid email format'),
  name: z.string().nullable().optional(),
  phone: z.string().nullable().optional(),
  metadata: z.any().optional().nullable(),
})

Business Logic Procedure

The LeadProcedure encapsulates all data operations and business rules:

// src/features/lead/procedures/lead.procedure.ts
export const LeadProcedure = igniter.procedure({
  name: 'LeadProcedure',
  handler: (_, { context }) => {
    return {
      lead: {
        findMany: async (organizationId: string): Promise<Lead[]> => {
          return context.services.database.lead.findMany({
            where: { organizationId },
          })
        },

        create: async (organizationId: string, data: CreateLeadBody): Promise<Lead> => {
          const lead = await context.services.database.lead.create({
            data: { ...data, organizationId },
          })

          // Business logic: Trigger notifications for new leads
          await context.services.notification.send({
            type: 'LEAD_CREATED',
            context: { organizationId },
            data: { leadName: lead.name, leadEmail: lead.email },
          })

          return lead
        },
      },
    }
  },
})

Feature Controller

The LeadController exposes RESTful API endpoints:

// src/features/lead/controllers/lead.controller.ts
export const LeadController = igniter.controller({
  name: 'Lead',
  path: '/leads',
  description: 'Manage customer leads.',
  actions: {
    list: igniter.query({
      name: 'List',
      description: 'List all leads for an organization.',
      path: '/',
      use: [AuthFeatureProcedure(), LeadProcedure()],
      query: LeadQuerySchema,
      handler: async ({ context, response }) => {
        const session = await context.auth.getSession({
          requirements: 'authenticated',
          roles: ['admin', 'owner', 'member'],
        })

        if (!session || !session.organization) {
          return response.unauthorized('Authentication required')
        }

        const leads = await context.lead.findMany(session.organization.id)
        return response.success(leads)
      },
    }),

    create: igniter.mutation({
      name: 'Create',
      description: 'Create a new lead.',
      path: '/',
      method: 'POST',
      use: [AuthFeatureProcedure(), LeadProcedure(), IntegrationFeatureProcedure()],
      body: LeadCreationSchema,
      handler: async ({ context, request, response }) => {
        const session = await context.auth.getSession({
          requirements: 'authenticated',
          roles: ['admin', 'owner', 'member'],
        })

        const lead = await context.lead.create(session.organization!.id, request.body)
        return response.created(lead)
      },
    }),
  },
})

Controller Registration

The LeadController is registered in the main application router:

// src/igniter.router.ts
export const AppRouter = igniter.router({
  controllers: {
    // SaaS Boilerplate controllers
    auth: AuthController,
    organization: OrganizationController,
    membership: MembershipController,

    // Custom feature controllers
    lead: LeadController,
    submission: SubmissionController,

    // ... other controllers
  },
})

Schema and Documentation Generation

The CLI commands generate TypeScript schemas and OpenAPI documentation:

# Generate TypeScript schema for type safety
npx @igniter-js/cli generate schema

# Generate OpenAPI documentation
npx @igniter-js/cli generate docs

Testing with Igniter Studio

Test the Lead endpoints through the interactive API documentation at http://localhost:3000/api/v1/docs.

Troubleshooting

Best Practices

Roles & Permissions

Learn how role-based access control (RBAC) works in the SaaS Boilerplate, including membership roles, permission validation, and organization-scoped access control.

Jobs & Queues

Background work with BullMQ adapters and patterns.

On this page

Before You BeginCore ConceptsControllers as Feature BoundariesProcedures as Business Logic LayerSchema-Driven ValidationOpenAPI DocumentationData ModelsA Practical ExampleFeature Directory StructureFeature InterfacesBusiness Logic ProcedureFeature ControllerController RegistrationSchema and Documentation GenerationTesting with Igniter StudioTroubleshootingBest Practices
Nitrofy LogoNitrofy

Automatize o envio e a cobrança dos seus contratos

© 2026 Nitrofy, All rights reserved