Skip to content

Conversation

@cjbell
Copy link
Contributor

@cjbell cjbell commented Jan 31, 2026

Description

This PR migrates the /api-reference and /mapi-reference documentation to the Next.js App Router.

Why: This migration leverages React Server Components for improved performance, better aligns with the latest Next.js architecture, and provides a more robust and scalable foundation for our API documentation.

How:

  • Introduced a new app/(api-reference) route group with [[...slug]] catch-all routes for both API and Management API references.
  • Implemented a clear server/client component split, with data fetching handled by Server Components and interactive UI by Client Components.
  • Developed new OpenAPI utilities (lib/openapi/) for efficient spec loading, processing, and schema referencing.
  • Replaced the legacy Pages Router implementations for /api-reference and /mapi-reference with the new App Router structure.
  • Addressed various compatibility issues, including adding "use client" directives, resolving next/router dependencies, and adapting @telegraph component usage for SSR contexts.
  • Updated next.config.js to remove outdated rewrites and redirects, allowing the App Router to manage these routes.

The migration ensures that all build, type-check, and linting checks pass, and the API reference pages are now fully served by the App Router.

Todos

Tasks

This PR addresses the migration detailed in api-reference-migration-plan.md.


Open in Cursor Open in Web

cursoragent and others added 3 commits January 31, 2026 10:59
- Create lib/openapi with cached loader, types, and helpers
- Create app/(api-reference) route group with layout and providers
- Create server components for resource sections, methods, schemas
- Create client component islands for expandable properties and code examples
- Create catch-all routes for /api-reference and /mapi-reference
- Update next.config.js to remove rewrites for API reference pages

Co-authored-by: chris <chris@knock.app>
- Remove old Pages Router files (/pages/api-reference, /pages/mapi-reference)
- Add App Router compatible client components for resource sections, methods, and schemas
- Refactor page-shell and loading components to use plain HTML/Tailwind (avoid @telegraph SSR issues)
- Add App Router compatible API sections, rate limit, section heading, and content actions components
- Add 'use client' directives to hooks that need them (useClipboard, useIsMounted)
- Add 'use client' directive to CodeBlock component
- Simplify pre-content to static HTML rendering
- Simplify providers to avoid NextRouter dependency issues

Co-authored-by: chris <chris@knock.app>
Co-authored-by: chris <chris@knock.app>
@cursor
Copy link

cursor bot commented Jan 31, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@vercel
Copy link

vercel bot commented Jan 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jan 31, 2026 0:05am

Request Review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 11 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

if (path) {
const url = new URL(window.location.href);
url.pathname = path;
window.history.pushState({}, "", url.toString());
Copy link

Choose a reason for hiding this comment

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

Section heading click handler sets incorrect URL path

Medium Severity

The handleClick function in SectionHeadingAppRouter sets url.pathname = path where path is a relative resource path like /workflows. This doesn't include the base path (e.g., /api-reference), causing navigation to incorrect URLs like example.com/workflows instead of example.com/api-reference/workflows. The path prop passed to this component from Section originates from resourcePath in resource-section.tsx, which is constructed as /${resourceName} without the full route prefix.

Fix in Cursor Fix in Web

// Navigate to the section
const element = document.querySelector(
`[data-resource-path="${item.slug.replace(/^\//, "")}"]`,
);
Copy link

Choose a reason for hiding this comment

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

Sidebar item click selectors don't match DOM attribute values

Medium Severity

The handleClick function in SidebarItem queries for elements using [data-resource-path="${item.slug.replace(/^\//, "")}"] which produces selectors like [data-resource-path="create"]. However, the actual data-resource-path attributes in the DOM are set to values like /workflows/create (from method.path in resource-section-client.tsx). This mismatch means the query selector will never find the target element, causing the scroll-to-section functionality to silently fail.

Fix in Cursor Fix in Web

tooltip: "1 update / second / entity",
color: "bg-red-100 text-red-800",
},
};
Copy link

Choose a reason for hiding this comment

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

Incomplete batchConfig causes crash for batch tier values

Low Severity

The batchConfig object only defines tier 1, but the component's Props type accepts tier: 1 | 2 | 3 | 4 | 5 with isBatch?: boolean. If isBatch is true and tier is any value other than 1, accessing batchConfig[tier].tooltip or batchConfig[tier].color will crash with "Cannot read properties of undefined" since batchConfig[2], batchConfig[3], etc. are all undefined.

Additional Locations (1)

Fix in Cursor Fix in Web

timeoutRef.current = setTimeout(() => {
setCopied(false);
}, 2000);
};
Copy link

Choose a reason for hiding this comment

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

Missing timeout cleanup causes memory leak on unmount

Low Severity

The ContentActionsAppRouter component creates a timeout via timeoutRef but lacks a cleanup effect to clear it on unmount. If the component unmounts while a timeout is pending (e.g., user navigates away within 2 seconds of copying), the timeout will still fire and attempt to call setCopied(false) on an unmounted component. This causes a memory leak and React's "Can't perform a state update on an unmounted component" warning. The existing useClipboard hook in hooks/useClipboard.ts correctly implements this cleanup pattern with a useEffect return function.

Fix in Cursor Fix in Web

return `${items.title || items.type || "unknown"}[]`;
}
return schema.type || "unknown";
}
Copy link

Choose a reason for hiding this comment

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

Duplicated getTypeString utility across five files

Medium Severity

The getTypeString function is duplicated 5 times across different component files with nearly identical implementations. This should be extracted to a shared utility in lib/openapi/helpers.ts to avoid redundancy and ensure consistent bug fixes.

Fix in Cursor Fix in Web

} from "../../../../lib/openapi/types";
import { ResourceSectionClient } from "./resource-section-client";
import { MethodContentClient } from "./method-content-client";
import { SchemaSectionClient } from "./schema-section-client";
Copy link

Choose a reason for hiding this comment

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

Unused imports MethodContentClient and SchemaSectionClient

Low Severity

MethodContentClient and SchemaSectionClient are imported but never used in this file. The component only renders ResourceSectionClient. These imports should be removed.

Fix in Cursor Fix in Web

}

export function ContentActionsAppRouter({
mdPath,
Copy link

Choose a reason for hiding this comment

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

Unused mdPath prop in ContentActionsAppRouter

Low Severity

The mdPath prop is declared in the interface and destructured in the component but never used in the function body. Either remove it or implement the intended functionality (e.g., edit on GitHub link).

Fix in Cursor Fix in Web

// Simple sidebar context for same-page routing
const SidebarContext = createContext<{ samePageRouting: boolean }>({
samePageRouting: true,
});
Copy link

Choose a reason for hiding this comment

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

Unused SidebarContext is created but never consumed

Low Severity

SidebarContext is created with createContext and wrapped with Provider, but useContext(SidebarContext) is never called anywhere in the codebase. This context has no consumers and should be removed.

Fix in Cursor Fix in Web

</ExampleColumn>
</Section>
);
}
Copy link

Choose a reason for hiding this comment

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

Entire MethodContent server component is unused

Medium Severity

The MethodContent async server component (258 lines) is defined but never imported or used anywhere. The app uses MethodContentClient instead. This entire file appears to be dead code.

Fix in Cursor Fix in Web

return { modelName, schema };
})
.filter((s): s is SchemaData => s !== null);
}
Copy link

Choose a reason for hiding this comment

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

Unused helper functions getResourceMethods and getResourceSchemas

Low Severity

getResourceMethods and getResourceSchemas are exported but never called anywhere in the codebase. They only appear in their definitions and the migration plan markdown. Remove these unused functions.

Fix in Cursor Fix in Web

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.

3 participants