Skip to content

feat(api): add OpenAPI documentation with hono-openapi#1954

Merged
yujonglee merged 4 commits intomainfrom
devin/1764245114-openapi-docs
Nov 28, 2025
Merged

feat(api): add OpenAPI documentation with hono-openapi#1954
yujonglee merged 4 commits intomainfrom
devin/1764245114-openapi-docs

Conversation

@yujonglee
Copy link
Contributor

@yujonglee yujonglee commented Nov 27, 2025

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.json endpoint serves the OpenAPI spec, and a React component renders the documentation in the web app.

Key changes:

  • Refactored API from Hono to OpenAPIHono with typed route definitions in apps/api/src/routes.ts
  • Added three API tags: internal (health checks), app (authenticated endpoints), webhook (external callbacks)
  • Created OpenAPIDocs component for rendering API documentation
  • Added API Reference page at /docs/developers/api

Updates since last revision:

  • Added build-time OpenAPI spec generation: pnpm -F @hypr/api run generate-openapi produces apps/api/openapi.yaml
  • Created shared openapi-config.ts module to avoid duplication between runtime and build scripts
  • Added yaml dependency for YAML output
  • openapi.yaml is treated as a build artifact (added to .gitignore, not committed)
  • Fixed type safety: Added hono-bindings.ts with AppBindings type so c.get("stripeEvent") is properly typed (removed as never cast)

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/completions and /listen endpoints properly reject unauthenticated requests.

  • Test /openapi.json endpoint: Start the API server and verify http://localhost:8787/openapi.json returns 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-openapi and verify apps/api/openapi.yaml is 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:

  1. Run pnpm -F @hypr/api run generate-openapi and inspect the generated openapi.yaml
  2. Run pnpm -F @hypr/api dev and hit /openapi.json to verify the spec
  3. Run pnpm -F @hypr/web dev and navigate to /docs/developers/api to see the rendered docs
  4. Test an authenticated endpoint to confirm auth middleware still works

Notes

  • The argos/web check may show visual diffs for the new API docs page - these are expected and need manual approval in Argos.
  • The Bearer security scheme is registered in both index.ts and generate-openapi.ts - consider factoring this out if it becomes a maintenance concern.
  • Verified that @hono/zod-openapi@^1.1.5 declares zod: "^4.0.0" in peerDependencies, so Zod v4 compatibility is confirmed.

Requested by: yujonglee (@yujonglee)
Devin session: https://app.devin.ai/sessions/f05797e6e5c94bc7878d280f06f51a2f

- 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-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Nov 27, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit 1a8369b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/6928ede6daa6c00008421bd0
😎 Deploy Preview https://deploy-preview-1954--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 27, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 1a8369b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/6928ede67ee39000074b31a3
😎 Deploy Preview https://deploy-preview-1954--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

📝 Walkthrough

Walkthrough

This 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

Cohort / File(s) Summary
Backend Dependencies & Type System
apps/api/package.json, apps/api/src/hono-bindings.ts
Updated Sentry, Supabase, and Hono dependencies; added OpenAPI tooling (@hono/zod-validator, @scalar/hono-api-reference, hono-openapi, zod-openapi). Introduced new AppBindings type with Stripe event variables for Hono context.
Backend Core App Restructuring
apps/api/src/index.ts
Refactored Hono app from plain Hono to generic Hono<AppBindings> type; integrated OpenAPI spec generation at /openapi.json; exposed API documentation UI at /docs with Scalar reference. Reworked route registration to centralize routes via app.route("/", routes) and apply conditional middleware (auth, webhooks) per-route. Updated default export to include websocket alongside port and fetch.
Backend Routing & Validation
apps/api/src/routes.ts
New centralized router module defining API_TAGS (INTERNAL, APP, WEBHOOK) and routes Hono instance. Registers health check, chat completions, Stripe webhook, and WebSocket listen endpoints with OpenAPI descriptions, Zod request/response schema validation, and security metadata.
Frontend OpenAPI Components
apps/web/src/components/openapi-docs.tsx, apps/web/src/routes/_view/docs/-components.tsx
New OpenAPIDocs React component fetches and renders OpenAPI specs from a provided API URL, grouping endpoints by tags with styled HTTP method and status indicators. Integrated into MDX component map to enable <OpenAPIDocs /> in documentation pages.
Documentation
apps/web/content/docs/developers/8.api.mdx
New API reference documentation page describing endpoint categories, authentication via Supabase JWT, embedded OpenAPI UI, and local development tooling guidance.
Build Artifacts
apps/api/.gitignore
Added openapi.yaml to version control ignore list.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • apps/api/src/index.ts: Core app refactoring with OpenAPI integration and middleware rewiring requires careful verification of route registration flow and correct middleware application order
  • apps/api/src/routes.ts: Multiple route definitions with Zod schemas and OpenAPI metadata need validation that schemas match actual request/response contracts and security declarations are accurate
  • apps/web/src/components/openapi-docs.tsx: Error handling, loading states, and spec parsing logic should be reviewed for edge cases and accessibility
  • Dependency compatibility: New OpenAPI libraries (@scalar/hono-api-reference, hono-openapi, zod-openapi) and their integration with existing Hono and Zod setup warrant verification
  • Type system changes: AppBindings type introduction across backend files needs confirmation that type inference and Hono context typing work correctly

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding OpenAPI documentation to the API using hono-openapi, which aligns with the primary objective of the changeset.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering API refactoring, new components, build-time generation, and type safety improvements.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1764245114-openapi-docs

Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link

argos-ci bot commented Nov 27, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
web (Inspect) ⚠️ Changes detected (Review) 3 changed Nov 28, 2025, 12:35 AM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 oneOf to 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 useEffect should include cleanup logic to abort the fetch if the component unmounts or if apiUrl changes 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

📥 Commits

Reviewing files that changed from the base of the PR and between d53f5f5 and ae105d8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/components/openapi-docs.tsx
  • apps/web/src/routes/_view/docs/-components.tsx
  • apps/api/src/routes.ts
  • apps/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.ts
  • apps/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 const for 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 for tools (Line 42) and tool_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 cn usage 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 never cast 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.json will 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>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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-openapi v1.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-openapi release with Zod v4 support.

🧹 Nitpick comments (2)
apps/api/scripts/generate-openapi.ts (1)

21-24: Route registration is duplicated between this script and index.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 listenRoute is 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

📥 Commits

Reviewing files that changed from the base of the PR and between ae105d8 and a83a3ed.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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.ts
  • apps/api/src/index.ts
  • apps/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, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/api/src/openapi-config.ts
  • apps/api/src/index.ts
  • apps/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.yaml build artifact with a clear explanatory comment.

apps/api/package.json (1)

7-9: LGTM!

The new scripts correctly wire up OpenAPI generation with bun run and the build script appropriately delegates to generate-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_TAGS constants 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 Hono to OpenAPIHono is the correct approach to enable OpenAPI documentation with schema validation.


46-52: LGTM!

Security scheme registration and health route using app.openapi are 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.json endpoint is properly exposed using app.doc with 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>
@devin-ai-integration
Copy link
Contributor

Regarding the Zod v4 compatibility concern: I checked the installed @hono/zod-openapi package and it declares "zod": "^4.0.0" in its peerDependencies, which means it does support Zod v4. The library has been updated since the documentation CodeRabbit referenced.

Additionally:

  • The generate-openapi script runs successfully and produces valid OpenAPI YAML
  • CI passes (fmt, typecheck, tests)
  • The /openapi.json endpoint works correctly in the Netlify preview

No changes needed for Zod compatibility.

@yujonglee yujonglee merged commit 3237c94 into main Nov 28, 2025
10 of 12 checks passed
@yujonglee yujonglee deleted the devin/1764245114-openapi-docs branch November 28, 2025 00:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 to openapi-config.ts if 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:

  • ChatCompletionRequestSchema uses .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 or z.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.

  1. Dynamic import pattern: The await import("./env") inside the handler adds latency on every request. Since env is configuration, consider importing it at module level.

  2. Model list maintenance: Hardcoded model lists (lines 104-109) may become stale. Consider extracting these to configuration.

  3. 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 env module 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

📥 Commits

Reviewing files that changed from the base of the PR and between 62c7416 and 1a8369b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is 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.ts
  • apps/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, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/api/src/routes.ts
  • apps/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 with stripeEvent. The imports are well-organized and the AppBindings type from hono-bindings.ts ensures c.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 /listen in 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_TAGS constant with as const ensures 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 stripeEvent from the typed context (thanks to AppBindings). 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 listenSocketHandler from the relevant code snippets. The dynamic import for listenSocketHandler is 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-Signature headers will match correctly without modification.

However, there is redundant validation: the verifyStripeWebhook middleware (line 42 in index.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.

This was referenced Dec 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments