Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/supabase

src/request.ts

92 lines · 2.8 KB · TypeScript
import { projectAuth } from './setup.ts'
import { assertMutationAllowed, readMutationFlags } from './safety.ts'
import { projectRequest } from './client.ts'
import {
	isReadMethod,
	isRecord,
	normalizeHttpMethod,
	optionalBoolean,
	optionalString,
	requireString,
	type HttpMethod,
	type Json,
	type SupabaseAuthInput,
} from './types.ts'

export type ProjectRequestInput = SupabaseAuthInput & {
	path: string
	method?: HttpMethod
	query?: Record<string, string | number | boolean | undefined>
	headers?: Record<string, string>
	body?: Json
	dryRun?: boolean
	confirm?: boolean
}

/**
 * Call a project API path (Auth, PostgREST, Storage, or another /.../v1 route).
 * Prefer named helpers. Mutating methods require `dryRun: true` or `confirm: true`.
 * @example
 * import request from 'kody:@kody/supabase/request'
 * const result = await request({
 *   projectRef: 'abcdefghijklmnop',
 *   path: '/rest/v1/items',
 *   query: { select: 'id,name', limit: 5 },
 * })
 */
export async function request(input: ProjectRequestInput) {
	const path = requireString(input.path, 'path')
	if (!path.startsWith('/')) {
		throw new Error('path must start with "/".')
	}
	const method = normalizeHttpMethod(input.method, 'GET')
	if (!isReadMethod(method)) {
		assertMutationAllowed(readMutationFlags(input), `request ${method}`)
	}
	return await projectRequest({
		auth: projectAuth(input),
		method,
		path,
		query: input.query,
		headers: input.headers,
		body: input.body,
		dryRun: input.dryRun === true,
	})
}

/**
 * Call a project API path. Prefer named helpers.
 * Mutating methods require `dryRun: true` or `confirm: true`.
 * @example
 * import request from 'kody:@kody/supabase/request'
 * const result = await request({ projectRef: 'abcdefghijklmnop', path: '/rest/v1/items?select=id' })
 */
export default async function requestEntrypoint(
	params: Partial<ProjectRequestInput> & Record<string, unknown> = {},
) {
	return await request({
		account: optionalString(params.account, 'account'),
		secretName: optionalString(params.secretName, 'secretName'),
		serviceRoleSecretName: optionalString(
			params.serviceRoleSecretName,
			'serviceRoleSecretName',
		),
		projectRef: optionalString(params.projectRef, 'projectRef'),
		projectUrl: optionalString(params.projectUrl, 'projectUrl'),
		path: requireString(params.path, 'path'),
		method: normalizeHttpMethod(params.method, 'GET'),
		query: isRecord(params.query)
			? (params.query as Record<string, string | number | boolean | undefined>)
			: undefined,
		headers: isRecord(params.headers)
			? Object.fromEntries(
					Object.entries(params.headers).filter(
						(entry): entry is [string, string] => typeof entry[1] === 'string',
					),
				)
			: undefined,
		body: params.body as Json | undefined,
		dryRun: optionalBoolean(params.dryRun, 'dryRun'),
		confirm: optionalBoolean(params.confirm, 'confirm'),
	})
}