Files
supabase/apps/studio/lib/api/self-hosted/query.ts
T
Chris StavitskyandAli Waseem e217f747bd feat(studio) instrument sentry on table creation flow (#43673)
(working with @charislam @awaseem on this one)

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

Per
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md):
- ✅ `pnpm --filter studio build`
- ✅ `pnpm test:prettier`
- ✅ `pnpm format`

## What kind of change does this PR introduce?

Instruments `studio` app's table creation flow with Sentry.

## What is the current behavior?

The table creation flow isn't instrumented in detail, which means we
have less visibility into understanding performance problems or errors
within this specific flow.

## What is the new behavior?

The table creation flow is instrumented in detail. This instrumentation
allows for Sentry dashboards like this one (created based on locally
generated Supabase data), to help monitor anything going wrong with this
critical flow:

<img width="1915" height="1131" alt="image"
src="https://github.com/user-attachments/assets/e2c5380e-7b00-41c1-9109-3ee4e47ff63b"
/>

---------

Co-authored-by: Ali Waseem <waseema393@gmail.com>
2026-03-16 09:57:43 -06:00

68 lines
2.1 KiB
TypeScript

import * as Sentry from '@sentry/nextjs'
import { PG_META_URL } from 'lib/constants/index'
import { constructHeaders } from '../apiHelpers'
import { PgMetaDatabaseError, databaseErrorSchema, WrappedResult } from './types'
import { assertSelfHosted, encryptString, getConnectionString } from './util'
export type QueryOptions = {
query: string
parameters?: unknown[]
readOnly?: boolean
headers?: HeadersInit
}
/**
* Executes a SQL query against the self-hosted Postgres instance via pg-meta service.
*
* _Only call this from server-side self-hosted code._
*/
export async function executeQuery<T = unknown>({
query,
parameters,
readOnly = false,
headers,
}: QueryOptions): Promise<WrappedResult<T[]>> {
assertSelfHosted()
const connectionString = getConnectionString({ readOnly })
const connectionStringEncrypted = encryptString(connectionString)
const requestBody: { query: string; parameters?: unknown[] } = { query }
if (parameters !== undefined) {
requestBody.parameters = parameters
}
return await Sentry.startSpan({ name: 'pg-meta.query', op: 'db.query' }, async (span) => {
const response = await fetch(`${PG_META_URL}/query`, {
method: 'POST',
headers: constructHeaders({
...headers,
'Content-Type': 'application/json',
'x-connection-encrypted': connectionStringEncrypted,
}),
body: JSON.stringify(requestBody),
})
try {
const result = await response.json()
if (!response.ok) {
const { message, code, formattedError } = databaseErrorSchema.parse(result)
span.setAttribute('db.error', 1)
span.setAttribute('db.status_code', response.status)
const error = new PgMetaDatabaseError(message, code, response.status, formattedError)
return { data: undefined, error }
}
span.setAttribute('db.status_code', response.status)
return { data: result, error: undefined }
} catch (error) {
span.setAttribute('db.error', 1)
if (error instanceof Error) {
return { data: undefined, error }
}
throw error
}
})
}