diff --git a/apps/docs/app/reference/[...slug]/page.tsx b/apps/docs/app/reference/[...slug]/page.tsx index 0ed5497704c..560623c6408 100644 --- a/apps/docs/app/reference/[...slug]/page.tsx +++ b/apps/docs/app/reference/[...slug]/page.tsx @@ -46,7 +46,7 @@ export default async function ReferencePage(props: { params: Promise<{ slug: Arr } else if (isCliReference) { return } else if (isApiReference) { - return + return } else if (isSelfHostingReference) { return ( diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index b0b872ff4e7..08c99b5eed0 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -3630,7 +3630,7 @@ export const references = [ label: 'Management API', description: 'something about the reference', icon: '/docs/img/icons/api-icon.svg', - url: '/reference/management-api/start', + url: '/reference/api/introduction', }, ], }, diff --git a/apps/docs/features/docs/Reference.apiPage.tsx b/apps/docs/features/docs/Reference.apiPage.tsx index 3e77558f3e6..e8344cb3a85 100644 --- a/apps/docs/features/docs/Reference.apiPage.tsx +++ b/apps/docs/features/docs/Reference.apiPage.tsx @@ -1,13 +1,32 @@ +import { notFound, redirect } from 'next/navigation' + import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu' import { reference_api } from '~/components/Navigation/NavigationMenu/NavigationMenu.constants' -import { ClientLibIntroduction } from '~/features/docs/Reference.introduction' +import { getSectionsBySlug } from '~/features/docs/Reference.generated.singleton' import { ReferenceNavigation } from '~/features/docs/Reference.navigation' import { ReferenceContentScrollHandler } from '~/features/docs/Reference.navigation.client' -import { RefSections } from '~/features/docs/Reference.sections' +import { SectionSwitch } from '~/features/docs/Reference.sections' import { LayoutMainContent } from '~/layouts/DefaultLayout' import { SidebarSkeleton } from '~/layouts/MainSkeleton' -export async function ApiReferencePage() { +export async function ApiReferencePage({ path }: { path: Array }) { + const operationSlug = path[0] + + if (!operationSlug) { + redirect('/reference/api/introduction') + } + + return +} + +// DOCS-1268: one endpoint per page, reusing the same SectionSwitch (and +// therefore the same ApiEndpointSection/MarkdownSection) that used to render +// every operation on the single monolithic /reference/api page. +async function ApiOperationPage({ operationSlug }: { operationSlug: string }) { + const sectionsBySlug = await getSectionsBySlug('api', 'latest') + const section = sectionsBySlug?.get(operationSlug) + if (!section) notFound() + return ( } >
- - +
diff --git a/apps/docs/features/docs/Reference.navigation.client.tsx b/apps/docs/features/docs/Reference.navigation.client.tsx index 4ddf294fd20..b676573baaf 100644 --- a/apps/docs/features/docs/Reference.navigation.client.tsx +++ b/apps/docs/features/docs/Reference.navigation.client.tsx @@ -239,11 +239,16 @@ export function RefLink({ section, skipChildren = false, className, + realNavigation, }: { basePath: string section: AbbrevApiReferenceSection skipChildren?: boolean className?: string + // Spike (DOCS-1268): when true, this link does a real navigation instead of + // the scroll-hijack below — used only by the API reference, whose endpoints + // are now real pages. Undefined everywhere else preserves current behavior. + realNavigation?: boolean }) { const ref = useRef(null) @@ -260,8 +265,11 @@ export function RefLink({ }, [isActive, className]) const onClick = useCallback( - (evt: MouseEvent) => createReferenceSubsectionNavigator(href, section.slug)(evt), - [href, section.slug] + (evt: MouseEvent) => { + if (realNavigation) return + createReferenceSubsectionNavigator(href, section.slug)(evt) + }, + [href, section.slug, realNavigation] ) if (!('title' in section)) return null @@ -272,13 +280,13 @@ export function RefLink({ return ( <> {isCompoundSection ? ( - + ) : ( { return (
  • - +
  • ) })} diff --git a/apps/docs/features/docs/Reference.navigation.tsx b/apps/docs/features/docs/Reference.navigation.tsx index 51e594b2874..393624245c3 100644 --- a/apps/docs/features/docs/Reference.navigation.tsx +++ b/apps/docs/features/docs/Reference.navigation.tsx @@ -19,6 +19,10 @@ interface ReferenceNavigationProps { libPath: string version: string isLatestVersion: boolean + // Spike (DOCS-1268): API reference sidebar links navigate to real pages + // instead of scrolling within one giant page. SDK/CLI/self-hosting callers + // never pass this, so their behavior is unchanged. + realNavigation?: boolean } export async function ReferenceNavigation({ @@ -28,6 +32,7 @@ export async function ReferenceNavigation({ libPath, version, isLatestVersion, + realNavigation, }: ReferenceNavigationProps) { const navSections = await getReferenceSections(libraryId, version) const filteredNavSections = navSections?.filter((section) => section.title !== 'Auth') @@ -46,11 +51,11 @@ export async function ReferenceNavigation({ {displayedNavSections?.map((section, index) => section.type === 'category' ? (
  • - +
  • ) : (
  • - +
  • ) )} @@ -64,9 +69,11 @@ const topLvlRefNavItemStyles = 'leading-5' function RefCategory({ basePath, section, + realNavigation, }: { basePath: string section: AbbrevApiReferenceSection + realNavigation?: boolean }) { if (!('items' in section && section.items && section.items.length > 0)) return null @@ -77,7 +84,7 @@ function RefCategory({
      {section.items?.map((item) => (
    • - +
    • ))}
    diff --git a/apps/docs/features/docs/Reference.utils.ts b/apps/docs/features/docs/Reference.utils.ts index 5824f27901a..5fe986caaee 100644 --- a/apps/docs/features/docs/Reference.utils.ts +++ b/apps/docs/features/docs/Reference.utils.ts @@ -95,6 +95,21 @@ async function generateStaticParamsForSdkVersion(sdkId: string, version: string) })) } +// Spike (DOCS-1268): one static page per Management API endpoint, in addition +// to the existing bare `/reference/api` monolith. Deliberately does not reuse +// generateStaticParamsForSdkVersion's output shape — that function bakes in a +// 'crawlers' path segment for a separate crawler-only mechanism unrelated to +// these human-facing per-operation URLs. +async function generateStaticParamsForApi() { + const flattenedSections = await getFlattenedSections('api', 'latest') + + return (flattenedSections || []) + .filter((section) => section.type !== 'category' && !!section.slug) + .map((section) => ({ + slug: ['api', section.slug], + })) +} + export async function generateReferenceStaticParams() { const sdkPages = clientSdkIds .flatMap((sdkId) => @@ -117,6 +132,7 @@ export async function generateReferenceStaticParams() { { slug: ['api'], }, + ...(await generateStaticParamsForApi()), ] const selfHostingPages = selfHostingServices.map((service) => ({ @@ -178,9 +194,35 @@ export async function generateReferenceMetadata( description: 'CLI reference for the Supabase CLI', } } else if (isApiReference) { + const { path } = parsedPath + const operationSlug = path[0] + + const flattenedSections = operationSlug + ? await getFlattenedSections('api', 'latest') + : undefined + const sectionTitle = flattenedSections?.find((section) => section.slug === operationSlug)?.title + + const url = [BASE_PATH, 'reference', 'api', operationSlug].filter(Boolean).join('/') + const images = generateOpenGraphImageMeta({ + type: 'API Reference', + title: `Management API${sectionTitle ? `: ${sectionTitle}` : ''}`, + }) + return { - title: 'Management API Reference | Supabase Docs', - description: 'Management API reference for the Supabase API', + title: `${sectionTitle ? `${sectionTitle} | ` : ''}Management API Reference | Supabase Docs`, + description: `Management API reference for the Supabase API${sectionTitle ? `: ${sectionTitle}` : ''}`, + ...(operationSlug + ? { + alternates: { + canonical: url, + }, + } + : {}), + openGraph: { + ...parentOg, + url, + images, + }, } } else if (isSelfHostingReference) { return { diff --git a/apps/docs/middleware.ts b/apps/docs/middleware.ts index fd885725c23..cd31319d43f 100644 --- a/apps/docs/middleware.ts +++ b/apps/docs/middleware.ts @@ -63,7 +63,10 @@ export function middleware(request: NextRequest) { return NextResponse.rewrite(new URL(rewritePath, request.url)) } - if (lib === 'api') { + // Spike (DOCS-1268): only the bare /reference/api needs normalizing now. + // /reference/api/ has its own statically generated page — don't + // collapse it back to the monolith. + if (lib === 'api' && !maybeVersion) { const rewritePath = [REFERENCE_PATH, 'api'].join('/') return NextResponse.rewrite(new URL(rewritePath, request.url)) } diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index 2ec7c41125d..866bef5ec9e 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -2081,7 +2081,17 @@ module.exports = [ { permanent: true, source: '/docs/reference/api', - destination: '/docs/reference/api/start', + destination: '/docs/reference/api/introduction', + }, + // 'start' was never a real API reference slug — it only ever worked because + // the old /reference/api/* routing collapsed every sub-path to the same + // monolith, and the bare /docs/reference/api redirect pointed here for years. + // Keep redirecting so external links and bookmarks don't 404 now that only + // real slugs resolve. + { + permanent: true, + source: '/docs/reference/api/start', + destination: '/docs/reference/api/introduction', }, { permanent: true,