@supabase/postgrest-js
Version:
Isomorphic PostgREST client
1,887 lines (1,882 loc) • 129 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
//#region src/types/common/common.ts
/**
* Default number of retry attempts.
*/
const DEFAULT_MAX_RETRIES = 3;
/**
* Default exponential backoff delay function.
* Delays: 1s, 2s, 4s, 8s, ... (max 30s)
*
* @param attemptIndex - Zero-based index of the retry attempt
* @returns Delay in milliseconds before the next retry
*/
const getRetryDelay = (attemptIndex) => Math.min(1e3 * 2 ** attemptIndex, 3e4);
/**
* Status codes that are safe to retry.
* 520 = Cloudflare timeout/connection errors (transient)
* 503 = PostgREST schema cache not yet loaded (transient, signals retry via Retry-After header)
*/
const RETRYABLE_STATUS_CODES = [520, 503];
/**
* HTTP methods that are safe to retry (idempotent operations).
*/
const RETRYABLE_METHODS = [
"GET",
"HEAD",
"OPTIONS"
];
//#endregion
//#region src/PostgrestError.ts
/**
* Error format
*
* {@link https://postgrest.org/en/stable/api.html?highlight=options#errors-and-http-status-codes}
*/
var PostgrestError = class extends Error {
/**
* @example
* ```ts
* import PostgrestError from '@supabase/postgrest-js'
*
* throw new PostgrestError({
* message: 'Row level security prevented the request',
* details: 'RLS denied the insert',
* hint: 'Check your policies',
* code: 'PGRST301',
* })
* ```
*/
constructor(context) {
super(context.message);
this.name = "PostgrestError";
this.details = context.details;
this.hint = context.hint;
this.code = context.code;
}
toJSON() {
return {
name: this.name,
message: this.message,
details: this.details,
hint: this.hint,
code: this.code
};
}
};
//#endregion
//#region src/PostgrestBuilder.ts
/**
* Sleep for a given number of milliseconds.
* If an AbortSignal is provided, the sleep resolves early when the signal is aborted.
*/
function sleep(ms, signal) {
return new Promise((resolve) => {
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
resolve();
return;
}
const id = setTimeout(() => {
signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
resolve();
}, ms);
function onAbort() {
clearTimeout(id);
resolve();
}
signal === null || signal === void 0 || signal.addEventListener("abort", onAbort);
});
}
/**
* Check if a request should be retried based on method and status code.
*/
function shouldRetry(method, status, attemptCount, retryEnabled) {
if (!retryEnabled || attemptCount >= DEFAULT_MAX_RETRIES) return false;
if (!RETRYABLE_METHODS.includes(method)) return false;
if (!RETRYABLE_STATUS_CODES.includes(status)) return false;
return true;
}
var PostgrestBuilder = class {
/**
* Creates a builder configured for a specific PostgREST request.
*
* @example Using supabase-js (recommended)
* ```ts
* import { createClient } from '@supabase/supabase-js'
*
* const supabase = createClient('https://xyzcompany.supabase.co', 'your-publishable-key')
* const { data, error } = await supabase.from('users').select('*')
* ```
*
* @category Database
*
* @example Standalone import for bundle-sensitive environments
* ```ts
* import { PostgrestQueryBuilder } from '@supabase/postgrest-js'
*
* const builder = new PostgrestQueryBuilder(
* new URL('https://xyzcompany.supabase.co/rest/v1/users'),
* { headers: new Headers({ apikey: 'your-publishable-key' }) }
* )
* ```
*/
constructor(builder) {
var _builder$shouldThrowO, _builder$isMaybeSingl, _builder$shouldStripN, _builder$urlLengthLim, _builder$retry;
this.shouldThrowOnError = false;
this.retryEnabled = true;
this.method = builder.method;
this.url = builder.url;
this.headers = new Headers(builder.headers);
this.schema = builder.schema;
this.body = builder.body;
this.shouldThrowOnError = (_builder$shouldThrowO = builder.shouldThrowOnError) !== null && _builder$shouldThrowO !== void 0 ? _builder$shouldThrowO : false;
this.signal = builder.signal;
this.isMaybeSingle = (_builder$isMaybeSingl = builder.isMaybeSingle) !== null && _builder$isMaybeSingl !== void 0 ? _builder$isMaybeSingl : false;
this.shouldStripNulls = (_builder$shouldStripN = builder.shouldStripNulls) !== null && _builder$shouldStripN !== void 0 ? _builder$shouldStripN : false;
this.urlLengthLimit = (_builder$urlLengthLim = builder.urlLengthLimit) !== null && _builder$urlLengthLim !== void 0 ? _builder$urlLengthLim : 8e3;
this.retryEnabled = (_builder$retry = builder.retry) !== null && _builder$retry !== void 0 ? _builder$retry : true;
if (builder.fetch) this.fetch = builder.fetch;
else this.fetch = fetch;
}
/**
* If there's an error with the query, throwOnError will reject the promise by
* throwing the error instead of returning it as part of a successful response.
*
* {@link https://github.com/supabase/supabase-js/issues/92}
*
* @category Database
*/
throwOnError() {
this.shouldThrowOnError = true;
return this;
}
/**
* Strip null values from the response data. Properties with `null` values
* will be omitted from the returned JSON objects.
*
* Requires PostgREST 11.2.0+.
*
* {@link https://docs.postgrest.org/en/stable/references/api/resource_representation.html#stripped-nulls}
*
* @category Database
* @subcategory Using modifiers
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .stripNulls()
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text, bio text);
*
* insert into
* characters (id, name, bio)
* values
* (1, 'Luke', null),
* (2, 'Leia', 'Princess of Alderaan');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 1,
* "name": "Luke"
* },
* {
* "id": 2,
* "name": "Leia",
* "bio": "Princess of Alderaan"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
stripNulls() {
if (this.headers.get("Accept") === "text/csv") throw new Error("stripNulls() cannot be used with csv()");
this.shouldStripNulls = true;
return this;
}
/**
* Set an HTTP header for the request.
*
* @category Database
*/
setHeader(name, value) {
this.headers = new Headers(this.headers);
this.headers.set(name, value);
return this;
}
/**
* @category Database
*
* Configure retry behavior for this request.
*
* By default, retries are enabled for idempotent requests (GET, HEAD, OPTIONS)
* that fail with network errors or specific HTTP status codes (503, 520).
* Retries use exponential backoff (1s, 2s, 4s) with a maximum of 3 attempts.
*
* @param enabled - Whether to enable retries for this request
*
* @example
* ```ts
* // Disable retries for a specific query
* const { data, error } = await supabase
* .from('users')
* .select()
* .retry(false)
* ```
*/
retry(enabled) {
this.retryEnabled = enabled;
return this;
}
then(onfulfilled, onrejected) {
var _this = this;
if (this.schema === void 0) {} else if (["GET", "HEAD"].includes(this.method)) this.headers.set("Accept-Profile", this.schema);
else this.headers.set("Content-Profile", this.schema);
if (this.method !== "GET" && this.method !== "HEAD") this.headers.set("Content-Type", "application/json");
if (this.shouldStripNulls) {
const currentAccept = this.headers.get("Accept");
if (currentAccept === "application/vnd.pgrst.object+json") this.headers.set("Accept", "application/vnd.pgrst.object+json;nulls=stripped");
else if (!currentAccept || currentAccept === "application/json") this.headers.set("Accept", "application/vnd.pgrst.array+json;nulls=stripped");
}
const _fetch = this.fetch;
const executeWithRetry = async () => {
let attemptCount = 0;
while (true) {
const requestHeaders = new Headers(_this.headers);
if (attemptCount > 0) requestHeaders.set("X-Retry-Count", String(attemptCount));
let res$1;
try {
res$1 = await _fetch(_this.url.toString(), {
method: _this.method,
headers: requestHeaders,
body: JSON.stringify(_this.body, (_, value) => typeof value === "bigint" ? value.toString() : value),
signal: _this.signal
});
} catch (fetchError) {
if ((fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) === "AbortError" || (fetchError === null || fetchError === void 0 ? void 0 : fetchError.code) === "ABORT_ERR") throw fetchError;
if (!RETRYABLE_METHODS.includes(_this.method)) throw fetchError;
if (_this.retryEnabled && attemptCount < DEFAULT_MAX_RETRIES) {
const delay = getRetryDelay(attemptCount);
attemptCount++;
await sleep(delay, _this.signal);
continue;
}
throw fetchError;
}
if (shouldRetry(_this.method, res$1.status, attemptCount, _this.retryEnabled)) {
var _res$headers$get, _res$headers;
const retryAfterHeader = (_res$headers$get = (_res$headers = res$1.headers) === null || _res$headers === void 0 ? void 0 : _res$headers.get("Retry-After")) !== null && _res$headers$get !== void 0 ? _res$headers$get : null;
const delay = retryAfterHeader !== null ? Math.max(0, parseInt(retryAfterHeader, 10) || 0) * 1e3 : getRetryDelay(attemptCount);
await res$1.text();
attemptCount++;
await sleep(delay, _this.signal);
continue;
}
return await _this.processResponse(res$1);
}
};
let res = executeWithRetry();
if (!this.shouldThrowOnError) res = res.catch((fetchError) => {
var _fetchError$name2;
let errorDetails = "";
let hint = "";
let code = "";
const cause = fetchError === null || fetchError === void 0 ? void 0 : fetchError.cause;
if (cause) {
var _cause$message, _cause$code, _fetchError$name, _cause$name;
const causeMessage = (_cause$message = cause === null || cause === void 0 ? void 0 : cause.message) !== null && _cause$message !== void 0 ? _cause$message : "";
const causeCode = (_cause$code = cause === null || cause === void 0 ? void 0 : cause.code) !== null && _cause$code !== void 0 ? _cause$code : "";
errorDetails = `${(_fetchError$name = fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) !== null && _fetchError$name !== void 0 ? _fetchError$name : "FetchError"}: ${fetchError === null || fetchError === void 0 ? void 0 : fetchError.message}`;
errorDetails += `\n\nCaused by: ${(_cause$name = cause === null || cause === void 0 ? void 0 : cause.name) !== null && _cause$name !== void 0 ? _cause$name : "Error"}: ${causeMessage}`;
if (causeCode) errorDetails += ` (${causeCode})`;
if (cause === null || cause === void 0 ? void 0 : cause.stack) errorDetails += `\n${cause.stack}`;
} else {
var _fetchError$stack;
errorDetails = (_fetchError$stack = fetchError === null || fetchError === void 0 ? void 0 : fetchError.stack) !== null && _fetchError$stack !== void 0 ? _fetchError$stack : "";
}
const urlLength = this.url.toString().length;
if ((fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) === "AbortError" || (fetchError === null || fetchError === void 0 ? void 0 : fetchError.code) === "ABORT_ERR") {
code = "";
hint = "Request was aborted (timeout or manual cancellation)";
if (urlLength > this.urlLengthLimit) hint += `. Note: Your request URL is ${urlLength} characters, which may exceed server limits. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [many IDs])), consider using an RPC function to pass values server-side.`;
} else if ((cause === null || cause === void 0 ? void 0 : cause.name) === "HeadersOverflowError" || (cause === null || cause === void 0 ? void 0 : cause.code) === "UND_ERR_HEADERS_OVERFLOW") {
code = "";
hint = "HTTP headers exceeded server limits (typically 16KB)";
if (urlLength > this.urlLengthLimit) hint += `. Your request URL is ${urlLength} characters. If selecting many fields, consider using views. If filtering with large arrays (e.g., .in('id', [200+ IDs])), consider using an RPC function instead.`;
}
return {
success: false,
error: {
message: `${(_fetchError$name2 = fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) !== null && _fetchError$name2 !== void 0 ? _fetchError$name2 : "FetchError"}: ${fetchError === null || fetchError === void 0 ? void 0 : fetchError.message}`,
details: errorDetails,
hint,
code
},
data: null,
count: null,
status: 0,
statusText: ""
};
});
return res.then(onfulfilled, onrejected);
}
/**
* Process a fetch response and return the standardized postgrest response.
*/
async processResponse(res) {
var _this2 = this;
let error = null;
let data = null;
let count = null;
let status = res.status;
let statusText = res.statusText;
if (res.ok) {
var _this$headers$get2, _res$headers$get2;
if (_this2.method !== "HEAD") {
var _this$headers$get;
const body = await res.text();
if (body === "") {} else if (_this2.headers.get("Accept") === "text/csv") data = body;
else if (_this2.headers.get("Accept") && ((_this$headers$get = _this2.headers.get("Accept")) === null || _this$headers$get === void 0 ? void 0 : _this$headers$get.includes("application/vnd.pgrst.plan+text"))) data = body;
else data = JSON.parse(body);
}
const countHeader = (_this$headers$get2 = _this2.headers.get("Prefer")) === null || _this$headers$get2 === void 0 ? void 0 : _this$headers$get2.match(/count=(exact|planned|estimated)/);
const contentRange = (_res$headers$get2 = res.headers.get("content-range")) === null || _res$headers$get2 === void 0 ? void 0 : _res$headers$get2.split("/");
if (countHeader && contentRange && contentRange.length > 1) count = parseInt(contentRange[1]);
if (_this2.isMaybeSingle && Array.isArray(data)) if (data.length > 1) {
error = {
code: "PGRST116",
details: `Results contain ${data.length} rows, application/vnd.pgrst.object+json requires 1 row`,
hint: null,
message: "JSON object requested, multiple (or no) rows returned"
};
data = null;
count = null;
status = 406;
statusText = "Not Acceptable";
} else if (data.length === 1) data = data[0];
else data = null;
} else {
const body = await res.text();
try {
error = JSON.parse(body);
if (Array.isArray(error) && res.status === 404) {
data = [];
error = null;
status = 200;
statusText = "OK";
}
} catch (_unused) {
if (res.status === 404 && body === "") {
status = 204;
statusText = "No Content";
} else error = { message: body };
}
if (error && _this2.shouldThrowOnError) throw new PostgrestError(error);
}
return {
success: error === null,
error,
data,
count,
status,
statusText
};
}
/**
* Override the type of the returned `data`.
*
* @typeParam NewResult - The new result type to override with
* @deprecated Use overrideTypes<yourType, { merge: false }>() method at the end of your call chain instead
*
* @category Database
*/
returns() {
/* istanbul ignore next */
return this;
}
/**
* Override the type of the returned `data` field in the response.
*
* @typeParam NewResult - The new type to cast the response data to
* @typeParam Options - Optional type configuration (defaults to { merge: true })
* @typeParam Options.merge - When true, merges the new type with existing return type. When false, replaces the existing types entirely (defaults to true)
* @example
* ```typescript
* // Merge with existing types (default behavior)
* const query = supabase
* .from('users')
* .select()
* .overrideTypes<{ custom_field: string }>()
*
* // Replace existing types completely
* const replaceQuery = supabase
* .from('users')
* .select()
* .overrideTypes<{ id: number; name: string }, { merge: false }>()
* ```
* @returns A PostgrestBuilder instance with the new type
*
* @category Database
* @subcategory Using modifiers
*
* @example Complete Override type of successful response
* ```ts
* const { data } = await supabase
* .from('countries')
* .select()
* .overrideTypes<Array<MyType>, { merge: false }>()
* ```
*
* @exampleResponse Complete Override type of successful response
* ```ts
* let x: typeof data // MyType[]
* ```
*
* @example Complete Override type of object response
* ```ts
* const { data } = await supabase
* .from('countries')
* .select()
* .maybeSingle()
* .overrideTypes<MyType, { merge: false }>()
* ```
*
* @exampleResponse Complete Override type of object response
* ```ts
* let x: typeof data // MyType | null
* ```
*
* @example Partial Override type of successful response
* ```ts
* const { data } = await supabase
* .from('countries')
* .select()
* .overrideTypes<Array<{ status: "A" | "B" }>>()
* ```
*
* @exampleResponse Partial Override type of successful response
* ```ts
* let x: typeof data // Array<CountryRowProperties & { status: "A" | "B" }>
* ```
*
* @example Partial Override type of object response
* ```ts
* const { data } = await supabase
* .from('countries')
* .select()
* .maybeSingle()
* .overrideTypes<{ status: "A" | "B" }>()
* ```
*
* @exampleResponse Partial Override type of object response
* ```ts
* let x: typeof data // CountryRowProperties & { status: "A" | "B" } | null
* ```
*
* @example Merge vs replace existing types
* ```typescript
* // Merge with existing types (default behavior)
* const query = supabase
* .from('users')
* .select()
* .overrideTypes<{ custom_field: string }>()
*
* // Replace existing types completely
* const replaceQuery = supabase
* .from('users')
* .select()
* .overrideTypes<{ id: number; name: string }, { merge: false }>()
* ```
*/
overrideTypes() {
return this;
}
};
//#endregion
//#region src/PostgrestTransformBuilder.ts
var PostgrestTransformBuilder = class extends PostgrestBuilder {
/**
* Perform a SELECT on the query result.
*
* By default, `.insert()`, `.update()`, `.upsert()`, and `.delete()` do not
* return modified rows. By calling this method, modified rows are returned in
* `data`.
*
* @param columns - The columns to retrieve, separated by commas
*
* @category Database
* @subcategory Using modifiers
*
* @example With `upsert()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .upsert({ id: 1, name: 'Han Solo' })
* .select()
* ```
*
* @exampleSql With `upsert()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Han');
* ```
*
* @exampleResponse With `upsert()`
* ```json
* {
* "data": [
* {
* "id": 1,
* "name": "Han Solo"
* }
* ],
* "status": 201,
* "statusText": "Created"
* }
* ```
*/
select(columns) {
let quoted = false;
const cleanedColumns = (columns !== null && columns !== void 0 ? columns : "*").split("").map((c) => {
if (/\s/.test(c) && !quoted) return "";
if (c === "\"") quoted = !quoted;
return c;
}).join("");
this.url.searchParams.set("select", cleanedColumns);
this.headers.append("Prefer", "return=representation");
return this;
}
/**
* Order the query result by `column`.
*
* You can call this method multiple times to order by multiple columns.
*
* You can order referenced tables, but it only affects the ordering of the
* parent table if you use `!inner` in the query.
*
* @param column - The column to order by
* @param options - Named parameters
* @param options.ascending - If `true`, the result will be in ascending order
* @param options.nullsFirst - If `true`, `null`s appear first. If `false`,
* `null`s appear last.
* @param options.referencedTable - Set this to order a referenced table by
* its columns
* @param options.foreignTable - Deprecated, use `options.referencedTable`
* instead
*
* @category Database
* @subcategory Using modifiers
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select('id, name')
* .order('id', { ascending: false })
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 3,
* "name": "Han"
* },
* {
* "id": 2,
* "name": "Leia"
* },
* {
* "id": 1,
* "name": "Luke"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*
* @exampleDescription On a referenced table
* Ordering with `referencedTable` doesn't affect the ordering of the
* parent table.
*
* @example On a referenced table
* ```ts
* const { data, error } = await supabase
* .from('orchestral_sections')
* .select(`
* name,
* instruments (
* name
* )
* `)
* .order('name', { referencedTable: 'instruments', ascending: false })
*
* ```
*
* @exampleSql On a referenced table
* ```sql
* create table
* orchestral_sections (id int8 primary key, name text);
* create table
* instruments (
* id int8 primary key,
* section_id int8 not null references orchestral_sections,
* name text
* );
*
* insert into
* orchestral_sections (id, name)
* values
* (1, 'strings'),
* (2, 'woodwinds');
* insert into
* instruments (id, section_id, name)
* values
* (1, 1, 'harp'),
* (2, 1, 'violin');
* ```
*
* @exampleResponse On a referenced table
* ```json
* {
* "data": [
* {
* "name": "strings",
* "instruments": [
* {
* "name": "violin"
* },
* {
* "name": "harp"
* }
* ]
* },
* {
* "name": "woodwinds",
* "instruments": []
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*
* @exampleDescription Order parent table by a referenced table
* Ordering with `referenced_table(col)` affects the ordering of the
* parent table.
*
* @example Order parent table by a referenced table
* ```ts
* const { data, error } = await supabase
* .from('instruments')
* .select(`
* name,
* section:orchestral_sections (
* name
* )
* `)
* .order('section(name)', { ascending: true })
*
* ```
*
* @exampleSql Order parent table by a referenced table
* ```sql
* create table
* orchestral_sections (id int8 primary key, name text);
* create table
* instruments (
* id int8 primary key,
* section_id int8 not null references orchestral_sections,
* name text
* );
*
* insert into
* orchestral_sections (id, name)
* values
* (1, 'strings'),
* (2, 'woodwinds');
* insert into
* instruments (id, section_id, name)
* values
* (1, 2, 'flute'),
* (2, 1, 'violin');
* ```
*
* @exampleResponse Order parent table by a referenced table
* ```json
* {
* "data": [
* {
* "name": "violin",
* "orchestral_sections": {"name": "strings"}
* },
* {
* "name": "flute",
* "orchestral_sections": {"name": "woodwinds"}
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
order(column, { ascending = true, nullsFirst, foreignTable, referencedTable = foreignTable } = {}) {
const key = referencedTable ? `${referencedTable}.order` : "order";
const existingOrder = this.url.searchParams.get(key);
this.url.searchParams.set(key, `${existingOrder ? `${existingOrder},` : ""}${column}.${ascending ? "asc" : "desc"}${nullsFirst === void 0 ? "" : nullsFirst ? ".nullsfirst" : ".nullslast"}`);
return this;
}
/**
* Limit the query result by `count`.
*
* @param count - The maximum number of rows to return
* @param options - Named parameters
* @param options.referencedTable - Set this to limit rows of referenced
* tables instead of the parent table
* @param options.foreignTable - Deprecated, use `options.referencedTable`
* instead
*
* @category Database
* @subcategory Using modifiers
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select('name')
* .limit(1)
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "name": "Luke"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*
* @example On a referenced table
* ```ts
* const { data, error } = await supabase
* .from('orchestral_sections')
* .select(`
* name,
* instruments (
* name
* )
* `)
* .limit(1, { referencedTable: 'instruments' })
* ```
*
* @exampleSql On a referenced table
* ```sql
* create table
* orchestral_sections (id int8 primary key, name text);
* create table
* instruments (
* id int8 primary key,
* section_id int8 not null references orchestral_sections,
* name text
* );
*
* insert into
* orchestral_sections (id, name)
* values
* (1, 'strings');
* insert into
* instruments (id, section_id, name)
* values
* (1, 1, 'harp'),
* (2, 1, 'violin');
* ```
*
* @exampleResponse On a referenced table
* ```json
* {
* "data": [
* {
* "name": "strings",
* "instruments": [
* {
* "name": "violin"
* }
* ]
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
limit(count, { foreignTable, referencedTable = foreignTable } = {}) {
const key = typeof referencedTable === "undefined" ? "limit" : `${referencedTable}.limit`;
this.url.searchParams.set(key, `${count}`);
return this;
}
/**
* Limit the query result by starting at an offset `from` and ending at the offset `to`.
* Only records within this range are returned.
* This respects the query order and if there is no order clause the range could behave unexpectedly.
* The `from` and `to` values are 0-based and inclusive: `range(1, 3)` will include the second, third
* and fourth rows of the query.
*
* @param from - The starting index from which to limit the result
* @param to - The last index to which to limit the result
* @param options - Named parameters
* @param options.referencedTable - Set this to limit rows of referenced
* tables instead of the parent table
* @param options.foreignTable - Deprecated, use `options.referencedTable`
* instead
*
* @category Database
* @subcategory Using modifiers
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select('name')
* .range(0, 1)
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "name": "Luke"
* },
* {
* "name": "Leia"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
range(from, to, { foreignTable, referencedTable = foreignTable } = {}) {
const keyOffset = typeof referencedTable === "undefined" ? "offset" : `${referencedTable}.offset`;
const keyLimit = typeof referencedTable === "undefined" ? "limit" : `${referencedTable}.limit`;
this.url.searchParams.set(keyOffset, `${from}`);
this.url.searchParams.set(keyLimit, `${to - from + 1}`);
return this;
}
/**
* Set the AbortSignal for the fetch request.
*
* @param signal - The AbortSignal to use for the fetch request
*
* @category Database
* @subcategory Using modifiers
*
* @remarks
* You can use this to set a timeout for the request.
*
* @exampleDescription Aborting requests in-flight
* You can use an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) to abort requests.
* Note that `status` and `statusText` don't mean anything for aborted requests as the request wasn't fulfilled.
*
* @example Aborting requests in-flight
* ```ts
* const ac = new AbortController()
*
* const { data, error } = await supabase
* .from('very_big_table')
* .select()
* .abortSignal(ac.signal)
*
* // Abort the request after 100 ms
* setTimeout(() => ac.abort(), 100)
* ```
*
* @exampleResponse Aborting requests in-flight
* ```json
* {
* "error": {
* "message": "AbortError: The user aborted a request.",
* "details": "",
* "hint": "The request was aborted locally via the provided AbortSignal.",
* "code": ""
* },
* "status": 0,
* "statusText": ""
* }
*
* ```
*
* @example Set a timeout
* ```ts
* const { data, error } = await supabase
* .from('very_big_table')
* .select()
* .abortSignal(AbortSignal.timeout(1000 /* ms *\/))
* ```
*
* @exampleResponse Set a timeout
* ```json
* {
* "error": {
* "message": "FetchError: The user aborted a request.",
* "details": "",
* "hint": "",
* "code": ""
* },
* "status": 400,
* "statusText": "Bad Request"
* }
*
* ```
*/
abortSignal(signal) {
this.signal = signal;
return this;
}
/**
* Return `data` as a single object instead of an array of objects.
*
* Query result must be one row (e.g. using `.limit(1)`), otherwise this
* returns an error.
*
* @category Database
* @subcategory Using modifiers
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select('name')
* .limit(1)
* .single()
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": {
* "name": "Luke"
* },
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
single() {
this.headers.set("Accept", "application/vnd.pgrst.object+json");
return this;
}
/**
* Return `data` as a single object instead of an array of objects.
*
* Query result must be zero or one row (e.g. using `.limit(1)`), otherwise
* this returns an error.
*
* @category Database
* @subcategory Using modifiers
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .eq('name', 'Katniss')
* .maybeSingle()
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
maybeSingle() {
this.isMaybeSingle = true;
return this;
}
/**
* Return `data` as a string in CSV format.
*
* @category Database
* @subcategory Using modifiers
*
* @exampleDescription Return data as CSV
* By default, the data is returned in JSON format, but can also be returned as Comma Separated Values.
*
* @example Return data as CSV
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .csv()
* ```
*
* @exampleSql Return data as CSV
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse Return data as CSV
* ```json
* {
* "data": "id,name\n1,Luke\n2,Leia\n3,Han",
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
csv() {
this.headers.set("Accept", "text/csv");
return this;
}
/**
* Return `data` as an object in [GeoJSON](https://geojson.org) format.
*
* @category Database
*/
geojson() {
this.headers.set("Accept", "application/geo+json");
return this;
}
/**
* Return `data` as the EXPLAIN plan for the query.
*
* You need to enable the
* [db_plan_enabled](https://supabase.com/docs/guides/database/debugging-performance#enabling-explain)
* setting before using this method.
*
* @param options - Named parameters
*
* @param options.analyze - If `true`, the query will be executed and the
* actual run time will be returned
*
* @param options.verbose - If `true`, the query identifier will be returned
* and `data` will include the output columns of the query
*
* @param options.settings - If `true`, include information on configuration
* parameters that affect query planning
*
* @param options.buffers - If `true`, include information on buffer usage
*
* @param options.wal - If `true`, include information on WAL record generation
*
* @param options.format - The format of the output, can be `"text"` (default)
* or `"json"`
*
* @category Database
* @subcategory Using modifiers
*
* @exampleDescription Get the execution plan
* By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter.
*
* @example Get the execution plan
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .explain()
* ```
*
* @exampleSql Get the execution plan
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse Get the execution plan
* ```js
* Aggregate (cost=33.34..33.36 rows=1 width=112)
* -> Limit (cost=0.00..18.33 rows=1000 width=40)
* -> Seq Scan on characters (cost=0.00..22.00 rows=1200 width=40)
* ```
*
* @exampleDescription Get the execution plan with analyze and verbose
* By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter.
*
* @example Get the execution plan with analyze and verbose
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .explain({analyze:true,verbose:true})
* ```
*
* @exampleSql Get the execution plan with analyze and verbose
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse Get the execution plan with analyze and verbose
* ```js
* Aggregate (cost=33.34..33.36 rows=1 width=112) (actual time=0.041..0.041 rows=1 loops=1)
* Output: NULL::bigint, count(ROW(characters.id, characters.name)), COALESCE(json_agg(ROW(characters.id, characters.name)), '[]'::json), NULLIF(current_setting('response.headers'::text, true), ''::text), NULLIF(current_setting('response.status'::text, true), ''::text)
* -> Limit (cost=0.00..18.33 rows=1000 width=40) (actual time=0.005..0.006 rows=3 loops=1)
* Output: characters.id, characters.name
* -> Seq Scan on public.characters (cost=0.00..22.00 rows=1200 width=40) (actual time=0.004..0.005 rows=3 loops=1)
* Output: characters.id, characters.name
* Query Identifier: -4730654291623321173
* Planning Time: 0.407 ms
* Execution Time: 0.119 ms
* ```
*/
explain({ analyze = false, verbose = false, settings = false, buffers = false, wal = false, format = "text" } = {}) {
var _this$headers$get;
const options = [
analyze ? "analyze" : null,
verbose ? "verbose" : null,
settings ? "settings" : null,
buffers ? "buffers" : null,
wal ? "wal" : null
].filter(Boolean).join("|");
const forMediatype = (_this$headers$get = this.headers.get("Accept")) !== null && _this$headers$get !== void 0 ? _this$headers$get : "application/json";
this.headers.set("Accept", `application/vnd.pgrst.plan+${format}; for="${forMediatype}"; options=${options};`);
if (format === "json") return this;
else return this;
}
/**
* Rollback the query.
*
* `data` will still be returned, but the query is not committed.
*
* @category Database
*/
rollback() {
this.headers.append("Prefer", "tx=rollback");
return this;
}
/**
* Override the type of the returned `data`.
*
* @typeParam NewResult - The new result type to override with
* @deprecated Use overrideTypes<yourType, { merge: false }>() method at the end of your call chain instead
*
* @category Database
* @subcategory Using modifiers
*
* @remarks
* - Deprecated: use overrideTypes method instead
*
* @example Override type of successful response
* ```ts
* const { data } = await supabase
* .from('countries')
* .select()
* .returns<Array<MyType>>()
* ```
*
* @exampleResponse Override type of successful response
* ```js
* let x: typeof data // MyType[]
* ```
*
* @example Override type of object response
* ```ts
* const { data } = await supabase
* .from('countries')
* .select()
* .maybeSingle()
* .returns<MyType>()
* ```
*
* @exampleResponse Override type of object response
* ```js
* let x: typeof data // MyType | null
* ```
*/
returns() {
return this;
}
/**
* Set the maximum number of rows that can be affected by the query.
* Only available in PostgREST v13+ and only works with PATCH and DELETE methods.
*
* @param value - The maximum number of rows that can be affected
*
* @category Database
*/
maxAffected(value) {
this.headers.append("Prefer", "handling=strict");
this.headers.append("Prefer", `max-affected=${value}`);
return this;
}
};
//#endregion
//#region src/PostgrestFilterBuilder.ts
const PostgrestReservedCharsRegexp = /* @__PURE__ */ new RegExp("[,()]");
var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
/**
* Match only rows where `column` is equal to `value`.
*
* To check if the value of `column` is NULL, you should use `.is()` instead.
*
* @param column - The column to filter on
* @param value - The value to filter with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .eq('name', 'Leia')
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 2,
* "name": "Leia"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
eq(column, value) {
this.url.searchParams.append(column, `eq.${value}`);
return this;
}
/**
* Match only rows where `column` is not equal to `value`.
*
* This filter does not include rows where `column` is `NULL`. To match null
* values, use `.is(column, null)` instead.
*
* @param column - The column to filter on
* @param value - The value to filter with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .neq('name', 'Leia')
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 1,
* "name": "Luke"
* },
* {
* "id": 3,
* "name": "Han"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
neq(column, value) {
this.url.searchParams.append(column, `neq.${value}`);
return this;
}
/**
* Match only rows where `column` is greater than `value`.
*
* @param column - The column to filter on
* @param value - The value to filter with
*
* @category Database
* @subcategory Using filters
*
* @exampleDescription With `select()`
* When using [reserved words](https://www.postgresql.org/docs/current/sql-keywords-appendix.html) for column names you need
* to add double quotes e.g. `.gt('"order"', 2)`
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .gt('id', 2)
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 3,
* "name": "Han"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
gt(column, value) {
this.url.searchParams.append(column, `gt.${value}`);
return this;
}
/**
* Match only rows where `column` is greater than or equal to `value`.
*
* @param column - The column to filter on
* @param value - The value to filter with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .gte('id', 2)
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 2,
* "name": "Leia"
* },
* {
* "id": 3,
* "name": "Han"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
gte(column, value) {
this.url.searchParams.append(column, `gte.${value}`);
return this;
}
/**
* Match only rows where `column` is less than `value`.
*
* @param column - The column to filter on
* @param value - The value to filter with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .lt('id', 2)
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 1,
* "name": "Luke"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
lt(column, value) {
this.url.searchParams.append(column, `lt.${value}`);
return this;
}
/**
* Match only rows where `column` is less than or equal to `value`.
*
* @param column - The column to filter on
* @param value - The value to filter with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .lte('id', 2)
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 1,
* "name": "Luke"
* },
* {
* "id": 2,
* "name": "Leia"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
lte(column, value) {
this.url.searchParams.append(column, `lte.${value}`);
return this;
}
/**
* Match only rows where `column` matches `pattern` case-sensitively.
*
* @param column - The column to filter on
* @param pattern - The pattern to match with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .like('name', '%Lu%')
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 1,
* "name": "Luke"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
like(column, pattern) {
this.url.searchParams.append(column, `like.${pattern}`);
return this;
}
/**
* Match only rows where `column` matches all of `patterns` case-sensitively.
*
* @param column - The column to filter on
* @param patterns - The patterns to match with
*
* @category Database
* @subcategory Using filters
*/
likeAllOf(column, patterns) {
this.url.searchParams.append(column, `like(all).{${patterns.join(",")}}`);
return this;
}
/**
* Match only rows where `column` matches any of `patterns` case-sensitively.
*
* @param column - The column to filter on
* @param patterns - The patterns to match with
*
* @category Database
* @subcategory Using filters
*/
likeAnyOf(column, patterns) {
this.url.searchParams.append(column, `like(any).{${patterns.join(",")}}`);
return this;
}
/**
* Match only rows where `column` matches `pattern` case-insensitively.
*
* @param column - The column to filter on
* @param pattern - The pattern to match with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
* .from('characters')
* .select()
* .ilike('name', '%lu%')
* ```
*
* @exampleSql With `select()`
* ```sql
* create table
* characters (id int8 primary key, name text);
*
* insert into
* characters (id, name)
* values
* (1, 'Luke'),
* (2, 'Leia'),
* (3, 'Han');
* ```
*
* @exampleResponse With `select()`
* ```json
* {
* "data": [
* {
* "id": 1,
* "name": "Luke"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
ilike(column, pattern) {
this.url.searchParams.append(column, `ilike.${pattern}`);
return this;
}
/**
* Match only rows where `column` matches all of `patterns` case-insensitively.
*
* @param column - The column to filter on
* @param patterns - The patterns to match with
*
* @category Database
* @subcategory Using filters
*/
ilikeAllOf(column, patterns) {
this.url.searchParams.append(column, `ilike(all).{${patterns.join(",")}}`);
return this;
}
/**
* Match only rows where `column` matches any of `patterns` case-insensitively.
*
* @param column - The column to filter on
* @param patterns - The patterns to match with
*
* @category Database
* @subcategory Using filters
*/
ilikeAnyOf(column, patterns) {
this.url.searchParams.append(column, `ilike(any).{${patterns.join(",")}}`);
return this;
}
/**
* Match only rows where `column` matches the PostgreSQL regex `pattern`
* case-sensitively (using the `~` operator).
*
* @param column - The column to filter on
* @param pattern - The PostgreSQL regular expression pattern to match with
*/
regexMatch(column, pattern) {
this.url.searchParams.append(column, `match.${pattern}`);
return this;
}
/**
* Match only rows where `column` matches the PostgreSQL regex `pattern`
* case-insensitively (using the `~*` operator).
*
* @param column - The column to filter on
* @param pattern - The PostgreSQL regular expression pattern to match with
*/
regexIMatch(column, pattern) {
this.url.searchParams.append(column, `imatch.${pattern}`);
return this;
}
/**
* Match only rows where `column` IS `value`.
*
* For non-boolean columns, this is only relevant for checking if the value of
* `column` is NULL by setting `value` to `null`.
*
* For boolean columns, you can also set `value` to `true` or `false` and it
* will behave the same way as `.eq()`.
*
* @param column - The column to filter on
* @param value - The value to filter with
*
* @category Database
* @subcategory Using filters
*
* @exampleDescription Checking for nullness, true or false
* Using the `eq()` filter doesn't work when filtering for `null`.
*
* Instead, you need to use `is()`.
*
* @example Checking for nullness, true or false
* ```ts
* const { data, error } = await supabase
* .from('countries')
* .select()
* .is('name', null)
* ```
*
* @exampleSql Checking for nullness, true or false
* ```sql
* create table
* countries (id int8 primary key, name text);
*
* insert into
* countries (id, name)
* values
* (1, 'null'),
* (2, null);
* ```
*
* @exampleResponse Checking for nullness, true or false
* ```json
* {
* "data": [
* {
* "id": 2,
* "name": "null"
* }
* ],
* "status": 200,
* "statusText": "OK"
* }
* ```
*/
is(column, value) {
this.url.searchParams.append(column, `is.${value}`);
return this;
}
/**
* Match only rows where `column` IS DISTINCT FROM `value`.
*
* Unlike `.neq()`, this treats `NULL` as a comparable value. Two `NULL` values
* are considered equal (not distinct), and comparing `NULL` with any non-NULL
* value returns true (distinct).
*
* @param column - The column to filter on
* @param value - The value to filter with
*/
isDistinct(column, value) {
this.url.searchParams.append(column, `isdistinct.${value}`);
return this;
}
/**
* Match only rows where `column` is included in the `values` array.
*
* @param column - The column to filter on
* @param values - The values array to filter with
*
* @category Database
* @subcategory Using filters
*
* @example With `select()`
* ```ts
* const { data, error } = await supabase
*