Aller au contenu principal

Routes API personnalisées

Par défaut, Mastra expose automatiquement les Agents et Workflows enregistrés par l’intermédiaire de son serveur. Pour ajouter d’autres comportements, vous pouvez définir vos propres routes HTTP.

Les routes sont créées à l’aide de la fonction utilitaire registerApiRoute() de @mastra/core/server. Elles peuvent se trouver dans le même fichier que l’instance Mastra, mais les séparer permet de conserver une configuration concise.

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' })
},
}),
],
},
})

Une fois enregistrée, une route personnalisée est accessible depuis la racine du serveur. Par exemple :

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

Le gestionnaire de chaque route reçoit le Context Hono. Dans ce gestionnaire, vous pouvez accéder à l’instance Mastra afin de récupérer ou d’appeler des Agents et des Workflows.

Middleware
Lien direct vers Middleware

Pour ajouter un middleware propre à une route, transmettez un tableau middleware lors de l’appel à registerApiRoute().

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' })
},
}),
],
},
})

Documentation OpenAPI
Lien direct vers Documentation OpenAPI

Les routes personnalisées peuvent inclure des métadonnées OpenAPI afin d’apparaître dans Swagger UI avec les routes du serveur Mastra. La spécification OpenAPI est accessible à l’adresse /api/openapi.json, qui répertorie les routes personnalisées et les routes intégrées. Transmettez une option openapi contenant les champs d’opération OpenAPI standard.

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' })
},
}),
],
},
})

Utiliser des schémas Zod
Lien direct vers Utiliser des schémas Zod

Les schémas Zod de la configuration openapi sont convertis en schémas JSON lors de la génération du document OpenAPI :

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)
},
}),
],
},
})

Afficher les routes dans Swagger UI
Lien direct vers Afficher les routes dans Swagger UI

En mode développement (mastra dev) ou lorsque les options de build contiennent swaggerUI: true, vos routes personnalisées apparaissent dans Swagger UI à l’adresse /swagger-ui.

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

Authentication
Lien direct vers Authentication

Lorsque l’authentification est configurée sur votre serveur Mastra, les routes API personnalisées nécessitent par défaut une authentification. Pour rendre une route accessible au public, définissez 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 })
},
}),
],
},
})

Comportement de l’authentification
Lien direct vers Comportement de l’authentification

  • Aucune authentification configurée : toutes les routes, intégrées comme personnalisées, sont publiques
  • Authentification configurée :
    • Les routes fournies par Mastra (/api/agents/*, /api/workflows/*, etc.) nécessitent une authentification
    • Les routes personnalisées nécessitent une authentification par défaut
    • Les routes personnalisées peuvent la désactiver avec requiresAuth: false

Accéder aux informations de l’utilisateur
Lien direct vers Accéder aux informations de l’utilisateur

Lorsqu’une requête est authentifiée, l’objet utilisateur est disponible dans le contexte de requête :

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

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

Pour en savoir plus sur les Providers d’authentification, consultez la documentation sur l’authentification.

Poursuivre la génération après la déconnexion du client
Lien direct vers Poursuivre la génération après la déconnexion du client

Les fonctions utilitaires de streaming intégrées, comme chatRoute(), transmettent l’AbortSignal de la requête entrante à agent.stream(). Ce comportement par défaut convient lorsque la déconnexion d’un navigateur doit annuler l’appel au modèle.

Pour les routes de streaming personnalisées qui doivent s’arrêter à la déconnexion du client, transmettez c.req.raw.signal aux opérations de longue durée comme agent.stream(). Les adaptateurs Mastra fondés sur Node cessent également de lire les corps de Response diffusés par les routes personnalisées lorsque la connexion du client se ferme. Les erreurs du corps de la réponse diffusée qui ne sont pas provoquées par une déconnexion du client continuent de se propager selon le traitement normal des erreurs de l’adaptateur. Dans Hono, le comportement en cas de déconnexion dépend de la transmission des fermetures de connexion à request.signal par l’environnement d’exécution hôte.

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()
},
})

Si vous souhaitez que le serveur poursuive la génération et conserve la réponse finale même après la déconnexion du client, créez une route personnalisée autour du MastraModelOutput sous-jacent. Démarrez le stream de l’Agent sans transmettre c.req.raw.signal, puis appelez consumeStream() en arrière-plan afin que la génération continue côté serveur.

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 })
},
}),
],
},
})
remarque

Utilisez ce modèle uniquement lorsque vous souhaitez délibérément poursuivre le traitement après le départ du client HTTP. Si les déconnexions doivent annuler la génération, continuez à utiliser chatRoute() ou transmettez vous-même l’AbortSignal de la requête.