feat(api): add OpenAPI documentation with hono-openapi#1954
Conversation
- Add @hono/zod-openapi package to apps/api - Refactor API routes to use OpenAPIHono with proper schemas and tags - Add API tags: internal (health checks), app (authenticated endpoints), webhook (external callbacks) - Add /openapi.json endpoint to serve OpenAPI spec - Create OpenAPIDocs component for rendering API docs in apps/web - Add API Reference documentation page in developers section Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
✅ Deploy Preview for hyprnote-storybook ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for hyprnote ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughThis PR introduces OpenAPI API documentation support to the application. It adds OpenAPI schema generation and a documentation endpoint to the Hono backend, creates a React component to render OpenAPI specifications, centralizes route definitions with Zod validation and OpenAPI metadata, and provides a corresponding documentation page. Changes
Sequence DiagramsequenceDiagram
actor User
participant Browser as Client Browser
participant WebApp as Web App<br/>(Hono API Reference UI)
participant APIServer as API Server<br/>(Hono Backend)
User->>Browser: Visit /docs
Browser->>WebApp: Request /docs page
WebApp->>Browser: Render OpenAPI UI component
Browser->>Browser: OpenAPIDocs component<br/>mounts
Browser->>APIServer: GET /openapi.json
APIServer->>APIServer: Generate OpenAPI<br/>spec from routes
APIServer->>Browser: Return OpenAPI spec<br/>(JSON)
Browser->>Browser: Parse & render<br/>endpoints by tag<br/>(INTERNAL, APP,<br/>WEBHOOK)
Browser->>User: Display interactive<br/>API documentation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/api/src/routes.ts (1)
68-71: Consider adding 200 response schema for better documentation.The 200 response only has a description without a schema. While this may be intentional due to streaming vs non-streaming responses, consider documenting at least one common response shape or using
oneOfto document both streaming and non-streaming formats for better API documentation.apps/web/src/components/openapi-docs.tsx (2)
71-85: Add AbortController for proper cleanup.The
useEffectshould include cleanup logic to abort the fetch if the component unmounts or ifapiUrlchanges before the request completes.Apply this diff:
useEffect(() => { + const controller = new AbortController(); + - fetch(`${apiUrl}/openapi.json`) + fetch(`${apiUrl}/openapi.json`, { signal: controller.signal }) .then((res) => { if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`); return res.json(); }) .then((data) => { setSpec(data); setLoading(false); }) .catch((err) => { + if (err.name === 'AbortError') return; setError(err.message); setLoading(false); }); + + return () => controller.abort(); }, [apiUrl]);
229-238: Consider extracting response code color logic.The nested ternary for determining response code colors could be extracted to a small helper function for improved readability.
const getResponseCodeColor = (code: string) => { if (code.startsWith("2")) return "bg-green-50 text-green-700 border-green-200"; if (code.startsWith("4")) return "bg-yellow-50 text-yellow-700 border-yellow-200"; if (code.startsWith("5")) return "bg-red-50 text-red-700 border-red-200"; return "bg-gray-50 text-gray-700 border-gray-200"; }; // Then use: <span className={cn("px-2 py-1 text-xs rounded border", getResponseCodeColor(code))} title={response.description} > {code} </span>
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
apps/api/package.json(1 hunks)apps/api/src/index.ts(4 hunks)apps/api/src/routes.ts(1 hunks)apps/web/content/docs/developers/8.api.mdx(1 hunks)apps/web/src/components/openapi-docs.tsx(1 hunks)apps/web/src/routes/_view/docs/-components.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/web/src/components/openapi-docs.tsxapps/web/src/routes/_view/docs/-components.tsxapps/api/src/routes.tsapps/api/src/index.ts
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/api/src/routes.tsapps/api/src/index.ts
🧠 Learnings (1)
📚 Learning: 2025-11-24T16:32:13.593Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: packages/nango/.cursor/rules/nango.mdc:0-0
Timestamp: 2025-11-24T16:32:13.593Z
Learning: Applies to packages/nango/**/*.ts : API endpoints must be correctly implemented according to actual API documentation
Applied to files:
apps/api/src/routes.ts
🧬 Code graph analysis (2)
apps/web/src/components/openapi-docs.tsx (1)
packages/utils/src/cn.ts (1)
cn(20-22)
apps/api/src/index.ts (1)
apps/api/src/routes.ts (5)
healthRoute(9-27)chatCompletionsRoute(51-81)stripeWebhookRoute(83-130)listenRoute(132-185)API_TAGS(3-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: ci (macos, macos-14)
- GitHub Check: fmt
🔇 Additional comments (21)
apps/api/src/routes.ts (5)
3-7: LGTM!The API tags are well-organized and properly typed with
as constfor type safety.
9-27: LGTM!Standard health check endpoint with appropriate OpenAPI metadata and response schema.
36-49: Schema correctly implements OpenAI API compatibility.The
.passthrough()on Line 48 allows additional properties beyond the defined schema, which is appropriate for OpenAI API compatibility. The permissive typing fortools(Line 42) andtool_choice(Line 43) aligns with the OpenAI API specification's flexible structure.
83-130: LGTM!Stripe webhook route is properly configured with signature validation and comprehensive error handling. The use of header validation instead of Bearer auth is appropriate for webhook endpoints.
132-185: LGTM!WebSocket endpoint is properly documented with appropriate status codes including 101 for WebSocket upgrade and comprehensive error handling for upstream service failures.
apps/web/content/docs/developers/8.api.mdx (1)
1-35: LGTM!The API documentation is well-structured with clear sections for authentication, interactive docs, and local development guidance. The use of the OpenAPIDocs component for interactive exploration is a good user experience.
apps/web/src/routes/_view/docs/-components.tsx (1)
21-21: LGTM!The OpenAPIDocs component is correctly imported and registered in the MDX components map, following the established pattern for other custom components.
Also applies to: 126-126
apps/web/src/components/openapi-docs.tsx (5)
1-5: LGTM!Appropriate use of "use client" directive for a component that uses browser APIs (fetch) and React hooks.
7-50: LGTM!The OpenAPISpec interface provides sufficient typing for the component's needs. Using
Record<string, unknown>for schemas is pragmatic given the flexible nature of OpenAPI schemas.
52-64: LGTM!Color mappings are semantically appropriate and align with the API tags defined in the routes configuration.
107-124: LGTM!Path grouping logic correctly organizes endpoints by their primary tag, providing a clean categorized view of the API.
126-164: LGTM!Header and tags sections are well-structured with appropriate styling. The
cnusage follows coding guidelines by passing arrays.apps/api/src/index.ts (9)
3-3: LGTM!Clean migration to OpenAPIHono with proper imports of route definitions.
Also applies to: 12-18
22-22: LGTM!Proper instantiation of OpenAPIHono.
46-50: LGTM!Security scheme is correctly registered using OpenAPI 3.0 specification for HTTP Bearer tokens.
52-52: LGTM!Health endpoint correctly implements the OpenAPI route definition with matching response schema.
56-100: LGTM!Chat completions endpoint correctly integrates OpenAPI validation with
c.req.valid("json")to access the validated request body. Authentication middleware and proxying logic remain intact.
103-113: LGTM with type cast note.Stripe webhook correctly integrates OpenAPI route with middleware. The
as nevercast on Line 105 is a workaround for accessing middleware-set context values in Hono—consider typing the middleware context properly if this pattern becomes common.
115-120: LGTM!Listen endpoint appropriately applies authentication middleware only in production while maintaining OpenAPI documentation in both environments.
122-145: LGTM!OpenAPI documentation endpoint is well-configured with comprehensive metadata and clear tag descriptions. The generated JSON at
/openapi.jsonwill provide a complete API specification.
147-151: LGTM!Export structure correctly preserved for Bun runtime integration.
- Create shared openapi-config.ts module for OpenAPI metadata - Add generate-openapi.ts script that extracts spec from route definitions - Add yaml dependency for YAML output - Add build script that runs generate-openapi - Add openapi.yaml to .gitignore (treated as build artifact) Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/api/package.json (1)
12-12: Zod v4 compatibility issue with@hono/zod-openapi@^1.1.5.This issue was previously flagged.
@hono/zod-openapiv1.x targets Zod v3; using it with Zod^4.1.13(line 19) will likely cause runtime failures. Either downgrade Zod to v3 or wait for an official@hono/zod-openapirelease with Zod v4 support.
🧹 Nitpick comments (2)
apps/api/scripts/generate-openapi.ts (1)
21-24: Route registration is duplicated between this script andindex.ts.Both files register the same routes (
healthRoute,chatCompletionsRoute,stripeWebhookRoute,listenRoute). If routes are added or removed, both files must be updated. Consider extracting route registration to a shared function to avoid drift.Example approach:
// In a shared module, e.g., src/register-routes.ts export function registerRoutes(app: OpenAPIHono) { app.openAPIRegistry.registerComponent("securitySchemes", "Bearer", { ... }); // Return route handlers map or accept handlers as parameter }apps/api/src/index.ts (1)
115-120: Consider consolidating duplicate route registration.The
listenRouteis registered twice (lines 116 and 119) with different middleware applied conditionally. This works but could be cleaner.+app.use("/listen", async (c, next) => { + if (env.NODE_ENV !== "development") { + return requireSupabaseAuth(c, next); + } + return next(); +}); +app.openapi(listenRoute, listenSocketHandler); -if (env.NODE_ENV === "development") { - app.openapi(listenRoute, listenSocketHandler); -} else { - app.use("/listen", requireSupabaseAuth); - app.openapi(listenRoute, listenSocketHandler); -}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (5)
apps/api/.gitignore(1 hunks)apps/api/package.json(1 hunks)apps/api/scripts/generate-openapi.ts(1 hunks)apps/api/src/index.ts(4 hunks)apps/api/src/openapi-config.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/api/src/openapi-config.tsapps/api/src/index.tsapps/api/scripts/generate-openapi.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/api/src/openapi-config.tsapps/api/src/index.tsapps/api/scripts/generate-openapi.ts
🧠 Learnings (1)
📚 Learning: 2025-11-24T16:32:01.459Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T16:32:01.459Z
Learning: Applies to **/*.config.{ts,json} : Agent configuration should be centralized and externalized from implementation logic
Applied to files:
apps/api/src/openapi-config.ts
🧬 Code graph analysis (2)
apps/api/src/openapi-config.ts (1)
apps/api/src/routes.ts (1)
API_TAGS(3-7)
apps/api/scripts/generate-openapi.ts (2)
apps/api/src/routes.ts (4)
healthRoute(9-27)chatCompletionsRoute(51-81)stripeWebhookRoute(83-130)listenRoute(132-185)apps/api/src/openapi-config.ts (1)
OPENAPI_CONFIG(3-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: ci (macos, macos-14)
- GitHub Check: fmt
🔇 Additional comments (8)
apps/api/.gitignore (1)
3-5: LGTM!Correctly ignoring the generated
openapi.yamlbuild artifact with a clear explanatory comment.apps/api/package.json (1)
7-9: LGTM!The new scripts correctly wire up OpenAPI generation with
bun runand thebuildscript appropriately delegates togenerate-openapi.apps/api/scripts/generate-openapi.ts (1)
26-31: LGTM!The OpenAPI document generation and YAML serialization logic is clean and straightforward.
apps/api/src/openapi-config.ts (1)
1-26: LGTM!Well-structured OpenAPI configuration with clear separation of concerns. Using
API_TAGSconstants ensures consistency between route definitions and documentation. Based on learnings, this centralized configuration approach aligns with best practices.apps/api/src/index.ts (4)
3-3: LGTM!Migration from
HonotoOpenAPIHonois the correct approach to enable OpenAPI documentation with schema validation.
46-52: LGTM!Security scheme registration and health route using
app.openapiare properly configured.
56-100: LGTM!The chat completions route correctly applies Supabase auth middleware before the OpenAPI handler and uses
c.req.valid("json")for typed request body access.
122-122: LGTM!The
/openapi.jsonendpoint is properly exposed usingapp.docwith the centralized config.
- Create hono-bindings.ts with AppBindings type
- Use OpenAPIHono<AppBindings> for proper type inference
- Remove 'as never' cast on c.get('stripeEvent')
Addresses CodeRabbit feedback on type safety
Co-Authored-By: yujonglee <yujonglee.dev@gmail.com>
|
Regarding the Zod v4 compatibility concern: I checked the installed Additionally:
No changes needed for Zod compatibility. |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
apps/api/src/index.ts (1)
52-99: OpenAPI spec configuration is well-structured.The documentation includes proper tags, security schemes, and server definitions. One minor observation: the Bearer security scheme is defined here and potentially also in
generate-openapi.ts(per PR notes). Consider extracting this toopenapi-config.tsif not already done to maintain single source of truth.apps/api/src/routes.ts (3)
14-46: Schema definitions are well-structured.The Zod schemas provide good type coverage for the API. A few observations:
ChatCompletionRequestSchemauses.passthrough()which is appropriate for OpenAI-compatible endpoints that may have additional fields.tool_choice: z.union([z.string(), z.object({})])accepts an empty object but the OpenAI spec allows specific object shapes like{ type: "function", function: { name: "..." } }. Consider using.passthrough()on the object orz.record()for flexibility.- tool_choice: z.union([z.string(), z.object({})]).optional(), + tool_choice: z.union([z.string(), z.object({}).passthrough()]).optional(),
70-137: Chat completions route has a few considerations.
Dynamic import pattern: The
await import("./env")inside the handler adds latency on every request. Sinceenvis configuration, consider importing it at module level.Model list maintenance: Hardcoded model lists (lines 104-109) may become stale. Consider extracting these to configuration.
Error handling: The fetch to OpenRouter doesn't handle network errors. If the fetch throws, the error will propagate unhandled.
Consider handling fetch errors:
+ try { const response = await fetch( "https://openrouter.ai/api/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${env.OPENROUTER_API_KEY}`, }, body: JSON.stringify({ ...bodyWithoutModel, models: modelsToUse, provider: { sort: "latency" }, }), }, ); return new Response(response.body, { status: response.status, headers: { "Content-Type": response.headers.get("Content-Type") ?? "application/json", }, }); + } catch (error) { + console.error("OpenRouter request failed:", error); + return c.text("upstream_error", 502); + }
96-96: Dynamic import adds unnecessary per-request overhead.The
envmodule is static configuration and should be imported at module level for better performance.Move the import to the top of the file:
import { Hono } from "hono"; import { describeRoute } from "hono-openapi"; import { resolver, validator } from "hono-openapi/zod"; import { z } from "zod"; import type { AppBindings } from "./hono-bindings"; +import { env } from "./env";Then remove the dynamic import inside the handler:
- const { env } = await import("./env");
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (3)
apps/api/package.json(1 hunks)apps/api/src/index.ts(2 hunks)apps/api/src/routes.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/api/package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces
Files:
apps/api/src/routes.tsapps/api/src/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, usecn(import from@hypr/utils). It is similar toclsx. Always pass an array and split by logical grouping.
Usemotion/reactinstead offramer-motion.
Files:
apps/api/src/routes.tsapps/api/src/index.ts
🧬 Code graph analysis (1)
apps/api/src/routes.ts (3)
apps/api/src/hono-bindings.ts (1)
AppBindings(3-7)apps/api/src/billing.ts (1)
syncBillingForStripeEvent(75-87)apps/api/src/listen.ts (1)
listenSocketHandler(10-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Redirect rules - hyprnote-storybook
- GitHub Check: Header rules - hyprnote-storybook
- GitHub Check: Pages changed - hyprnote-storybook
- GitHub Check: Redirect rules - hyprnote
- GitHub Check: Header rules - hyprnote
- GitHub Check: Pages changed - hyprnote
- GitHub Check: ci (macos, macos-14)
- GitHub Check: fmt
🔇 Additional comments (9)
apps/api/src/index.ts (4)
1-17: LGTM! Clean imports and proper type integration.The migration to
Hono<AppBindings>properly addresses the previous type safety issue withstripeEvent. The imports are well-organized and theAppBindingstype fromhono-bindings.tsensuresc.get("stripeEvent")is properly typed.
22-39: CORS configuration looks appropriate.The CORS middleware correctly excludes
/listen(WebSocket endpoint) and allows necessary headers for Supabase authentication. The wildcard origin ("*") is acceptable for a public API, though worth noting for security-sensitive contexts.
41-48: Per-route middleware wiring is clean.The authentication middleware is applied at the right granularity. The conditional auth for
/listenin non-development environments is a reasonable pattern for local development convenience.
101-109: API reference endpoint configuration looks good.The Scalar API reference with the Saturn theme provides a clean documentation UI linked to the OpenAPI spec.
apps/api/src/routes.ts (5)
1-12: LGTM! Clean setup with proper type integration.The imports are appropriate for the hono-openapi integration. The
API_TAGSconstant withas constensures type safety for tag references.
50-68: Health check route is clean and correct.Simple and effective health check implementation with proper OpenAPI documentation.
139-192: Stripe webhook route is well-typed and correct.The route properly retrieves
stripeEventfrom the typed context (thanks toAppBindings). Error handling is appropriate with proper status codes.
194-244: WebSocket route documentation and implementation look correct.The route properly documents all possible response codes (101, 400, 401, 502, 504) which align with the error handling in
listenSocketHandlerfrom the relevant code snippets. The dynamic import forlistenSocketHandleris acceptable here since WebSocket connections are less latency-sensitive on initial setup.
173-178: The header validator correctly uses lowercase"stripe-signature"and will work properly with Hono's case-insensitive header handling.Hono's validator (via @hono/zod-validator) uses the Fetch API's normalized Headers object, which treats header names as case-insensitive. Using lowercase in the Zod schema is the correct and recommended approach. Incoming
Stripe-Signatureheaders will match correctly without modification.However, there is redundant validation: the
verifyStripeWebhookmiddleware (line 42 inindex.ts) already validates the signature and extracts the event. The validator on lines 173-178 re-validates the same header unnecessarily. Consider removing this duplicate validation since the middleware already ensures the header is present and valid.
Summary
This PR adds OpenAPI documentation to the Hyprnote API using
@hono/zod-openapi. The API endpoints are now tagged by type (internal, app, webhook) and documented with proper schemas. A new/openapi.jsonendpoint serves the OpenAPI spec, and a React component renders the documentation in the web app.Key changes:
HonotoOpenAPIHonowith typed route definitions inapps/api/src/routes.tsinternal(health checks),app(authenticated endpoints),webhook(external callbacks)OpenAPIDocscomponent for rendering API documentation/docs/developers/apiUpdates since last revision:
pnpm -F @hypr/api run generate-openapiproducesapps/api/openapi.yamlopenapi-config.tsmodule to avoid duplication between runtime and build scriptsyamldependency for YAML outputopenapi.yamlis treated as a build artifact (added to.gitignore, not committed)hono-bindings.tswithAppBindingstype soc.get("stripeEvent")is properly typed (removedas nevercast)Review & Testing Checklist for Human
Verify authentication still works: The middleware pattern changed from inline (
app.post("/path", auth, handler)) to separate (app.use("/path", auth); app.openapi(route, handler)). Test that/chat/completionsand/listenendpoints properly reject unauthenticated requests.Test
/openapi.jsonendpoint: Start the API server and verifyhttp://localhost:8787/openapi.jsonreturns valid OpenAPI 3.0 JSON. Consider using a validator like https://editor.swagger.io/Test build-time generation: Run
pnpm -F @hypr/api run generate-openapiand verifyapps/api/openapi.yamlis created with correct content.Check OpenAPIDocs component rendering: The MDX file hardcodes
apiUrl="https://api.hyprnote.com". Verify this works in production and consider if environment-based configuration is needed.Recommended test plan:
pnpm -F @hypr/api run generate-openapiand inspect the generatedopenapi.yamlpnpm -F @hypr/api devand hit/openapi.jsonto verify the specpnpm -F @hypr/web devand navigate to/docs/developers/apito see the rendered docsNotes
argos/webcheck may show visual diffs for the new API docs page - these are expected and need manual approval in Argos.index.tsandgenerate-openapi.ts- consider factoring this out if it becomes a maintenance concern.@hono/zod-openapi@^1.1.5declareszod: "^4.0.0"in peerDependencies, so Zod v4 compatibility is confirmed.Requested by: yujonglee (@yujonglee)
Devin session: https://app.devin.ai/sessions/f05797e6e5c94bc7878d280f06f51a2f