import PostgrestQueryBuilder from './PostgrestQueryBuilder' import PostgrestFilterBuilder from './PostgrestFilterBuilder' import PostgrestBuilder from './PostgrestBuilder' import { DEFAULT_HEADERS } from './constants' import { Fetch, GenericSchema } from './types' /** * PostgREST client. * * @typeParam Database - Types for the schema from the [type * generator](https://supabase.com/docs/reference/javascript/next/typescript-support) * * @typeParam SchemaName - Postgres schema to switch to. Must be a string * literal, the same one passed to the constructor. If the schema is not * `"public"`, this must be supplied manually. */ export default class PostgrestClient< Database = any, SchemaName extends string & keyof Database = 'public' extends keyof Database ? 'public' : string & keyof Database, Schema extends GenericSchema = Database[SchemaName] extends GenericSchema ? Database[SchemaName] : any > { url: string headers: Record schemaName?: SchemaName fetch?: Fetch // TODO: Add back shouldThrowOnError once we figure out the typings /** * Creates a PostgREST client. * * @param url - URL of the PostgREST endpoint * @param options - Named parameters * @param options.headers - Custom headers * @param options.schema - Postgres schema to switch to * @param options.fetch - Custom fetch */ constructor( url: string, { headers = {}, schema, fetch, }: { headers?: Record schema?: SchemaName fetch?: Fetch } = {} ) { this.url = url this.headers = { ...DEFAULT_HEADERS, ...headers } this.schemaName = schema this.fetch = fetch } from< TableName extends string & keyof Schema['Tables'], Table extends Schema['Tables'][TableName] >(relation: TableName): PostgrestQueryBuilder from( relation: ViewName ): PostgrestQueryBuilder from(relation: string): PostgrestQueryBuilder /** * Perform a query on a table or a view. * * @param relation - The table or view name to query */ from(relation: string): PostgrestQueryBuilder { const url = new URL(`${this.url}/${relation}`) return new PostgrestQueryBuilder(url, { headers: { ...this.headers }, schema: this.schemaName, fetch: this.fetch, }) } /** * Select a schema to query or perform an function (rpc) call. * * The schema needs to be on the list of exposed schemas inside Supabase. * * @param schema - The schema to query */ schema( schema: DynamicSchema ): PostgrestClient< Database, DynamicSchema, Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any > { return new PostgrestClient(this.url, { headers: this.headers, schema, fetch: this.fetch, }) } /** * Perform a function call. * * @param fn - The function name to call * @param args - The arguments to pass to the function call * @param options - Named parameters * @param options.head - When set to `true`, `data` will not be returned. * Useful if you only need the count. * @param options.count - Count algorithm to use to count rows returned by the * function. Only applicable for [set-returning * functions](https://www.postgresql.org/docs/current/functions-srf.html). * * `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the * hood. * * `"planned"`: Approximated but fast count algorithm. Uses the Postgres * statistics under the hood. * * `"estimated"`: Uses exact count for low numbers and planned count for high * numbers. */ rpc< FunctionName extends string & keyof Schema['Functions'], Function_ extends Schema['Functions'][FunctionName] >( fn: FunctionName, args: Function_['Args'] = {}, { head = false, count, }: { head?: boolean count?: 'exact' | 'planned' | 'estimated' } = {} ): PostgrestFilterBuilder< Schema, Function_['Returns'] extends any[] ? Function_['Returns'][number] extends Record ? Function_['Returns'][number] : never : never, Function_['Returns'] > { let method: 'HEAD' | 'POST' const url = new URL(`${this.url}/rpc/${fn}`) let body: unknown | undefined if (head) { method = 'HEAD' Object.entries(args).forEach(([name, value]) => { url.searchParams.append(name, `${value}`) }) } else { method = 'POST' body = args } const headers = { ...this.headers } if (count) { headers['Prefer'] = `count=${count}` } return new PostgrestFilterBuilder({ method, url, headers, schema: this.schemaName, body, fetch: this.fetch, allowEmpty: false, } as unknown as PostgrestBuilder) } }