Supabase-compatible API

2026-10-22

Supabase-compatible API

Database exposes its row API through Supabase client paths. Use a base URL ending in /supabase/<database_id>/, including the trailing slash, and a Praxis credential. Unlike Supabase, storage uses SQLite; define tables and fields through /database/v1, not SQL migrations, and do not expect Supabase Auth, RLS policies or realtime subscriptions. Auth, storage, realtime, functions and graphql service paths return 501 with x-should-retry=false. See the Database domain for supported concepts and differences.

Use the official supabase-js client to read and write tables in one database.

Guide →

Base URL

https://api.deepraxis.ai/supabase/{database_id}/

PartMeaning
{database_id}ID returned by database creation; each database has its own URL.
/Keep the trailing slash so supabase-js preserves the database ID when adding rest/v1.

The API Key is the Authorization: Bearer credential; pass that same key as the second supabase-js argument. These examples run on the server.

import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
  `https://api.deepraxis.ai/supabase/${process.env.DATABASE_ID}/`,
  process.env.PRAXIS_API_KEY,
  { auth: { persistSession: false, autoRefreshToken: false } },
);

Table endpoint

GET / HEAD / POST / PATCH / DELETE

/supabase/{database_id}/rest/v1/{table}

Only the PostgREST subset below is available. Unsupported syntax returns HTTP 400 with code PGRST127; invalid or unknown parameters can return PGRST100.

Filter operators

ValueSupport
eqsupported
neqsupported
gtsupported
gtesupported
ltsupported
ltesupported
likesupported
ilikesupported
insupported
issupported
cssupported
cdsupported
ovsupported
ftsunsupported
plftsunsupported
phftsunsupported
wftsunsupported
matchunsupported
imatchunsupported
isdistinctunsupported
slunsupported
srunsupported
nxlunsupported
nxrunsupported
adjunsupported

Prefer

ValueSupport
return=minimalsupported
return=headers-onlysupported
return=representationsupported
count=exactsupported
count=plannedpartial
count=estimatedpartial
resolution=merge-duplicatessupported
resolution=ignore-duplicatessupported
missing=defaultpartial
handling=strictsupported
handling=lenientsupported

Accept

ValueSupport
application/jsonsupported
*/*supported
application/vnd.pgrst.object+jsonsupported
text/csvunsupported
application/geo+jsonunsupported
application/vnd.pgrst.plan*unsupported

Query syntax

ValueSupport
notsupported
and/or/not.and/not.orsupported
JSON paths (->, ->>)supported
aliasessupported
link embeddingsupported
!field hintsupported
embedded filters/order/paginationunsupported
casts (::)unsupported
aggregatesunsupported
spread (...)unsupported
!inner/!leftunsupported
operator(any)/operator(all)unsupported

planned/estimated currently return exact counts. Fields can declare a default. In a bulk insert, a column some rows supply and others omit takes the field default where omitted with missing=default, and null otherwise, as in PostgREST; a column no row supplies always takes the default (null without one). cs/cd/ov accept arrays ({a,b} or ["a"]); on json fields cs/cd also accept a JSON object and use jsonb containment, e.g. meta=cs.{"color":"red"} (keys and values are contained recursively); cs also accepts a JSON scalar, matching a top-level array that holds it, e.g. meta=cs."a". cs.{}/cd.{} is the empty object on json fields and the empty array on array fields. Rows deleted by one DELETE may reference each other; a referencing row outside that DELETE still returns 409 23503. like/ilike apply only to string fields and ->> text paths; other types return 400 42883. Strings compare and sort in binary order (case-sensitive, uppercase before lowercase); ordering by lower() is not supported, so store a lowercase copy in another field and order by it for case-insensitive sorting. GET/HEAD with If-None-Match matching the current ETag return 304 with no body. Unsupported Accept values return 406 PGRST107. Upsert accepts id or one unique field.

Unknown Prefer values are ignored by default; handling=strict returns 400 PGRST122. Singular Accept returns an object for exactly one row, otherwise 406 PGRST116. Other unsupported Accept values return 406 PGRST107.

Unavailable services

PathHTTPcode
/supabase/{database_id}/auth/v1/{+path}501not_implemented
/supabase/{database_id}/storage/v1/{+path}501not_implemented
/supabase/{database_id}/realtime/v1/{+path}501not_implemented
/supabase/{database_id}/functions/v1/{+path}501not_implemented
/supabase/{database_id}/graphql/v1/{+path}501not_implemented

These services return x-should-retry: false. Their error body has only code and message, unlike table endpoint errors.

Error codes and envelopes

Read and write rows

Guide →

/supabase/{database_id}/rest/v1/{table}

Reads and writes the same rows as the other entry point for this database. Errors use PostgREST codes.

Support matrix and client setup

Parameters and request headers

NameLocationDescription
database_idpathDatabase ID.
tablepathtable ID or name.
selectqueryComma-separated columns; supports *, aliases, JSON paths and link-based embedding. Casts, aggregates, spread and join modifiers are unsupported.
{column}queryHorizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. See the filter operator matrix.
andqueryParenthesized conditions, e.g. (done.eq.false,title.neq.draft).
orqueryParenthesized alternatives; not.and and not.or are also supported.
not.andqueryNegated conjunction.
not.orqueryNegated disjunction.
orderqueryComma-separated columns with asc/desc and nullsfirst/nullslast; JSON paths allowed.
limitqueryMaximum returned rows.
offsetqueryRows to skip.
on_conflictqueryPOST upsert: id or one unique field; use Prefer: resolution=merge-duplicates or ignore-duplicates.
columnsqueryComma-separated input columns; keys outside this set are ignored.
RangeheaderGET/HEAD inclusive row interval, e.g. 0-9. Intersects limit/offset.
Range-Unitheaderitems; other units cause Range to be ignored.
PreferheaderComma-separated preferences. Unknown preferences are ignored unless handling=strict (400 PGRST122).
Acceptheaderapplication/json, /, or application/vnd.pgrst.object+json. Singular output requires exactly one row; otherwise 406 PGRST116. Other media types return 406 PGRST107; text/csv, application/geo+json and application/vnd.pgrst.plan return 400 PGRST127.
If-None-MatchheaderEntity tag from a previous GET or HEAD (weak comparison). When it matches the current ETag the response is 304 with no body.

POST accepts an object or an array of objects; PATCH accepts an object. System columns are managed by the service. Writes return no body by default; Prefer: return=representation (SDK .select()) returns rows. PATCH and DELETE affect rows matching the filters; omitting filters matches all rows.

Response headerMeaning
Content-RangeReturned range/total; total is * when no count is requested.
Preference-AppliedApplied preferences.
ETagEntity tag returned by GET/HEAD; a matching If-None-Match returns 304 with no body.
LocationNew row URL for POST with return=headers-only.

Reads normally return 200, or 206 when count is requested and only some rows are returned. A matching If-None-Match returns 304. datetime values are stored and read back in UTC with exactly three fractional digits (e.g. 2026-10-11T00:30:00.000Z); precision is milliseconds and extra fractional digits are rounded half up. POST returns 201. PATCH/DELETE return 200 with representation, otherwise 204. HEAD has no body.

Select rows

GET /supabase/{database_id}/rest/v1/{table}

const { data, error } = await supabase.from("tasks").select("*").order("title").limit(10);
if (error) throw error;
console.log(data);

Response

HTTP 200

Success; write bodies require Prefer: return=representation

application/json

NameTypeDescription
[].idstring
[].versioninteger
[].created_atstring
[].updated_atstring

application/vnd.pgrst.object+json

NameTypeDescription
idstring
versioninteger
created_atstring
updated_atstring

HTTP 206

Partial range with requested count

application/json

NameTypeDescription
[].idstring
[].versioninteger
[].created_atstring
[].updated_atstring

application/vnd.pgrst.object+json

NameTypeDescription
idstring
versioninteger
created_atstring
updated_atstring

Response example — HTTP 200

HeaderValue
content-range0-0/*
etagW/"5.1"
range-unititems
[
  {
    "id": "rec_e88fcbd357aa49c9",
    "version": 1,
    "created_at": "2026-10-11T17:42:00.094Z",
    "updated_at": "2026-10-11T17:42:00.094Z",
    "title": "Write a summary",
    "done": false
  }
]

Count rows

HEAD /supabase/{database_id}/rest/v1/{table}

const { data, error, count } = await supabase.from("tasks").select("*", { head: true, count: "exact" });
if (error) throw error;
console.log(count);

Response

HTTP 200

Success; write bodies require Prefer: return=representation

No response body is declared.

HTTP 206

Partial range with requested count

No response body is declared.

Response example — HTTP 200

HeaderValue
content-range0-0/1
preference-appliedcount=exact
etagW/"5.1"
range-unititems

No response body.

Insert rows

POST /supabase/{database_id}/rest/v1/{table}

const { data, error } = await supabase.from("tasks").insert({"title":"Write a summary","done":false}).select();
if (error) throw error;
console.log(data);

Response

HTTP 201

Success; write bodies require Prefer: return=representation

application/json

NameTypeDescription
[].idstring
[].versioninteger
[].created_atstring
[].updated_atstring

application/vnd.pgrst.object+json

NameTypeDescription
idstring
versioninteger
created_atstring
updated_atstring

Response example — HTTP 201

HeaderValue
content-range0-0/*
preference-appliedreturn=representation
range-unititems
[
  {
    "id": "rec_e88fcbd357aa49c9",
    "version": 1,
    "created_at": "2026-10-11T17:42:00.094Z",
    "updated_at": "2026-10-11T17:42:00.094Z",
    "title": "Write a summary",
    "done": false
  }
]

Update rows

PATCH /supabase/{database_id}/rest/v1/{table}

const { data, error } = await supabase.from("tasks").update({ done: true }).eq("title", "Write a summary").select();
if (error) throw error;
console.log(data);

Response

HTTP 200

Success; write bodies require Prefer: return=representation

application/json

NameTypeDescription
[].idstring
[].versioninteger
[].created_atstring
[].updated_atstring

application/vnd.pgrst.object+json

NameTypeDescription
idstring
versioninteger
created_atstring
updated_atstring

HTTP 204

Success without a body

No response body is declared.

Response example — HTTP 200

HeaderValue
content-range0-0/*
preference-appliedreturn=representation
range-unititems
[
  {
    "id": "rec_e88fcbd357aa49c9",
    "version": 2,
    "created_at": "2026-10-11T17:42:00.094Z",
    "updated_at": "2026-10-11T17:42:01.047Z",
    "title": "Write a summary",
    "done": true
  }
]

Upsert rows

POST /supabase/{database_id}/rest/v1/{table}

const { data, error } = await supabase.from("tasks").upsert({"title":"Write a summary","done":true}, { onConflict: "title" }).select();
if (error) throw error;
console.log(data);

Response

HTTP 201

Success; write bodies require Prefer: return=representation

application/json

NameTypeDescription
[].idstring
[].versioninteger
[].created_atstring
[].updated_atstring

application/vnd.pgrst.object+json

NameTypeDescription
idstring
versioninteger
created_atstring
updated_atstring

Response example — HTTP 201

HeaderValue
content-range0-0/*
preference-appliedresolution=merge-duplicates, return=representation
range-unititems
[
  {
    "id": "rec_e88fcbd357aa49c9",
    "version": 3,
    "created_at": "2026-10-11T17:42:00.094Z",
    "updated_at": "2026-10-11T17:42:01.735Z",
    "title": "Write a summary",
    "done": true
  }
]

Delete rows

DELETE /supabase/{database_id}/rest/v1/{table}

const { data, error } = await supabase.from("tasks").delete().eq("title", "Write a summary").select();
if (error) throw error;
console.log(data);

Response

HTTP 200

Success; write bodies require Prefer: return=representation

application/json

NameTypeDescription
[].idstring
[].versioninteger
[].created_atstring
[].updated_atstring

application/vnd.pgrst.object+json

NameTypeDescription
idstring
versioninteger
created_atstring
updated_atstring

HTTP 204

Success without a body

No response body is declared.

Response example — HTTP 200

HeaderValue
content-range0-0/*
preference-appliedreturn=representation
range-unititems
[
  {
    "id": "rec_e88fcbd357aa49c9",
    "version": 3,
    "created_at": "2026-10-11T17:42:00.094Z",
    "updated_at": "2026-10-11T17:42:01.735Z",
    "title": "Write a summary",
    "done": true
  }
]