@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.
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
@personaai/runtime and @personaai/sdk (installed automatically). Peer dependencies: @nestjs/common, @nestjs/core, and optionally reflect-metadata and rxjs.
Register the module
AddPersonaModule.forRoot() to your root module:
/api/persona/* (the default routePrefix).
Change the mount path
routePrefix and mountPath are accepted — routePrefix is preferred for clarity. The middleware is configured to match ${routePrefix}/* automatically.
Use the service
InjectPersonaService 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
ThePersonaMiddleware translates NestJS requests to the runtime’s framework-neutral contract. It:
- Reads
req.originalUrlfor path and query parameters - Reads
req.methodfor HTTP method - Reads
req.headersfor headers - Handles multipart requests natively (via
req.readablestream) or from pre-parsedreq.file/req.files(multer) - Falls back to reading
req.bodyif a JSON body parser already ran
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.streamand the AG-UI events it emits (see the SDK chat guide), or drop in pre-built components with@personaai/reactand@personaai/ui. - Drop to
@personaai/sdkfor any call the adapter doesn’t cover.