본문으로 건너뛰기

커스텀 API 경로

기본적으로 Mastra는 서버를 통해 등록된 Agent와 Workflow를 자동으로 노출합니다. 추가 동작을 위해 자체 HTTP 경로를 정의할 수 있습니다.

경로는 @mastra/core/serverregisterApiRoute() 헬퍼로 정의합니다. 경로를 Mastra 인스턴스와 같은 파일에 둘 수도 있지만, 분리하면 구성을 간결하게 유지하는 데 도움이 됩니다.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { registerApiRoute } from '@mastra/core/server'

export const mastra = new Mastra({
server: {
apiRoutes: [
registerApiRoute('/my-custom-route', {
method: 'GET',
handler: async c => {
const mastra = c.get('mastra')
const agent = await mastra.getAgent('my-agent')

return c.json({ message: 'Custom route' })
},
}),
],
},
})

등록되면 서버 루트에서 사용자 지정 경로에 액세스할 수 있습니다. 예를 들어:

curl http://localhost:4111/my-custom-route

각 경로의 핸들러는 Hono Context를 받습니다. 핸들러 내에서 Mastra 인스턴스에 접근하여 Agent와 Workflow를 가져오거나 호출할 수 있습니다.

미들웨어
미들웨어에 대한 직접 링크

경로별 미들웨어를 추가하려면 registerApiRoute()를 호출할 때 middleware 배열을 사용하세요.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { registerApiRoute } from '@mastra/core/server'

export const mastra = new Mastra({
server: {
apiRoutes: [
registerApiRoute('/my-custom-route', {
method: 'GET',
middleware: [
async (c, next) => {
console.log(`${c.req.method} ${c.req.url}`)
await next()
},
],
handler: async c => {
return c.json({ message: 'Custom route with middleware' })
},
}),
],
},
})

OpenAPI 문서
OpenAPI 문서에 대한 직접 링크

사용자 지정 경로에는 Mastra 서버 경로와 함께 Swagger UI에 표시되는 OpenAPI 메타데이터를 포함할 수 있습니다. 사용자 지정 경로와 기본 제공 경로가 모두 나열되는 /api/openapi.json에서 OpenAPI 사양에 접근할 수 있습니다. 표준 OpenAPI 작업 필드가 포함된 openapi 옵션을 전달하세요.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { registerApiRoute } from '@mastra/core/server'
import { z } from 'zod'

export const mastra = new Mastra({
server: {
apiRoutes: [
registerApiRoute('/items/:itemId', {
method: 'GET',
openapi: {
summary: 'Get item by ID',
description: 'Retrieves a single item by its unique identifier',
tags: ['Items'],
parameters: [
{
name: 'itemId',
in: 'path',
required: true,
description: 'The item ID',
schema: { type: 'string' },
},
],
responses: {
200: {
description: 'Item found',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
},
},
},
},
},
404: {
description: 'Item not found',
},
},
},
handler: async c => {
const itemId = c.req.param('itemId')
return c.json({ id: itemId, name: 'Example Item' })
},
}),
],
},
})

Zod 스키마 사용
Zod 스키마 사용에 대한 직접 링크

OpenAPI 문서를 생성할 때 openapi 구성의 Zod 스키마는 JSON Schema로 변환됩니다.

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { registerApiRoute } from '@mastra/core/server'
import { z } from 'zod'

const ItemSchema = z.object({
id: z.string(),
name: z.string(),
price: z.number(),
})

const CreateItemSchema = z.object({
name: z.string().min(1),
price: z.number().positive(),
})

export const mastra = new Mastra({
server: {
apiRoutes: [
registerApiRoute('/items', {
method: 'POST',
openapi: {
summary: 'Create a new item',
tags: ['Items'],
requestBody: {
required: true,
content: {
'application/json': {
schema: CreateItemSchema,
},
},
},
responses: {
201: {
description: 'Item created',
content: {
'application/json': {
schema: ItemSchema,
},
},
},
},
},
handler: async c => {
const body = await c.req.json()
return c.json({ id: 'new-id', ...body }, 201)
},
}),
],
},
})

Swagger UI에서 보기
Swagger UI에서 보기에 대한 직접 링크

개발 모드(mastra dev) 또는 빌드 옵션에서 swaggerUI: true로 설정한 경우 사용자 지정 경로가 /swagger-ui의 Swagger UI에 표시됩니다.

export const mastra = new Mastra({
server: {
build: {
swaggerUI: true, // Enable in production builds
},
apiRoutes: [
// Your routes...
],
},
})

입증
입증에 대한 직접 링크

Mastra 서버에 인증이 구성되면 사용자 정의 API 경로에는 기본적으로 인증이 필요합니다. 경로를 공개적으로 접근 가능하게 하려면 다음을 설정하십시오.requiresAuth: false:

src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { registerApiRoute } from '@mastra/core/server'
import { MastraJwtAuth } from '@mastra/auth'

export const mastra = new Mastra({
server: {
auth: new MastraJwtAuth({
secret: process.env.MASTRA_JWT_SECRET,
}),
apiRoutes: [
// Protected route (default behavior)
registerApiRoute('/protected-data', {
method: 'GET',
handler: async c => {
// Access authenticated user from request context
const user = c.get('requestContext').get('user')
return c.json({ message: 'Authenticated user', user })
},
}),

// Public route (no authentication required)
registerApiRoute('/webhooks/github', {
method: 'POST',
requiresAuth: false, // Explicitly opt out of authentication
handler: async c => {
const payload = await c.req.json()
// Process webhook without authentication
return c.json({ received: true })
},
}),
],
},
})

인증 동작
인증 동작에 대한 직접 링크

  • 구성된 인증 없음: 모든 경로(기본 제공 및 사용자 지정)가 공개됩니다.
  • 인증이 구성됨:
    • Mastra 제공 노선(/api/agents/*, /api/workflows/*, etc.) require authentication
    • 커스텀 경로에는 기본적으로 인증이 필요합니다.
    • 커스텀 경로는 다음을 통해 선택 해제할 수 있습니다.requiresAuth: false

사용자 정보에 접근하기
사용자 정보에 접근하기에 대한 직접 링크

요청이 인증되면 요청 컨텍스트에서 사용자 개체를 사용할 수 있습니다.

registerApiRoute('/user-profile', {
method: 'GET',
handler: async c => {
const requestContext = c.get('requestContext')
const user = requestContext.get('user')

return c.json({ user })
},
})

인증 공급자에 대한 자세한 내용은 다음을 참조하세요.Auth documentation.

클라이언트 연결 해제 후 계속 생성
클라이언트 연결 해제 후 계속 생성에 대한 직접 링크

chatRoute() 같은 기본 제공 스트리밍 헬퍼는 수신 요청의 AbortSignalagent.stream()에 전달합니다. 브라우저 연결이 끊어질 때 Model 호출을 취소해야 하는 경우 적절한 기본 동작입니다. 클라이언트 연결이 끊어질 때 중지되어야 하는 사용자 지정 스트리밍 경로에서는 c.req.raw.signalagent.stream() 같은 장기 실행 작업에 전달하세요. Mastra의 Node 기반 어댑터는 클라이언트 연결이 종료되면 사용자 지정 경로에서 스트리밍되는 Response 본문 읽기도 중지합니다. 클라이언트 연결 끊김으로 인해 발생한 것이 아닌 스트리밍 응답 본문 오류는 어댑터의 일반 오류 처리를 통해 계속 전파됩니다. Hono에서 연결 끊김 동작은 호스트 런타임이 연결 종료를 request.signal에 전달하는지에 따라 달라집니다.

src/mastra/index.ts
registerApiRoute('/stream', {
method: 'GET',
handler: async c => {
const stream = await agent.stream(prompt, {
abortSignal: c.req.raw.signal,
})

return stream.toTextStreamResponse()
},
})

클라이언트 연결이 끊어진 후에도 서버에서 최종 응답을 계속 생성하고 유지하려면 기본 MastraModelOutput을 중심으로 사용자 지정 경로를 구축하세요. c.req.raw.signal을 전달하지 않고 Agent 스트림을 시작한 다음, 백그라운드에서 consumeStream()을 호출하여 서버 측에서 생성을 계속하세요.

src/mastra/index.ts
import {
createUIMessageStream,
createUIMessageStreamResponse,
InferUIMessageChunk,
UIMessage,
} from 'ai'
import { toAISdkStream } from '@mastra/ai-sdk'
import { Mastra } from '@mastra/core'
import { registerApiRoute } from '@mastra/core/server'

export const mastra = new Mastra({
server: {
apiRoutes: [
registerApiRoute('/chat/persist/:agentId', {
method: 'POST',
handler: async c => {
const { messages, memory } = await c.req.json()
const mastra = c.get('mastra')
const agent = mastra.getAgent(c.req.param('agentId'))

const stream = await agent.stream(messages, {
memory,
// Do not pass c.req.raw.signal if this route should keep running
// after the client disconnects.
})

void stream.consumeStream().catch(error => {
mastra.getLogger()?.error('Background stream consumption failed', { error })
})

const uiStream = createUIMessageStream({
originalMessages: messages,
execute: async ({ writer }) => {
for await (const part of toAISdkStream(stream, { from: 'agent' })) {
writer.write(part as InferUIMessageChunk<UIMessage>)
}
},
})

return createUIMessageStreamResponse({ stream: uiStream })
},
}),
],
},
})
노트

HTTP 클라이언트가 사라진 후에도 의도적으로 작업을 계속하려는 경우에만 이 패턴을 사용하세요. 연결이 끊어지면 생성을 취소하려면 계속 chatRoute()를 사용하거나 요청의 AbortSignal을 직접 전달하세요.