trpc-rest

Extensions

Add custom OpenAPI extensions (x-*) to generated operations.

The extensions callback lets you add vendor-specific OpenAPI extensions (x-* fields) to each operation in the generated spec.

Basic usage

const doc = generateOpenApiDocument(appRouter, {
  // ...
  extensions: ({ meta, procedurePath, operationId }) => {
    return {
      'x-internal': meta.internal === true,
      'x-group': procedurePath[0],
    };
  },
});

Callback parameters

ParameterTypeDescription
metaOpenApiMetaThe full metadata object from .meta()
procedurePathstring[]Procedure path segments (e.g. ['runs', 'create'])
operationIdstringThe generated operation ID (e.g. runs-create)

Speakeasy SDK generation

A common use case is generating extensions for Speakeasy SDK generation:

extensions: ({ meta, procedurePath }) => {
  const ext: Record<string, unknown> = {};

  if (meta.openapi?.['x-speakeasy-group']) {
    ext['x-speakeasy-group'] = meta.openapi['x-speakeasy-group'];
  }

  ext['x-speakeasy-name-override'] = procedurePath[procedurePath.length - 1];

  return ext;
},

This produces SDK methods like client.runs.create() instead of client.runsCreate().

Custom metadata passthrough

Since OpenApiMeta supports an index signature, you can attach custom fields to .meta() and forward them as extensions:

// In your router
t.procedure.meta({
  openapi: { method: 'POST', path: '/runs' },
  xAccess: [
    { level: 'limited', authType: 'station' },
    { level: 'full', authType: 'user' },
  ],
});

// In the generator
extensions: ({ meta }) => {
  const xAccess = (meta as Record<string, unknown>).xAccess;
  return xAccess ? { 'x-access': xAccess } : {};
},

Returning undefined

If the callback returns undefined, no extensions are added to that operation.

On this page