Aviato / docs
Plugins & integrations

NestJS

Configure Aviato through Nest modules, injected plugins, and decorated service methods.

View Markdown

Nest modules, dependency injection and decorators for the Aviato TypeScript SDK. Requires NestJS 12, Node.js 22.22.1 or newer, and an existing Aviato agent. Installing this package does not install the agent.

npm install @getaviato/nestjs @getaviato/sdk

Ships ESM JavaScript and declarations. Node 22.22.1+ also supports loading the package from CommonJS Nest applications. Enable experimentalDecorators and emitDecoratorMetadata in your TypeScript configuration, as in a standard Nest app.

Configure once

import { Injectable, Module } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AviatoModule, AviatoAction, type ActionContext } from '@getaviato/nestjs';

@Injectable()
class CustomerActions {
    @AviatoAction({ collection: 'customers', name: 'Confirm selection', scope: 'single' })
    async confirm(context: ActionContext) {
        const records = await context.getRecords();
        return records.length === 1
            ? context.success('Customer selected')
            : context.error('Select one customer you can read');
    }
}

@Module({
    imports: [AviatoModule.forRoot({ secret: process.env.AVIATO_PLUGIN_SECRET! })],
    providers: [CustomerActions],
})
class AppModule {}

const app = await NestFactory.create(AppModule, { rawBody: true });
await app.listen(3000);

rawBody: true is required for signature verification. Keep Nest's built-in body parser enabled. Express and Fastify are supported. Their parser limits apply before the SDK's 10 MiB limit; configure them separately if your payloads exceed the host's defaults. Configure AVIATO_PLUGIN_URL=http://your-app:3000/aviato and the same AVIATO_PLUGIN_SECRET on the agent. A Nest global prefix is included in this URL: app.setGlobalPrefix('api') means /api/aviato.

Set path: 'internal/aviato' to change the controller mount. Paths contain literal segments, not route patterns. Async configuration uses the same static path option:

AviatoModule.forRootAsync({
    imports: [ConfigModule],
    inject: [ConfigService],
    path: 'internal/aviato',
    useFactory: (config: ConfigService) => ({
        secret: config.getOrThrow<string>('AVIATO_PLUGIN_SECRET'),
    }),
});

Import the root configuration exactly once. It exports a global AviatoPlugin provider. Register decorated classes in any feature module's providers; discovery runs at application bootstrap. Dependencies are ordinary Nest constructor injections.

Plugins through Nest providers

Use forFeature in your domain module to build integrations with injected configuration:

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AviatoModule, StripePlugin } from '@getaviato/nestjs';

@Module({
    imports: [AviatoModule.forFeature({
        imports: [ConfigModule],
        providers: [{
            provide: StripePlugin,
            inject: [ConfigService],
            useFactory: (config: ConfigService) =>
                new StripePlugin({
                    credentialEnv: config.get<string>('STRIPE_WIDGET_ENV') ?? 'STRIPE_WIDGET_KEY',
                    readOnly: true,
                }).invoices({
                    collection: 'customers',
                    name: 'billing',
                    customerId: { field: 'stripe_customer_id' },
                }),
        }],
        plugins: [StripePlugin],
    })],
})
export class BillingModule {}

plugins is an explicit list of provider tokens implementing IntegrationPlugin. Nothing is exposed merely because it is injectable. Each listed integration is registered through the underlying SDK's use() method. useValue, useClass, useFactory (including async factories), and useExisting are standard Nest providers.

For an existing plugin exported by another module, reuse its instance:

AviatoModule.forFeature({
    imports: [ExistingBillingModule], // exports BillingIntegration
    providers: [{ provide: 'AVIATO_BILLING', useExisting: BillingIntegration }],
    plugins: ['AVIATO_BILLING'],
});

You can also list BillingIntegration directly in plugins when the imported module exports it. Parent module providers are not implicitly visible inside forFeature; export them from an imported module or declare them in its providers option. To reuse a plugin elsewhere, own and export it from your billing module, then import that module into forFeature.

Construct the complete widget declaration in the constructor or factory, before registration. Registration snapshots and validates widgets; later builder mutations are not applied unless you explicitly call AviatoPlugin.use() again. Request-scoped plugin dependencies are rejected at startup. Multiple plugins follow Nest dependency initialization order, so use unique collection/name pairs across feature modules. The underlying SDK replaces widgets with the same key; do not rely on import order for overrides.

For prebuilt integrations, forRoot({ secret, plugins: [stripe] }) is also available. An integration implements widgets(); it does not install new provider execution code into the agent. The Stripe credential itself stays in the customer agent's environment. Configure a restricted key with Read/None permissions. Only its environment-variable name belongs in Nest configuration.

Decorators and the full SDK

DecoratorMethod argumentsReturns
@AviatoAction(options)ActionContextAction result or undefined
@AviatoComputedField(options)records, caller contextOne JSON value per record
@AviatoHook(options)hook contextRejection, changed values, or undefined
@AviatoSegment(options)caller contextFilter or record IDs
@AviatoSearch(options)query, caller contextFilter
@AviatoChart(options)filter, caller contextVega-Lite specification
@AviatoWriteOverride(options)value, record, caller contextRecord patch

Options match the SDK definition, excluding the decorated callback. Methods may return promises. One Aviato decorator is allowed per method. Duplicate decorated keys fail at startup, except hooks, which may compose. Handlers must be singleton providers with singleton dependencies; request-scoped and transient handlers are rejected. Symbol-named methods are not discovered; use ordinary named methods.

Inject AviatoPlugin for custom datasources, summaries, relation hints, dynamic forms, widgets and other SDK features. Register these in onModuleInit() before bootstrap. Use the core SDK's types and ui helpers from @getaviato/sdk. An instance method used as a dynamic form callback must be bound to its service when registered.

The HTTP controller participates in Nest's global guards, interceptors and filters. Allow the signed Aviato endpoint through any browser-session-only guard using your application's guard configuration. SDK signature verification remains mandatory. Decorated provider methods are ordinary service calls: controller pipes, method guards and interceptors are not automatically applied to them. Use ActionContext for the Aviato caller, not Nest's request-scoped REQUEST token. Direct database or service calls do not inherit Aviato record permissions; use context.getRecords() or context.data for governed access.

See the TypeScript SDK for the underlying APIs and Stripe widgets for agent-side credentials and permissions.

For customer support data, Zendesk widgets provide a typed ZendeskPlugin with user profiles, requested-ticket lists, and individual tickets. Its OAuth scopes are verified by the customer agent before each data request.

Edit on GitHub

On this page