fix(docs): resolve legacy sdk reference urls (#51064)

I made the crawler renderer resolve legacy JavaScript and Dart reference
slugs to their current sections, and updated authored guide and SDK spec
links to use them. Exact slugs still win, ambiguous bare slugs still
return 404, and `file-buckets-listv2` remains a section slug in
canonical links. I kept the www redirect work in a separate draft PR
because the apps deploy independently.

## To test

- [x] On the Docs preview, request `reference/javascript/order` and
`reference/dart/get-user` with a bot user agent. Expect the intended
heading and canonical URL.
- [x] Request `reference/javascript/file-buckets-listv2` with bot and
browser user agents. Expect it to open the list v2 section.
- [x] Request `reference/swift/get-user` and the Kotlin reference root
with a bot user agent. Expect the intended heading.
- [x] Open the Storage quickstart guide and follow its upload reference
link. Expect the current JavaScript upload section.

## Linear

refs GROWTH-1293


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Reference pages now resolve legacy aliases and ambiguous slugs more
accurately, with canonical links that preserve explicit SDK versions.
* SDK version paths are recognized only when the full path segment
matches the version format, improving reference-page routing.

* **Documentation**
* Updated API reference links across authentication, storage, security,
and SDK guides to point to current pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Pamela Chia authored and GitHub committed 2026-09-30 17:11:53 -07:00
1 parent 2303de33f5
commit 5a7c0d6d84
24 files changed
+206 -35

No files matched your search

+125
View File
@@ -0,0 +1,125 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const reference = vi.hoisted(() => ({
sections: [] as Array<{ id: string; slug: string; title: string; type: string }>,
}))
vi.mock('~/features/docs/Reference.generated.singleton', () => ({
getFlattenedSections: async () => reference.sections,
getFunctionsList: async () => [],
getTypeSpec: async () => undefined,
}))
vi.mock('~/features/docs/Reference.mdx', () => ({
getRefMarkdown: async () => 'Reference content',
}))
vi.mock('next/navigation', () => ({
notFound: () => {
throw new Error('notFound')
},
}))
import { GET } from './route'
function section(slug: string, title: string, type = 'markdown') {
return { id: slug, slug, title, type }
}
async function html(path: string) {
const response = await GET(new Request(`https://supabase.com${path}`))
expect(response.status).toBe(200)
return response.text()
}
beforeEach(() => {
reference.sections = []
})
describe('crawler reference aliases', () => {
it('resolves unique bare JavaScript modifier and filter slugs', async () => {
reference.sections = [
section('using-modifiers-order', 'Order'),
section('using-filters-or', 'Or'),
section('using-modifiers-maybesingle', 'Maybe single'),
]
for (const [requested, canonical] of [
['order', 'using-modifiers-order'],
['or', 'using-filters-or'],
['maybeSingle', 'using-modifiers-maybesingle'],
]) {
expect(await html(`/reference/javascript/${requested}`)).toContain(
`href="https://supabase.com/docs/reference/javascript/${canonical}"`
)
}
})
it('resolves storage slugs and keeps an exact versioned slug', async () => {
reference.sections = [
section('file-buckets-createbucket', 'Create bucket'),
section('file-buckets-upload', 'Upload'),
section('file-buckets-createsignedurl', 'Create signed URL'),
section('file-buckets-updatebucket', 'Update bucket'),
section('file-buckets-listv2', 'List files v2'),
]
for (const [requested, canonical] of [
['storage-createbucket', 'file-buckets-createbucket'],
['storage-from-upload', 'file-buckets-upload'],
['storage-from-createsignedurl', 'file-buckets-createsignedurl'],
['storage-updatebucket', 'file-buckets-updatebucket'],
['file-buckets-listv2', 'file-buckets-listv2'],
]) {
expect(await html(`/reference/javascript/${requested}`)).toContain(
`href="https://supabase.com/docs/reference/javascript/${canonical}"`
)
}
})
it('resolves SDK-specific user and root aliases', async () => {
reference.sections = [
section('introduction', 'Introduction'),
section('auth-admin', 'Admin'),
section('auth-getuser', 'Get user'),
section('auth-currentuser', 'Current user'),
]
expect(await html('/reference/javascript')).toContain(
'href="https://supabase.com/docs/reference/javascript/introduction"'
)
expect(await html('/reference/javascript/start')).toContain('JavaScript: Introduction')
expect(await html('/reference/javascript/admin-api')).toContain('JavaScript: Admin')
expect(await html('/reference/swift/get-user')).toContain(
'href="https://supabase.com/docs/reference/swift/auth-getuser"'
)
expect(await html('/reference/dart/get-user')).toContain(
'href="https://supabase.com/docs/reference/dart/auth-currentuser"'
)
expect(await html('/reference/kotlin')).toContain('Kotlin: Introduction')
})
it('keeps exact matches ahead of aliases and rejects ambiguous bare slugs', async () => {
reference.sections = [
section('delete', 'Delete'),
section('auth-passkey-list', 'Passkeys'),
section('file-buckets-list', 'Files'),
]
expect(await html('/reference/javascript/delete')).toContain('JavaScript: Delete')
expect(await html('/reference/javascript/storage-list')).toContain('JavaScript: Files')
await expect(html('/reference/javascript/list')).rejects.toThrow('notFound')
})
it('uses canonical paths for explicit versions', async () => {
reference.sections = [section('auth-update', 'Update')]
expect(await html('/reference/javascript/v1/auth-update')).toContain(
'href="https://supabase.com/docs/reference/javascript/v1/auth-update"'
)
reference.sections = [section('auth-updateuser', 'Update')]
expect(await html('/reference/javascript/v2/auth-updateuser')).toContain(
'href="https://supabase.com/docs/reference/javascript/auth-updateuser"'
)
})
})
+30 -4
View File
@@ -43,10 +43,23 @@ export async function GET(request: Request) {
url,
}
})
section = flattenedSections.find(
(section) =>
(section.type === 'markdown' || section.type === 'function') && section.slug === slug
const contentSections = flattenedSections.filter(
(section) => section.type === 'markdown' || section.type === 'function'
)
section = contentSections.find((section) => section.slug === slug)
if (!section) {
const legacySlug = slug?.toLowerCase()
const replacement = legacyReferenceSlug(lib, version, legacySlug)
if (replacement) {
section = contentSections.find((section) => section.slug?.toLowerCase() === replacement)
} else if (legacySlug && version === 'v2' && (lib === 'javascript' || lib === 'dart')) {
const matches = contentSections.filter((section) =>
section.slug?.toLowerCase().endsWith(`-${legacySlug}`)
)
if (matches.length === 1) section = matches[0]
}
}
} catch {}
if (!section) {
@@ -56,7 +69,7 @@ export async function GET(request: Request) {
const html = htmlShell(
lib,
isVersion ? version : null,
slug,
section.slug ?? slug,
section,
libraryNav(sectionsWithUrl) + (await sectionDetails(lib, isVersion ? version : null, section))
)
@@ -66,6 +79,17 @@ export async function GET(request: Request) {
return response
}
function legacyReferenceSlug(lib: string, version: string, slug?: string) {
if (!slug || slug === 'start') return 'introduction'
if (lib === 'swift' && version === 'v2' && slug === 'get-user') return 'auth-getuser'
if (version !== 'v2' || (lib !== 'javascript' && lib !== 'dart')) return undefined
if (slug === 'admin-api') return 'auth-admin'
if (slug === 'get-user') return lib === 'dart' ? 'auth-currentuser' : 'auth-getuser'
if (slug.startsWith('storage-from-')) return `file-buckets-${slug.slice('storage-from-'.length)}`
if (slug.startsWith('storage-')) return `file-buckets-${slug.slice('storage-'.length)}`
return undefined
}
function htmlShell(
lib: string,
version: string | null,
@@ -74,6 +98,7 @@ function htmlShell(
body: string
) {
const libraryName = REFERENCES[lib].name
const versionPath = version && version !== REFERENCES[lib].versions[0] ? '/' + version : ''
let title = libraryName + ': ' + (section.title ?? '')
return (
@@ -84,6 +109,7 @@ function htmlShell(
`<meta name="og:image" content="https://supabase.com/docs/img/supabase-og-image.png">` +
`<meta name="twitter:image" content="https://supabase.com/docs/img/supabase-og-image.png">` +
`<link rel="canonical" href="https://supabase.com/docs/reference/${lib}` +
versionPath +
(slug ? '/' + slug : '') +
`">` +
'</head>' +
@@ -38,7 +38,7 @@ In the **sign-in flow**, the user signs in (upgrading the session to AAL1) and t
<Admonition type="note">
[TOTP MFA API](/docs/reference/javascript/auth-mfa-api) is free to use and is enabled on all Supabase projects by default.
[TOTP MFA API](/docs/reference/javascript/auth-mfa) is free to use and is enabled on all Supabase projects by default.
</Admonition>
@@ -637,7 +637,7 @@ Response:
<Admonition type="note">
For complete API documentation, see the [OAuth Admin API reference](/docs/reference/javascript/auth-admin-oauth-admin).
For complete API documentation, see the [OAuth Admin API reference](/docs/reference/javascript/oauth-admin).
</Admonition>
@@ -849,7 +849,7 @@ It's a good practice to provide a settings page where users can view all authori
</Admonition>
For complete API reference, see the [OAuth methods in supabase-js](/docs/reference/javascript/auth-admin-oauth-server).
For complete API reference, see the [OAuth methods in supabase-js](/docs/reference/javascript/oauth-server).
## Next steps
@@ -36,7 +36,7 @@ Leaked password protection is available on the Pro Plan and above.
Users will need to be recently logged in to change their password without requiring reauthentication. (A user is considered recently logged in if the session was created within the last 24 hours.) If disabled, a user can change their password at any time.
When enabled, a `nonce` will be sent to the user and this nonce must be validated before the a password change can occur. This can be triggered with the [reauthenticate()](/docs/reference/javascript/auth-reauthentication) API call.
When enabled, a `nonce` will be sent to the user and this nonce must be validated before the a password change can occur. This can be triggered with the [reauthenticate()](/docs/reference/javascript/auth-reauthenticate) API call.
```
const { error } = await supabase.auth.reauthenticate()
@@ -14,7 +14,7 @@ When a JWT is issued by Supabase Auth, the key used to create its [signature](ht
| System | Type | Description |
| ----------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Legacy | JWT&nbsp;secret | Initially Supabase was designed to use a single shared secret key to sign all JWTs. This includes <span className="whitespace-nowrap!">the `anon` and `service_role`</span> keys, all user access tokens including some [Storage pre-signed URLs](/docs/reference/javascript/storage-from-createsignedurl). **No longer recommended.** Available for backward compatibility. |
| Legacy | JWT&nbsp;secret | Initially Supabase was designed to use a single shared secret key to sign all JWTs. This includes <span className="whitespace-nowrap!">the `anon` and `service_role`</span> keys, all user access tokens including some [Storage pre-signed URLs](/docs/reference/javascript/file-buckets-createsignedurl). **No longer recommended.** Available for backward compatibility. |
| Signing&nbsp;keys | Asymmetric&nbsp;key (RSA, Elliptic Curves) | A JWT signing key based on [public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography) (RSA, Elliptic Curves) that follows industry best practices and significantly improves the security, reliability and performance of your applications. |
| Signing&nbsp;keys | Shared&nbsp;secret&nbsp;key | A JWT signing key based on a [shared secret](https://en.wikipedia.org/wiki/HMAC). |
@@ -30,7 +30,7 @@ Various products at Supabase have their own hardening and configuration guides,
- [Object ownership](/docs/guides/storage/security/ownership)
- [Access control](/docs/guides/storage/security/access-control)
- The Storage API docs contain hints about required [RLS policy permissions](/docs/reference/javascript/storage-createbucket)
- The Storage API docs contain hints about required [RLS policy permissions](/docs/reference/javascript/file-buckets-createbucket)
- [Custom roles with the storage schema](/docs/guides/storage/schema/custom-roles)
## Realtime
@@ -69,7 +69,7 @@ Use the Auth admin API to create providers. You need your project's secret key f
<Admonition type="danger">
The JavaScript examples use a [supabase-js](/docs/reference/javascript/start) client created with the secret key (`SUPABASE_SECRET_KEY`), which must only run in trusted server-side environments.
The JavaScript examples use a [supabase-js](/docs/reference/javascript/introduction) client created with the secret key (`SUPABASE_SECRET_KEY`), which must only run in trusted server-side environments.
</Admonition>
@@ -29,7 +29,7 @@ const { data, error } = await supabase.storage.createBucket('avatars', {
})
```
[Reference.](/docs/reference/javascript/storage-createbucket)
[Reference.](/docs/reference/javascript/file-buckets-createbucket)
</TabPanel>
@@ -17,8 +17,8 @@ When a bucket is set to **Private** all operations are subject to access control
The only ways to download assets within a private bucket is to:
- Use the [download method](/docs/reference/javascript/storage-from-download) by providing an authorization header containing your user's JWT. The RLS policy you create on the `storage.objects` table will use this user to determine if they have access.
- Create a signed URL with the [`createSignedUrl` method](/docs/reference/javascript/storage-from-createsignedurl) that can be accessed for a limited time.
- Use the [download method](/docs/reference/javascript/file-buckets-download) by providing an authorization header containing your user's JWT. The RLS policy you create on the `storage.objects` table will use this user to determine if they have access.
- Create a signed URL with the [`createSignedUrl` method](/docs/reference/javascript/file-buckets-createsignedurl) that can be accessed for a limited time.
#### Example use cases:
@@ -17,7 +17,7 @@ Smart CDN caching is automatically enabled for [Pro Plan and above](/pricing).
## Cache duration
When Smart CDN is enabled, the asset is cached on the CDN for as long as possible. You can still control how long assets are stored in the browser using the [`cacheControl`](/docs/reference/javascript/storage-from-upload) option when uploading a file. Smart CDN caching works with all types of storage operations including signed URLs.
When Smart CDN is enabled, the asset is cached on the CDN for as long as possible. You can still control how long assets are stored in the browser using the [`cacheControl`](/docs/reference/javascript/file-buckets-upload) option when uploading a file. Smart CDN caching works with all types of storage operations including signed URLs.
When a file is updated or deleted, the CDN cache is automatically invalidated to reflect the change (including transformed images). It can take **up to 60 seconds** for the CDN cache to be invalidated as the asset metadata has to propagate across all the data-centers around the globe.
@@ -69,7 +69,7 @@ values
const { data, error } = await supabase.storage.createBucket('avatars')
```
[Reference.](/docs/reference/javascript/storage-createbucket)
[Reference.](/docs/reference/javascript/file-buckets-createbucket)
</TabPanel>
<$Show if="sdk:dart">
@@ -152,7 +152,7 @@ const { data, error } = await supabase.storage
.upload('public/avatar1.png', avatarFile)
```
[Reference.](/docs/reference/javascript/storage-from-upload)
[Reference.](/docs/reference/javascript/file-buckets-upload)
</TabPanel>
<$Show if="sdk:dart">
@@ -220,7 +220,7 @@ You can download a file from the Dashboard, or within a browser using our JS lib
const { data, error } = await supabase.storage.from('avatars').download('public/avatar1.png')
```
[Reference.](/docs/reference/javascript/storage-from-download)
[Reference.](/docs/reference/javascript/file-buckets-download)
</TabPanel>
<$Show if="sdk:dart">
@@ -237,7 +237,7 @@ void main() async {
}
```
[Reference.](/docs/reference/dart/storage-from-download)
[Reference.](/docs/reference/dart/file-buckets-download)
</TabPanel>
</$Show>
@@ -20,11 +20,11 @@ If you need different `SELECT` policies for different Storage actions, such as l
<Admonition type="note">
The RLS policies required for different operations are documented [here](/docs/reference/javascript/storage-createbucket)
The RLS policies required for different operations are documented [here](/docs/reference/javascript/file-buckets-createbucket)
</Admonition>
For example, the only RLS policy required for [uploading](/docs/reference/javascript/storage-from-upload) objects is to grant the `INSERT` permission to the `storage.objects` table.
For example, the only RLS policy required for [uploading](/docs/reference/javascript/file-buckets-upload) objects is to grant the `INSERT` permission to the `storage.objects` table.
To allow overwriting files using the `upsert` functionality you will need to additionally grant `SELECT` and `UPDATE` permissions.
@@ -77,7 +77,7 @@ final response = await supabase.storage
);
```
[Reference.](/docs/reference/dart/storage-from-download)
[Reference.](/docs/reference/dart/file-buckets-download)
</TabPanel>
</$Show>
@@ -69,7 +69,7 @@ const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY, {
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
Read more about the [constructor options](/docs/reference/javascript/release-notes#explicit-constructor-options).
Read more about the [constructor options](/docs/reference/javascript/initializing).
### Auth methods
+1 -1
View File
@@ -37,7 +37,7 @@ export function parseReferencePath(slug: Array<string>) {
let maybeCrawlers: string | null
let path: string[]
;[sdkId, maybeVersion, maybeCrawlers, ...path] = slug
if (!/v\d+/.test(maybeVersion)) {
if (!/^v\d+$/.test(maybeVersion)) {
maybeVersion = null
path = [maybeCrawlers, ...path]
maybeCrawlers = maybeVersion
+20
View File
@@ -163,3 +163,23 @@ describe('docs middleware — /guides/* content negotiation', () => {
expect(rewrite).not.toContain('/api/guides-md/')
})
})
describe('docs middleware — reference paths', () => {
it('does not mistake listv2 for a version segment', () => {
const path = '/docs/reference/javascript/file-buckets-listv2'
expect(
middleware(makeRequest(path, { userAgent: 'Googlebot' })).headers.get(REWRITE_HEADER)
).toBe('https://supabase.com/docs/api/crawlers')
expect(middleware(makeRequest(path)).headers.get(REWRITE_HEADER)).toBe(
'https://supabase.com/docs/reference/javascript'
)
})
it('sends the Kotlin root to the crawler renderer for bots', () => {
expect(
middleware(makeRequest('/docs/reference/kotlin', { userAgent: 'Googlebot' })).headers.get(
REWRITE_HEADER
)
).toBe('https://supabase.com/docs/api/crawlers')
})
})
+2 -2
View File
@@ -43,7 +43,7 @@ export function middleware(request: NextRequest) {
let [, lib, maybeVersion, ...slug] = pathname.replace(REFERENCE_PATH, '').split('/')
if (clientSdkIds.includes(lib)) {
const version = /v\d+/.test(maybeVersion) ? maybeVersion : undefined
const version = /^v\d+$/.test(maybeVersion) ? maybeVersion : undefined
if (!version) {
slug = [maybeVersion, ...slug]
}
@@ -77,7 +77,7 @@ export function middleware(request: NextRequest) {
}
if (clientSdkIds.includes(lib)) {
const version = /v\d+/.test(maybeVersion) ? maybeVersion : null
const version = /^v\d+$/.test(maybeVersion) ? maybeVersion : null
const rewritePath = [REFERENCE_PATH, lib, version].filter(Boolean).join('/')
return NextResponse.rewrite(new URL(rewritePath, request.url))
}
+1 -1
View File
@@ -1553,7 +1553,7 @@ functions:
Retrieve URLs for assets in public buckets
title: 'from.getPublicUrl()'
notes: |
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/dart/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/dart/v1/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- Policy permissions required:
- `buckets` permissions: none
- `objects` permissions: none
+5 -5
View File
@@ -948,7 +948,7 @@ functions:
- Drives the full WebAuthn ceremony end to end: starts the challenge with the Supabase server, calls the `authenticator` you supply to prompt the user for biometrics or a security key, and verifies the credential with the server.
- Does not require an existing session. On success the session is persisted and an `AuthChangeEvent.signedIn` event is fired.
- `supabase_flutter` does not depend on a passkey plugin directly. Pass an implementation of `PasskeyAuthenticatorInterface`, such as the [`passkeys`](https://pub.dev/packages/passkeys) plugin's `PasskeyAuthenticator` (since `passkeys` `2.21.0`).
- For native flows or custom UI, use the lower-level [`auth.passkey`](/docs/reference/dart/auth-passkey-api) namespace instead.
- For native flows or custom UI, use the lower-level [`auth.passkey`](/docs/reference/dart/auth-passkey) namespace instead.
- Passkeys are a BETA feature and must be enabled for your project in the Supabase Dashboard under Authentication > Configuration > Passkeys.
- Platform setup the library cannot perform (Associated Domains on iOS/macOS, Digital Asset Links on Android, the `passkeys` web SDK on web) is documented in the `supabase_flutter` package README.
params:
@@ -982,7 +982,7 @@ functions:
- Drives the full WebAuthn ceremony end to end: starts the registration with the Supabase server, calls the `authenticator` you supply to create a credential on the device, and verifies it with the server.
- Requires a signed in (non-anonymous) user. If the user has verified MFA factors, the session has to be at `aal2` to manage passkeys.
- `supabase_flutter` does not depend on a passkey plugin directly. Pass an implementation of `PasskeyAuthenticatorInterface`, such as the [`passkeys`](https://pub.dev/packages/passkeys) plugin's `PasskeyAuthenticator` (since `passkeys` `2.21.0`).
- For native flows or custom UI, use the lower-level [`auth.passkey`](/docs/reference/dart/auth-passkey-api) namespace instead.
- For native flows or custom UI, use the lower-level [`auth.passkey`](/docs/reference/dart/auth-passkey) namespace instead.
- Passkeys are a BETA feature and must be enabled for your project in the Supabase Dashboard under Authentication > Configuration > Passkeys.
params:
- name: authenticator
@@ -2379,7 +2379,7 @@ functions:
- id: passkey-verify-registration
title: 'passkey.verifyRegistration()'
notes: |
Completes the registration of a new passkey and returns the stored [`Passkey`](/docs/reference/dart/auth-passkey-api).
Completes the registration of a new passkey and returns the stored [`Passkey`](/docs/reference/dart/auth-passkey).
params:
- name: challengeId
isOptional: false
@@ -5329,7 +5329,7 @@ functions:
Retrieve URLs for assets in public buckets
title: 'from.getPublicUrl()'
notes: |
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/dart/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/dart/file-buckets-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- Policy permissions required:
- `buckets` permissions: none
- `objects` permissions: none
@@ -5537,7 +5537,7 @@ functions:
title: 'from.downloadStream()'
notes: |
- The request is sent when the stream is listened to. A non-success response surfaces as a `StorageException` on the stream before any bytes are emitted.
- Prefer this over [`download()`](/docs/reference/dart/storage-from-download) for large files to keep memory usage low.
- Prefer this over [`download()`](/docs/reference/dart/file-buckets-download) for large files to keep memory usage low.
- Policy permissions required:
- `buckets` permissions: none
- `objects` permissions: `select`
+1 -1
View File
@@ -1532,7 +1532,7 @@ functions:
title: 'from.getPublicUrl()'
$ref: '@supabase/storage-js."lib/StorageFileApi".StorageFileApi.getPublicUrl'
notes: |
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/v1/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- Policy permissions required:
- `buckets` permissions: none
- `objects` permissions: none
+1 -1
View File
@@ -3333,7 +3333,7 @@ functions:
- id: from-get-public-url
title: from.getPublicUrl()
notes: |
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/swift/v1/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- RLS policy permissions required:
- `buckets` table permissions: none
- `objects` table permissions: none
+1 -1
View File
@@ -5691,7 +5691,7 @@ functions:
- id: from-get-public-url
title: from.getPublicUrl()
notes: |
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- The bucket needs to be set to public, either via [updateBucket()](/docs/reference/swift/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public"
- RLS policy permissions required:
- `buckets` table permissions: none
- `objects` table permissions: none