2026-10-22
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.
https://api.deepraxis.ai/supabase/{database_id}/
| Part | Meaning |
|---|---|
| {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 } },
);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.
| Value | Support |
|---|---|
eq | supported |
neq | supported |
gt | supported |
gte | supported |
lt | supported |
lte | supported |
like | supported |
ilike | supported |
in | supported |
is | supported |
cs | supported |
cd | supported |
ov | supported |
fts | unsupported |
plfts | unsupported |
phfts | unsupported |
wfts | unsupported |
match | unsupported |
imatch | unsupported |
isdistinct | unsupported |
sl | unsupported |
sr | unsupported |
nxl | unsupported |
nxr | unsupported |
adj | unsupported |
| Value | Support |
|---|---|
return=minimal | supported |
return=headers-only | supported |
return=representation | supported |
count=exact | supported |
count=planned | partial |
count=estimated | partial |
resolution=merge-duplicates | supported |
resolution=ignore-duplicates | supported |
missing=default | partial |
handling=strict | supported |
handling=lenient | supported |
| Value | Support |
|---|---|
application/json | supported |
*/* | supported |
application/vnd.pgrst.object+json | supported |
text/csv | unsupported |
application/geo+json | unsupported |
application/vnd.pgrst.plan* | unsupported |
| Value | Support |
|---|---|
not | supported |
and/or/not.and/not.or | supported |
JSON paths (->, ->>) | supported |
aliases | supported |
link embedding | supported |
!field hint | supported |
embedded filters/order/pagination | unsupported |
casts (::) | unsupported |
aggregates | unsupported |
spread (...) | unsupported |
!inner/!left | unsupported |
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.
| Path | HTTP | code |
|---|---|---|
| /supabase/{database_id}/auth/v1/{+path} | 501 | not_implemented |
| /supabase/{database_id}/storage/v1/{+path} | 501 | not_implemented |
| /supabase/{database_id}/realtime/v1/{+path} | 501 | not_implemented |
| /supabase/{database_id}/functions/v1/{+path} | 501 | not_implemented |
| /supabase/{database_id}/graphql/v1/{+path} | 501 | not_implemented |
These services return x-should-retry: false. Their error body has only code and message, unlike table endpoint errors.
/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
| Name | Location | Description |
|---|---|---|
| database_id | path | Database ID. |
| table | path | table ID or name. |
| select | query | Comma-separated columns; supports *, aliases, JSON paths and link-based embedding. Casts, aggregates, spread and join modifiers are unsupported. |
| {column} | query | Horizontal filter: replace {column} with a field name or JSON path, e.g. title=eq.summary. Repeat for more columns. See the filter operator matrix. |
| and | query | Parenthesized conditions, e.g. (done.eq.false,title.neq.draft). |
| or | query | Parenthesized alternatives; not.and and not.or are also supported. |
| not.and | query | Negated conjunction. |
| not.or | query | Negated disjunction. |
| order | query | Comma-separated columns with asc/desc and nullsfirst/nullslast; JSON paths allowed. |
| limit | query | Maximum returned rows. |
| offset | query | Rows to skip. |
| on_conflict | query | POST upsert: id or one unique field; use Prefer: resolution=merge-duplicates or ignore-duplicates. |
| columns | query | Comma-separated input columns; keys outside this set are ignored. |
| Range | header | GET/HEAD inclusive row interval, e.g. 0-9. Intersects limit/offset. |
| Range-Unit | header | items; other units cause Range to be ignored. |
| Prefer | header | Comma-separated preferences. Unknown preferences are ignored unless handling=strict (400 PGRST122). |
| Accept | header | application/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-Match | header | Entity 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 header | Meaning |
|---|---|
| Content-Range | Returned range/total; total is * when no count is requested. |
| Preference-Applied | Applied preferences. |
| ETag | Entity tag returned by GET/HEAD; a matching If-None-Match returns 304 with no body. |
| Location | New 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.
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);Success; write bodies require Prefer: return=representation
application/json
| Name | Type | Description |
|---|---|---|
| [].id | string | |
| [].version | integer | |
| [].created_at | string | |
| [].updated_at | string |
application/vnd.pgrst.object+json
| Name | Type | Description |
|---|---|---|
| id | string | |
| version | integer | |
| created_at | string | |
| updated_at | string |
Partial range with requested count
application/json
| Name | Type | Description |
|---|---|---|
| [].id | string | |
| [].version | integer | |
| [].created_at | string | |
| [].updated_at | string |
application/vnd.pgrst.object+json
| Name | Type | Description |
|---|---|---|
| id | string | |
| version | integer | |
| created_at | string | |
| updated_at | string |
Response example — HTTP 200
| Header | Value |
|---|---|
| content-range | 0-0/* |
| etag | W/"5.1" |
| range-unit | items |
[
{
"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
}
]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);Success; write bodies require Prefer: return=representation
No response body is declared.
Partial range with requested count
No response body is declared.
Response example — HTTP 200
| Header | Value |
|---|---|
| content-range | 0-0/1 |
| preference-applied | count=exact |
| etag | W/"5.1" |
| range-unit | items |
No response body.
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);Success; write bodies require Prefer: return=representation
application/json
| Name | Type | Description |
|---|---|---|
| [].id | string | |
| [].version | integer | |
| [].created_at | string | |
| [].updated_at | string |
application/vnd.pgrst.object+json
| Name | Type | Description |
|---|---|---|
| id | string | |
| version | integer | |
| created_at | string | |
| updated_at | string |
Response example — HTTP 201
| Header | Value |
|---|---|
| content-range | 0-0/* |
| preference-applied | return=representation |
| range-unit | items |
[
{
"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
}
]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);Success; write bodies require Prefer: return=representation
application/json
| Name | Type | Description |
|---|---|---|
| [].id | string | |
| [].version | integer | |
| [].created_at | string | |
| [].updated_at | string |
application/vnd.pgrst.object+json
| Name | Type | Description |
|---|---|---|
| id | string | |
| version | integer | |
| created_at | string | |
| updated_at | string |
Success without a body
No response body is declared.
Response example — HTTP 200
| Header | Value |
|---|---|
| content-range | 0-0/* |
| preference-applied | return=representation |
| range-unit | items |
[
{
"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
}
]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);Success; write bodies require Prefer: return=representation
application/json
| Name | Type | Description |
|---|---|---|
| [].id | string | |
| [].version | integer | |
| [].created_at | string | |
| [].updated_at | string |
application/vnd.pgrst.object+json
| Name | Type | Description |
|---|---|---|
| id | string | |
| version | integer | |
| created_at | string | |
| updated_at | string |
Response example — HTTP 201
| Header | Value |
|---|---|
| content-range | 0-0/* |
| preference-applied | resolution=merge-duplicates, return=representation |
| range-unit | items |
[
{
"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 /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);Success; write bodies require Prefer: return=representation
application/json
| Name | Type | Description |
|---|---|---|
| [].id | string | |
| [].version | integer | |
| [].created_at | string | |
| [].updated_at | string |
application/vnd.pgrst.object+json
| Name | Type | Description |
|---|---|---|
| id | string | |
| version | integer | |
| created_at | string | |
| updated_at | string |
Success without a body
No response body is declared.
Response example — HTTP 200
| Header | Value |
|---|---|
| content-range | 0-0/* |
| preference-applied | return=representation |
| range-unit | items |
[
{
"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
}
]