Skip to content

Mastra server.build config for swaggerUI, openAPIDocs, apiReqLogs #3674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ee8dde2
Disable swaggerUI, playground for deployment builds, enable swaggerUI…
TheIsrael1 Apr 16, 2025
adb46af
changeset
TheIsrael1 Apr 16, 2025
68c66eb
Merge branch 'main' into ehindero2016/mastra-3076-feature-allow-disab…
TheIsrael1 Apr 17, 2025
88b0dd7
disable openapi route in prod build, optional build flag to enable
TheIsrael1 Apr 17, 2025
f0d4039
Expose ServerBundleOptions from deployer
TheIsrael1 Apr 17, 2025
22eab18
Merge branch 'main' into ehindero2016/mastra-3076-feature-allow-disab…
TheIsrael1 Apr 17, 2025
bc1e3d5
fix lint
TheIsrael1 Apr 17, 2025
0659e5c
fix lint
TheIsrael1 Apr 17, 2025
f8c1827
fix lint
TheIsrael1 Apr 17, 2025
83ed6de
fix lint
TheIsrael1 Apr 17, 2025
cd92e95
fix lint
TheIsrael1 Apr 17, 2025
98e4579
Merge branch 'main' into ehindero2016/mastra-3076-feature-allow-disab…
TheIsrael1 Apr 17, 2025
5a0ab12
hardcode buildbundler entry
TheIsrael1 Apr 17, 2025
f386158
enable server options from mastra instance server config
TheIsrael1 Apr 17, 2025
7b6b922
remove redundant addition
TheIsrael1 Apr 17, 2025
6d53ce6
Merge branches 'main' and 'ehindero2016/mastra-3076-feature-allow-dis…
codesandbot Apr 17, 2025
2be7ba8
Update local dev docs
TheIsrael1 Apr 17, 2025
3d8381d
Update deployer entries
TheIsrael1 Apr 17, 2025
baab338
Merge branch 'main' into ehindero2016/mastra-3076-feature-allow-disab…
TheIsrael1 Apr 17, 2025
2655349
Merge branch 'main' into ehindero2016/mastra-3076-feature-allow-disab…
TheIsrael1 Apr 22, 2025
33e5a62
move extra build options to dedicated server build config
TheIsrael1 Apr 22, 2025
0a42ea3
update docs
TheIsrael1 Apr 22, 2025
1d695c2
Merge branch 'main' into ehindero2016/mastra-3076-feature-allow-disab…
TheIsrael1 Apr 22, 2025
c54f9c2
update changeset description
TheIsrael1 Apr 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/soft-ants-slide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@mastra/deployer-cloudflare': patch
'@mastra/deployer-netlify': patch
'@mastra/deployer': patch
'@mastra/deployer-vercel': patch
'@mastra/core': patch
'mastra': patch
'create-mastra': patch
---

Disable swaggerUI, playground for production builds, mastra instance server build config to enable swaggerUI, apiReqLogs, openAPI documentation for prod builds
68 changes: 57 additions & 11 deletions deployers/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { writeFile } from 'fs/promises';
import { join } from 'path';

import { Deployer, createChildProcessLogger } from '@mastra/deployer';
import type { analyzeBundle } from '@mastra/deployer/analyze';
import virtual from '@rollup/plugin-virtual';
Expand Down Expand Up @@ -80,16 +79,63 @@ export class CloudflareDeployer extends Deployer {

private getEntry(): string {
return `
import '#polyfills';
import { mastra } from '#mastra';
import { createHonoServer } from '#server';

export default {
fetch: async (request, env, context) => {
const app = await createHonoServer(mastra)
return app.fetch(request, env, context);
}
}
import '#polyfills';
import { mastra } from '#mastra';
import { createHonoServer } from '#server';
import { evaluate } from '@mastra/core/eval';
import { AvailableHooks, registerHook } from '@mastra/core/hooks';
import { TABLE_EVALS } from '@mastra/core/storage';
import { checkEvalStorageFields } from '@mastra/core/utils';

registerHook(AvailableHooks.ON_GENERATION, ({ input, output, metric, runId, agentName, instructions }) => {
evaluate({
agentName,
input,
metric,
output,
runId,
globalRunId: runId,
instructions,
});
});

if (mastra.getStorage()) {
// start storage init in the background
mastra.getStorage().init();
}

registerHook(AvailableHooks.ON_EVALUATION, async traceObject => {
const storage = mastra.getStorage();
if (storage) {
// Check for required fields
const logger = mastra?.getLogger();
const areFieldsValid = checkEvalStorageFields(traceObject, logger);
if (!areFieldsValid) return;

await storage.insert({
tableName: TABLE_EVALS,
record: {
input: traceObject.input,
output: traceObject.output,
result: JSON.stringify(traceObject.result || {}),
agent_name: traceObject.agentName,
metric_name: traceObject.metricName,
instructions: traceObject.instructions,
test_info: null,
global_run_id: traceObject.globalRunId,
run_id: traceObject.runId,
created_at: new Date().toISOString(),
},
});
}
});

export default {
fetch: async (request, env, context) => {
const app = await createHonoServer(mastra)
return app.fetch(request, env, context);
}
}
`;
}
async prepare(outputDirectory: string): Promise<void> {
Expand Down
59 changes: 52 additions & 7 deletions deployers/netlify/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { existsSync, mkdirSync, writeFileSync } from 'fs';
import { join } from 'path';

import { Deployer } from '@mastra/deployer';
import { DepsService } from '@mastra/deployer/services';
import { execa } from 'execa';

import { getOrCreateSite } from './helpers.js';

export class NetlifyDeployer extends Deployer {
Expand Down Expand Up @@ -100,13 +98,60 @@ to = "/.netlify/functions/api/:splat"

private getEntry(): string {
return `
import { handle } from 'hono/netlify'
import { mastra } from '#mastra';
import { createHonoServer } from '#server';
import { handle } from 'hono/netlify'
import { mastra } from '#mastra';
import { createHonoServer } from '#server';
import { evaluate } from '@mastra/core/eval';
import { AvailableHooks, registerHook } from '@mastra/core/hooks';
import { TABLE_EVALS } from '@mastra/core/storage';
import { checkEvalStorageFields } from '@mastra/core/utils';

registerHook(AvailableHooks.ON_GENERATION, ({ input, output, metric, runId, agentName, instructions }) => {
evaluate({
agentName,
input,
metric,
output,
runId,
globalRunId: runId,
instructions,
});
});

if (mastra.getStorage()) {
// start storage init in the background
mastra.getStorage().init();
}

registerHook(AvailableHooks.ON_EVALUATION, async traceObject => {
const storage = mastra.getStorage();
if (storage) {
// Check for required fields
const logger = mastra?.getLogger();
const areFieldsValid = checkEvalStorageFields(traceObject, logger);
if (!areFieldsValid) return;

await storage.insert({
tableName: TABLE_EVALS,
record: {
input: traceObject.input,
output: traceObject.output,
result: JSON.stringify(traceObject.result || {}),
agent_name: traceObject.agentName,
metric_name: traceObject.metricName,
instructions: traceObject.instructions,
test_info: null,
global_run_id: traceObject.globalRunId,
run_id: traceObject.runId,
created_at: new Date().toISOString(),
},
});
}
});

const app = await createHonoServer(mastra);
const app = await createHonoServer(mastra);

export default handle(app)
export default handle(app)
`;
}
}
48 changes: 47 additions & 1 deletion deployers/vercel/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as child_process from 'child_process';
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import process from 'process';

import { Deployer } from '@mastra/deployer';

interface EnvVar {
Expand Down Expand Up @@ -139,6 +138,53 @@ export class VercelDeployer extends Deployer {
import { handle } from 'hono/vercel'
import { mastra } from '#mastra';
import { createHonoServer } from '#server';
import { evaluate } from '@mastra/core/eval';
import { AvailableHooks, registerHook } from '@mastra/core/hooks';
import { TABLE_EVALS } from '@mastra/core/storage';
import { checkEvalStorageFields } from '@mastra/core/utils';

registerHook(AvailableHooks.ON_GENERATION, ({ input, output, metric, runId, agentName, instructions }) => {
evaluate({
agentName,
input,
metric,
output,
runId,
globalRunId: runId,
instructions,
});
});

if (mastra.getStorage()) {
// start storage init in the background
mastra.getStorage().init();
}

registerHook(AvailableHooks.ON_EVALUATION, async traceObject => {
const storage = mastra.getStorage();
if (storage) {
// Check for required fields
const logger = mastra?.getLogger();
const areFieldsValid = checkEvalStorageFields(traceObject, logger);
if (!areFieldsValid) return;

await storage.insert({
tableName: TABLE_EVALS,
record: {
input: traceObject.input,
output: traceObject.output,
result: JSON.stringify(traceObject.result || {}),
agent_name: traceObject.agentName,
metric_name: traceObject.metricName,
instructions: traceObject.instructions,
test_info: null,
global_run_id: traceObject.globalRunId,
run_id: traceObject.runId,
created_at: new Date().toISOString(),
},
});
}
});

const app = await createHonoServer(mastra);

Expand Down
1 change: 0 additions & 1 deletion docs/src/components/github-star-count.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import React from "react";

function formatToK(number: number) {
Expand Down
34 changes: 34 additions & 0 deletions docs/src/content/en/docs/local-dev/mastra-dev.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,40 @@ You can then leverage the [Mastra Client](/docs/deployment/client) SDK to intera

`mastra dev` provides an OpenAPI spec at http://localhost:4111/openapi.json

To enable OpenAPI documentation in your Mastra instance, add the following configuration:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably always enable all of these on dev and only read the config on production builds

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh ok, we can do that, we can have a build property on the server config

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok we have a server.build config now that applies if !isDev in the hono server

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @TheIsrael1 . Could you help me understand if I'm using this wrong? It tried these options on a production build, but they are not being respected. Following the built code I noticed it's calling await createNodeServer(mastra); without options, and inside it there's a call for const app = await createHonoServer(mastra, options);, which makes it look like these options under Mastra.server.build are not being read. Do I need to set something else? Also, would it make sense to also keep the playground option?


```typescript
import { Mastra } from "@mastra/core";

export const mastra = new Mastra({
server: {
build: {
openAPIDocs: true, // Enable OpenAPI documentation
// ... other build config options
}
}
});
```

## Swagger UI

Swagger UI provides an interactive interface for testing your API endpoints at `mastra dev` provides an OpenAPI spec at http://localhost:4111/swagger-ui.
To enable Swagger UI in your Mastra instance, add the following configuration:

```typescript
import { Mastra } from "@mastra/core";

export const mastra = new Mastra({
server: {
build: {
openAPIDocs: true, // Enable OpenAPI documentation
swaggerUI: true, // Enable Swagger UI
// ... other build config options
}
}
});
```

## Local Dev Architecture

The local development server is designed to run without any external dependencies or containerization. This is achieved through:
Expand Down
63 changes: 55 additions & 8 deletions packages/cli/src/commands/build/BuildBundler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { FileService } from '@mastra/deployer/build';
import { Bundler } from '@mastra/deployer/bundler';
import * as fsExtra from 'fs-extra';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

export class BuildBundler extends Bundler {
constructor() {
Expand All @@ -29,13 +25,64 @@ export class BuildBundler extends Bundler {
await super.prepare(outputDirectory);
}

bundle(entryFile: string, outputDirectory: string, toolsPaths: string[]): Promise<void> {
async bundle(entryFile: string, outputDirectory: string, toolsPaths: string[]): Promise<void> {
return this._bundle(this.getEntry(), entryFile, outputDirectory, toolsPaths);
}

protected getEntry(): string {
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
return readFileSync(join(__dirname, 'templates', 'dev.entry.js'), 'utf8');
return `
// @ts-ignore
import { evaluate } from '@mastra/core/eval';
import { AvailableHooks, registerHook } from '@mastra/core/hooks';
import { TABLE_EVALS } from '@mastra/core/storage';
import { checkEvalStorageFields } from '@mastra/core/utils';
import { mastra } from '#mastra';
import { createNodeServer } from '#server';
// @ts-ignore
await createNodeServer(mastra);

registerHook(AvailableHooks.ON_GENERATION, ({ input, output, metric, runId, agentName, instructions }) => {
evaluate({
agentName,
input,
metric,
output,
runId,
globalRunId: runId,
instructions,
});
});

if (mastra.getStorage()) {
// start storage init in the background
mastra.getStorage().init();
}

registerHook(AvailableHooks.ON_EVALUATION, async traceObject => {
const storage = mastra.getStorage();
if (storage) {
// Check for required fields
const logger = mastra?.getLogger();
const areFieldsValid = checkEvalStorageFields(traceObject, logger);
if (!areFieldsValid) return;

await storage.insert({
tableName: TABLE_EVALS,
record: {
input: traceObject.input,
output: traceObject.output,
result: JSON.stringify(traceObject.result || {}),
agent_name: traceObject.agentName,
metric_name: traceObject.metricName,
instructions: traceObject.instructions,
test_info: null,
global_run_id: traceObject.globalRunId,
run_id: traceObject.runId,
created_at: new Date().toISOString(),
},
});
}
});
`;
}
}
5 changes: 4 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ program
command: 'mastra build',
args,
execution: async () => {
await build({ dir: args.dir, tools: args.tools ? args.tools.split(',') : [] });
await build({
dir: args.dir,
tools: args.tools ? args.tools.split(',') : [],
});
},
origin,
});
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/templates/dev.entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { checkEvalStorageFields } from '@mastra/core/utils';
import { mastra } from '#mastra';
import { createNodeServer } from '#server';
// @ts-ignore
await createNodeServer(mastra, { playground: true, swaggerUI: true });
await createNodeServer(mastra, { playground: true, isDev: true });

registerHook(AvailableHooks.ON_GENERATION, ({ input, output, metric, runId, agentName, instructions }) => {
evaluate({
Expand Down
Loading
Loading