Files
supabase/apps/studio/components/interfaces/Integrations/DataApi/DataApi.utils.ts
Alaister Young d0d41e00d6 [FE-3035] fix(studio): show /rest/v1/ suffix on Data API overview URL (#45045)
The Data API overview page (`/integrations/data_api/overview`) was
showing the project URL as `https://xxx.supabase.co`, but the documented
Data API base URL is `https://xxx.supabase.co/rest/v1/`. This normalizes
the URL so it matches the docs.

**Changed:**
- `getApiEndpoint` now appends `/rest/v1/` to the resolved endpoint
(only used by the Data API overview card, so no other dashboard URLs are
affected)

## To test

- Visit `/dashboard/project/_/integrations/data_api/overview` and
confirm the API URL field ends with `/rest/v1/`
- Switch the database selector between primary, a read replica, and (if
available) a load balancer — all should show a URL ending in `/rest/v1/`
- With a custom domain active, the custom domain URL should also end
with `/rest/v1/`

Addresses
[FE-3035](https://linear.app/supabase/issue/FE-3035/dashboard-data-api-page-shows-inconsistent-api-url)

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

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
* API endpoints are now properly normalized to ensure consistent path
formatting with the `/rest/v1/` suffix across all endpoint sources.
* Fixed URL handling for custom domain and load balancer endpoint
selection.
* Enhanced replica database URL handling to ensure correct trailing
slash formatting.

* **Tests**
* Updated test expectations and added new test cases to verify proper
endpoint normalization behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-04-20 18:55:54 +08:00

79 lines
2.4 KiB
TypeScript

import type { ProjectJsonSchemaPaths } from '@/data/docs/project-json-schema-query'
import type { LoadBalancer } from '@/data/read-replicas/load-balancers-query'
import type { Database } from '@/data/read-replicas/replicas-query'
import { snakeToCamel } from '@/lib/helpers'
/**
* Resolves the API endpoint URL based on the selected database, custom domain
* status, and load balancer configuration. The returned URL is normalized to
* end with `/rest/v1/` to match the Data API base path documented elsewhere.
*/
export function getApiEndpoint({
selectedDatabaseId,
projectRef,
resolvedEndpoint,
loadBalancers,
selectedDatabase,
}: {
selectedDatabaseId: string | undefined
projectRef: string | undefined
resolvedEndpoint: string | undefined
loadBalancers: Array<LoadBalancer> | undefined
selectedDatabase: Database | undefined
}): string {
const loadBalancerSelected = selectedDatabaseId === 'load-balancer'
if (selectedDatabaseId === projectRef && !!resolvedEndpoint) {
return withDataApiPath(resolvedEndpoint)
}
if (loadBalancerSelected) {
return withDataApiPath(loadBalancers?.[0]?.endpoint)
}
return withDataApiPath(selectedDatabase?.restUrl)
}
function withDataApiPath(url: string | undefined): string {
if (!url) return ''
const trimmed = url.replace(/\/+$/, '')
return /\/rest\/v1$/.test(trimmed) ? `${trimmed}/` : `${trimmed}/rest/v1/`
}
export type EnrichedEntity = { id: string; displayName: string; camelCase: string }
export type EntityMap = Record<string, EnrichedEntity>
/**
* Partitions JSON schema paths into resource and RPC entity maps.
*/
export function buildEntityMaps(paths: ProjectJsonSchemaPaths | undefined): {
resources: EntityMap
rpcs: EntityMap
} {
const RPC_PREFIX = 'rpc/'
return Object.keys(paths ?? {}).reduce<{ resources: EntityMap; rpcs: EntityMap }>(
(acc, name) => {
const trimmedName = name.slice(1)
if (!trimmedName.length) return acc
const isRpc = trimmedName.startsWith(RPC_PREFIX)
const id = isRpc ? trimmedName.slice(RPC_PREFIX.length) : trimmedName
const enriched: EnrichedEntity = {
id,
displayName: id.replace(/_/g, ' '),
camelCase: snakeToCamel(id),
}
if (isRpc) {
acc.rpcs[id] = enriched
} else {
acc.resources[id] = enriched
}
return acc
},
{ resources: {}, rpcs: {} }
)
}