Skip to main content
@personaai/nestjs is a NestJS adapter for the Persona runtime — a dynamic module, an injectable service, and middleware that let you mount the entire runtime surface (AG-UI streaming chat, threads, files, memory, MCP OAuth, health) in a NestJS application without writing any SSE forwarding, protocol parsing, or thread-CRUD plumbing. It’s a thin translation layer: all logic lives in the runtime. If you need a raw call the adapter doesn’t cover, drop down to @personaai/sdk via PersonaService.client.
Server-side only. The credential you configure is a server-side secret — never bundle it into a browser app.

Prerequisites

  • Node.js 18+ and NestJS 10 or 11 (peer dependencies: @nestjs/common ^10 || ^11, @nestjs/core ^10 || ^11)
  • A Project credential shaped "<keyId>.<secret>", minted in Developer Studio
  • An Agent to talk to (create one in Studio, or via the SDK’s agents.create)

Install

The package ships ESM and CommonJS. Dependencies: @personaai/runtime and @personaai/sdk (installed automatically). Peer dependencies: @nestjs/common, @nestjs/core, and optionally reflect-metadata and rxjs.

Register the module

Add PersonaModule.forRoot() to your root module:
That’s it. The Persona runtime surface is now mounted at /api/persona/* (the default routePrefix).

Change the mount path

Both routePrefix and mountPath are accepted — routePrefix is preferred for clarity. The middleware is configured to match ${routePrefix}/* automatically.

Use the service

Inject PersonaService in any controller or service to interact with the Persona API programmatically:

PersonaService properties

PersonaService.forUser(externalUserId)

Creates a new PersonaClient scoped to a specific end-user. Use this for threads, chat, files, and memory operations that need user context:

Async module configuration

For dependency injection of config services (e.g. ConfigService), use forRootAsync:

forRootAsync options

A class-based factory:

What you get

Every route the runtime exposes is available at ${routePrefix}/*: The complete route table is in the Runtime routes reference.

Middleware and body parsers

The PersonaMiddleware translates NestJS requests to the runtime’s framework-neutral contract. It:
  • Reads req.originalUrl for path and query parameters
  • Reads req.method for HTTP method
  • Reads req.headers for headers
  • Handles multipart requests natively (via req.readable stream) or from pre-parsed req.file/req.files (multer)
  • Falls back to reading req.body if a JSON body parser already ran
Important: If you’re using multer or another body parser, mount it on the same route prefix as the adapter so the middleware sees the parsed body. If a body parser runs and discards the multipart body (e.g. express.json() on a multipart request), the adapter responds 400 INVALID_REQUEST.

Lifecycle hooks

All eight hooks (beforeRun, afterRun, onError, beforeToolCall, afterToolCall, onFileUpload, onThreadCreate, onMemoryWrite) pass straight through to the runtime — see the Runtime Hooks reference. Configure them on PersonaModule.forRoot():

Clean shutdown

PersonaService implements OnModuleDestroy and calls runtime.close() automatically when the NestJS app shuts down. This stops the runtime’s internal eviction timer. No manual cleanup needed.

Next steps

  • Auth & user resolution — the two resolver patterns in depth.
  • Routes — every route the middleware exposes.
  • Uploads — multipart parsing and multer coexistence.
  • Streaming & disconnects — SSE, backpressure, reconnect, teardown.
  • Build a chat UI with the raw SDK’s chat.stream and the AG-UI events it emits (see the SDK chat guide), or drop in pre-built components with @personaai/react and @personaai/ui.
  • Drop to @personaai/sdk for any call the adapter doesn’t cover.