Skip to content

Commit f7bf7d7

Browse files
authored
feat(studio): move data api docs to integrations section (supabase#42749)
Feature / Refactor ## What is the current behavior? Data API docs live at the `/api` route as a standalone page. Old links point to the previous location. ## What is the new behavior? Data API docs are moved to the integrations section with a dedicated docs tab and settings tab. Old links are cleaned up, a mobile menu is added for data API docs navigation, and minor code review fixes are applied. ## Additional context Resolves FE-2517 ## Summary by CodeRabbit * **New Features** * Revamped API docs UI with reusable section layout, language toggle (JS/Bash), API key selection, and improved code snippets * Added Data API docs tab, mobile navigation, and dedicated loading/error/disabled states * **Navigation Updates** * Moved API docs and related links into the Integrations/Data API area and added redirects to new routes * Updated various internal links to the new Data API settings and overview locations * **Tests** * Added comprehensive unit tests for Data API utilities
1 parent e99ea31 commit f7bf7d7

42 files changed

Lines changed: 1587 additions & 1116 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/studio/components/interfaces/Auth/Policies/PolicyTableRow/index.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import type { PostgresPolicy } from '@supabase/postgres-meta'
2+
import { useParams } from 'common'
23
import { noop } from 'lodash'
34
import { memo, useMemo } from 'react'
4-
5-
import { useParams } from 'common'
6-
import AlertError from 'components/ui/AlertError'
7-
import { InlineLink } from 'components/ui/InlineLink'
8-
import { useTablesRolesAccessQuery } from 'data/tables/tables-roles-access-query'
9-
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
105
import {
116
Card,
127
CardContent,
@@ -20,10 +15,15 @@ import {
2015
} from 'ui'
2116
import { Admonition } from 'ui-patterns'
2217
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
18+
2319
import { usePoliciesData } from '../PoliciesDataContext'
2420
import { PolicyRow } from './PolicyRow'
2521
import type { PolicyTable } from './PolicyTableRow.types'
2622
import { PolicyTableRowHeader } from './PolicyTableRowHeader'
23+
import AlertError from '@/components/ui/AlertError'
24+
import { InlineLink } from '@/components/ui/InlineLink'
25+
import { useTablesRolesAccessQuery } from '@/data/tables/tables-roles-access-query'
26+
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
2727

2828
export interface PolicyTableRowProps {
2929
table: PolicyTable
@@ -120,7 +120,10 @@ const PolicyTableRowComponent = ({
120120
<p className="text-foreground-light">
121121
No data will be selectable via Supabase APIs as this schema is not exposed. You may
122122
configure this in your project’s{' '}
123-
<InlineLink href={`/project/${ref}/settings/api`}>API settings</InlineLink>.
123+
<InlineLink href={`/project/${ref}/integrations/data_api/settings`}>
124+
API settings
125+
</InlineLink>
126+
.
124127
</p>
125128
</Admonition>
126129
)}

apps/studio/components/interfaces/Docs/Authentication.tsx

Lines changed: 100 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { PermissionAction } from '@supabase/shared-types/out/constants'
2-
import Link from 'next/link'
3-
42
import { useParams } from 'common'
5-
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
6-
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
7-
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
3+
84
import CodeSnippet from './CodeSnippet'
5+
import { DocSection } from './DocSection'
96
import Snippets from './Snippets'
7+
import { InlineLink } from '@/components/ui/InlineLink'
8+
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
9+
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
10+
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
1011

1112
interface AuthenticationProps {
1213
selectedLang: 'bash' | 'js'
@@ -35,83 +36,102 @@ const Authentication = ({ selectedLang, showApiKey }: AuthenticationProps) => {
3536
: 'SUPABASE_SERVICE_KEY'
3637

3738
return (
38-
<>
39-
<h2 className="doc-heading">Authentication</h2>
40-
<div className="doc-section">
41-
<article className="code-column text-foreground">
42-
<p>Supabase works through a mixture of JWT and Key auth.</p>
43-
<p>
44-
If no <code>Authorization</code> header is included, the API will assume that you are
45-
making a request with an anonymous user.
46-
</p>
47-
<p>
48-
If an <code>Authorization</code> header is included, the API will "switch" to the role
49-
of the user making the request. See the User Management section for more details.
50-
</p>
51-
<p>We recommend setting your keys as Environment Variables.</p>
52-
</article>
53-
</div>
39+
<div className="flex flex-col flex-1">
40+
<DocSection
41+
title="Authentication"
42+
content={
43+
<>
44+
<p>Supabase works through a mixture of JWT and Key auth.</p>
45+
<p>
46+
If no <code>Authorization</code> header is included, the API will assume that you are
47+
making a request with an anonymous user.
48+
</p>
49+
<p>
50+
If an <code>Authorization</code> header is included, the API will "switch" to the role
51+
of the user making the request. See the User Management section for more details.
52+
</p>
53+
<p>We recommend setting your keys as Environment Variables.</p>
54+
</>
55+
}
56+
/>
5457

55-
<h2 className="doc-heading">Client API Keys</h2>
56-
<div className="doc-section">
57-
<article className="code-column text-foreground">
58-
<p>
59-
Client keys allow "anonymous access" to your database, until the user has logged in.
60-
After logging in the keys will switch to the user's own login token.
61-
</p>
62-
<p>
63-
In this documentation, we will refer to the key using the name <code>SUPABASE_KEY</code>
64-
.
65-
</p>
66-
<p>
67-
We have provided you a Client Key to get started. You will soon be able to add as many
68-
keys as you like. You can find the <code>anon</code> key in the{' '}
69-
<Link href={`/project/${projectRef}/settings/api`}>API Settings</Link> page.
70-
</p>
71-
</article>
72-
<article className="code">
73-
<CodeSnippet
74-
selectedLang={selectedLang}
75-
snippet={Snippets.authKey('CLIENT API KEY', 'SUPABASE_KEY', defaultApiKey)}
76-
/>
77-
<CodeSnippet
78-
selectedLang={selectedLang}
79-
snippet={Snippets.authKeyExample(defaultApiKey, endpoint, {
80-
showBearer: false,
81-
})}
82-
/>
83-
</article>
84-
</div>
58+
<DocSection
59+
title="Client API Keys"
60+
content={
61+
<>
62+
<p>
63+
Client keys allow "anonymous access" to your database, until the user has logged in.
64+
After logging in the keys will switch to the user's own login token.
65+
</p>
66+
<p>
67+
In this documentation, we will refer to the key using the name{' '}
68+
<code>SUPABASE_KEY</code>.
69+
</p>
70+
<p>
71+
We have provided you a Client Key to get started. You will soon be able to add as many
72+
keys as you like. You can find the <code>anon</code> key in the{' '}
73+
<InlineLink href={`/project/${projectRef}/settings/api-keys`}>
74+
API Keys Settings
75+
</InlineLink>{' '}
76+
page.
77+
</p>
78+
</>
79+
}
80+
snippets={
81+
<>
82+
<CodeSnippet
83+
selectedLang={selectedLang}
84+
snippet={Snippets.authKey('CLIENT API KEY', 'SUPABASE_KEY', defaultApiKey)}
85+
/>
86+
<CodeSnippet
87+
selectedLang={selectedLang}
88+
snippet={Snippets.authKeyExample(defaultApiKey, endpoint, {
89+
showBearer: false,
90+
})}
91+
/>
92+
</>
93+
}
94+
/>
8595

86-
<h2 className="doc-heading">Service Keys</h2>
87-
<div className="doc-section">
88-
<article className="code-column text-foreground">
89-
<p>
90-
Service keys have FULL access to your data, bypassing any security policies. Be VERY
91-
careful where you expose these keys. They should only be used on a server and never on a
92-
client or browser.
93-
</p>
94-
<p>
95-
In this documentation, we will refer to the key using the name <code>SERVICE_KEY</code>.
96-
</p>
97-
<p>
98-
We have provided you with a Service Key to get started. Soon you will be able to add as
99-
many keys as you like. You can find the <code>service_role</code> in the{' '}
100-
<Link href={`/project/${projectRef}/settings/api`}>API Settings</Link> page.
101-
</p>
102-
</article>
103-
<article className="code">
104-
<CodeSnippet
105-
selectedLang={selectedLang}
106-
snippet={Snippets.authKey('SERVICE KEY', 'SERVICE_KEY', serviceApiKey)}
107-
/>
108-
<CodeSnippet
109-
selectedLang={selectedLang}
110-
snippet={Snippets.authKeyExample(serviceApiKey, endpoint, { keyName: 'SERVICE_KEY' })}
111-
/>
112-
</article>
113-
</div>
114-
</>
96+
<DocSection
97+
title="Service Keys"
98+
content={
99+
<>
100+
<p>
101+
Service keys have FULL access to your data, bypassing any security policies. Be VERY
102+
careful where you expose these keys. They should only be used on a server and never on
103+
a client or browser.
104+
</p>
105+
<p>
106+
In this documentation, we will refer to the key using the name{' '}
107+
<code>SERVICE_KEY</code>.
108+
</p>
109+
<p>
110+
We have provided you with a Service Key to get started. Soon you will be able to add
111+
as many keys as you like. You can find the <code>service_role</code> in the{' '}
112+
<InlineLink href={`/project/${projectRef}/settings/api-keys`}>
113+
API Keys Settings
114+
</InlineLink>{' '}
115+
page.
116+
</p>
117+
</>
118+
}
119+
snippets={
120+
<>
121+
<CodeSnippet
122+
selectedLang={selectedLang}
123+
snippet={Snippets.authKey('SERVICE KEY', 'SERVICE_KEY', serviceApiKey)}
124+
/>
125+
<CodeSnippet
126+
selectedLang={selectedLang}
127+
snippet={Snippets.authKeyExample(serviceApiKey, endpoint, {
128+
keyName: 'SERVICE_KEY',
129+
})}
130+
/>
131+
</>
132+
}
133+
/>
134+
</div>
115135
)
116136
}
117137

apps/studio/components/interfaces/Docs/CodeSnippet.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useParams } from 'common'
2-
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
3-
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
42
import { SimpleCodeBlock } from 'ui'
53

4+
import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
5+
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
6+
67
interface CodeSnippetProps {
78
selectedLang: 'bash' | 'js'
89
snippet: {
@@ -33,11 +34,13 @@ const CodeSnippet = ({ selectedLang, snippet }: CodeSnippetProps) => {
3334

3435
if (!snippet[selectedLang]) return null
3536
return (
36-
<div className="codeblock-container">
37-
<h4>{snippet.title}</h4>
38-
<SimpleCodeBlock className={snippet[selectedLang]?.language} onCopy={handleCopy}>
39-
{snippet[selectedLang]?.code}
40-
</SimpleCodeBlock>
37+
<div>
38+
<h4 className="heading-default mb-2">{snippet.title}</h4>
39+
<div className="[&_.codeBlock]:p-0 [&_.token-line]:text-sm">
40+
<SimpleCodeBlock className={snippet[selectedLang]?.language} onCopy={handleCopy}>
41+
{snippet[selectedLang]?.code}
42+
</SimpleCodeBlock>
43+
</div>
4144
</div>
4245
)
4346
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ReactNode } from 'react'
2+
import { cn } from 'ui'
3+
4+
interface DocSectionProps {
5+
title: ReactNode
6+
id?: string
7+
content: ReactNode
8+
snippets?: ReactNode
9+
className?: string
10+
}
11+
12+
export const DocSection = ({ title, id, content, snippets, className }: DocSectionProps) => {
13+
return (
14+
<div className={cn('grid grid-cols-1 lg:grid-cols-2 border-b', className)} id={id}>
15+
<article className="text-foreground-light prose prose-sm p-6 lg:py-10 lg:pr-10 lg:pl-0 flex-1">
16+
{title && <h2 className="heading-subTitle mb-4">{title}</h2>}
17+
{content}
18+
</article>
19+
<article className={cn('bg flex-1 lg:border-l space-y-6 px-6 pb-6 lg:py-10')}>
20+
{snippets}
21+
</article>
22+
</div>
23+
)
24+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type showApiKey = {
1+
export type ShowApiKey = {
22
key: string
33
name: string
44
}

apps/studio/components/interfaces/Docs/GeneralContent.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import Authentication from 'components/interfaces/Docs/Authentication'
2-
import Introduction from 'components/interfaces/Docs/Introduction'
3-
import RpcIntroduction from 'components/interfaces/Docs/Pages/Rpc/Introduction'
4-
import TablesIntroduction from 'components/interfaces/Docs/Pages/Tables/Introduction'
5-
import { UserManagement } from 'components/interfaces/Docs/Pages/UserManagement'
1+
import { DocSection } from './DocSection'
2+
import Authentication from '@/components/interfaces/Docs/Authentication'
3+
import Introduction from '@/components/interfaces/Docs/Introduction'
4+
import RpcIntroduction from '@/components/interfaces/Docs/Pages/Rpc/Introduction'
5+
import TablesIntroduction from '@/components/interfaces/Docs/Pages/Tables/Introduction'
6+
import { UserManagement } from '@/components/interfaces/Docs/Pages/UserManagement'
67

78
interface GeneralContentProps {
89
page?: string
@@ -21,9 +22,9 @@ export const GeneralContent = ({ selectedLang, page, showApiKey }: GeneralConten
2122
if (selected == 'rpc-intro') return <RpcIntroduction />
2223
else
2324
return (
24-
<div>
25-
<h2 className="m-4">Not found</h2>
26-
<p className="m-4"> Looks like you went somewhere that nobody knows.</p>
27-
</div>
25+
<DocSection
26+
title="Not found"
27+
content={<p>Looks like you went somewhere that nobody knows.</p>}
28+
/>
2829
)
2930
}

0 commit comments

Comments
 (0)