Files
supabase/apps/studio/data/analytics/service-health-query.ts
Jordi EnricandClaude Sonnet 4.6 4ca7e66153 feat(observability): migrate overview to service-health endpoint (#46100)
## Problem

The observability overview page fetched service health data by making
six separate calls to the generic \`logs.all\` endpoint with
hand-crafted SQL (via \`genChartQuery\`). This coupled the overview to
SQL internals and missed out on the purpose-built \`service-health\`
endpoint that accepts structured \`lql\` filters and a \`granularity\`
parameter.

## Fix

- Added \`/platform/projects/{ref}/analytics/endpoints/service-health\`
to \`platform.d.ts\`, including the \`ProjectServiceHealthResponse\`
schema and \`UsageApiController_getProjectServiceHealth\` operation.
- Created \`apps/studio/data/analytics/service-health-query.ts\` with a
\`getServiceHealth\` fetch function and \`useServiceHealthQuery\` hook
following the same pattern as other analytics query files.
- Added a \`serviceHealth\` key factory to
\`apps/studio/data/analytics/keys.ts\`.
- Rewrote \`useServiceHealthMetrics.ts\` to call the new endpoint per
service using \`lql\` selectors (\`s:postgres_logs\`, \`s:auth_logs\`,
etc.) and a \`granularity\` value derived from the selected interval
(\`1hr\` -> \`minute\`, \`1day\` -> \`hour\`, \`7day\` -> \`day\`). The
timeseries normalisation and chart data pipeline is unchanged.
- Updated the refresh handler in \`ObservabilityOverview.tsx\` to
invalidate the new query key prefix and removed the now-unused
\`postgrest-overview-metrics\` invalidation.

## How to test

- Navigate to a project's Observability > Overview page.
- Verify that the Service Health table loads data for all six services
(Database, Auth, Edge Functions, Realtime, Storage, Data API).
- Switch between the 1hr, 1day, and 7day interval selectors and confirm
the charts update.
- Click the Refresh button and confirm the charts reload.
- Click a bar in any chart and confirm navigation to the corresponding
logs page scoped to that time window.
- Confirm no regressions in the Database Infrastructure section (CPU,
RAM, disk, connections).

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

* **New Features**
* Centralized service‑health fetching for consistent cross‑service
metrics and improved charting.
* New analytics key and backend endpoint for project service‑health; API
schemas added.
* Backend support for an additional log‑drain type (hidden from the UI).

* **Bug Fixes**
  * Improved refresh behavior for service‑health data.
* Clear "No requests in this period" fallback and correct charts when
totals are zero.

* **Tests**
* Added unit tests for service‑health data extraction and
transformation.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46100?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 10:01:54 +02:00

69 lines
1.9 KiB
TypeScript

import { get, handleError } from '@/data/fetchers'
export type ServiceHealthGranularity = 'day' | 'hour' | 'minute'
export type ServiceHealthServiceData = {
error: number
ok: number
warning: number
total: number
}
/** Shape of a single result row returned by the service-health endpoint */
export type ServiceHealthResultRow = {
timestamp: string
postgres_logs: ServiceHealthServiceData
auth_logs: ServiceHealthServiceData
function_edge_logs: ServiceHealthServiceData
storage_logs: ServiceHealthServiceData
realtime_logs: ServiceHealthServiceData
postgrest_logs: ServiceHealthServiceData
edge_logs: ServiceHealthServiceData
supavisor_logs: ServiceHealthServiceData
function_logs: ServiceHealthServiceData
etl_replication_logs: ServiceHealthServiceData
}
export type ProjectServiceHealthResponse = {
error?: string | null
result?: ServiceHealthResultRow[]
}
export type ServiceHealthVariables = {
projectRef?: string
startDate?: string
endDate?: string
granularity?: ServiceHealthGranularity
}
export async function getServiceHealth(
{ projectRef, startDate, endDate, granularity }: ServiceHealthVariables,
signal?: AbortSignal
): Promise<ProjectServiceHealthResponse> {
if (!projectRef) throw new Error('Project ref is required')
if (!startDate) throw new Error('Start date is required')
if (!endDate) throw new Error('End date is required')
const { data, error } = await get('/platform/projects/{ref}/analytics/endpoints/service-health', {
params: {
path: { ref: projectRef },
query: {
iso_timestamp_start: startDate,
iso_timestamp_end: endDate,
granularity,
},
},
signal,
})
if (error) handleError(error)
const result = data as unknown as ProjectServiceHealthResponse
if (result?.error)
throw new Error(
typeof result.error === 'string' ? result.error : 'Failed to fetch service health'
)
return result
}