Skip to Content
APIREST API

REST API

Read, insert, update and delete rows. The syntax follows PostgREST.

Every path lives under https://gridie.ai/api/v1.

List tables

GET /api/v1/tables

Returns the tables this key can work with, along with their columns. Use it to check the shape before writing a client.

{ "tables": [ { "name": "orders", "label": "Orders", "description": null, "rowCount": 128, "columns": [ { "name": "id", "type": "INTEGER", "nullable": false, "primaryKey": true }, { "name": "customer", "type": "TEXT", "nullable": true, "label": "Customer name" } ] } ] }

name is what you use in requests, label is what appears in the Gridie interface. An empty list means no table has been opened to the API yet.

This endpoint is a Gridie addition, which is why the response is an object with a tables key rather than a bare array.

Read rows

GET /api/v1/tables/orders

The response is a bare array of rows, with no wrapper key.

Query parameters

NameFormatDescription
selectcomma listColumns to return. Defaults to all
ordercolumn.directionSuch as created_at.desc. Comma separated for several
limitintegerDefaults to 50, capped at 1000
offsetintegerRows to skip
any other nameoperator.valueA column filter, such as status=eq.paid
curl "https://gridie.ai/api/v1/tables/orders?select=id,customer,total&status=eq.paid&total=gte.10000&order=created_at.desc&limit=20" \ -H "apikey: $GRIDIE_API_KEY"

Name a column that does not exist and you get a 400 telling you which name was wrong. It is never silently ignored.

Filter operators

OperatorMeaningExample
eqEqualsstatus=eq.paid
neqNot equalsstatus=neq.draft
gt gteGreater than, or equaltotal=gte.10000
lt lteLess than, or equaltotal=lt.500
likePattern match, * is the wildcardname=like.Ada*
ilikeSame, ignoring caseemail=ilike.*@gmail.com
isMatches null, true or falsepaid_at=is.null
inAny of a liststatus=in.(paid,shipped)

Prefix with not. to negate the whole condition that follows.

status=not.eq.paid paid_at=not.is.null

Several parameters combine with AND.

Ordering

order=created_at.desc order=priority.asc.nullslast,created_at.desc

Direction is asc or desc, and null placement is nullsfirst or nullslast.

Counting

We do not count by default. Ask for it with a header when you need a total for pagination.

curl "https://gridie.ai/api/v1/tables/orders?limit=20" \ -H "apikey: $GRIDIE_API_KEY" \ -H "Prefer: count=exact"

It arrives in the Content-Range header.

Content-Range: 0-19/128

Without the header the total is *. Counting every time would mean a second query on every read.

A single row

Ask for one object with the Accept header. This is what supabase-js sends for .single().

curl "https://gridie.ai/api/v1/tables/orders?id=eq.1" \ -H "apikey: $GRIDIE_API_KEY" \ -H "Accept: application/vnd.pgrst.object+json"

If the result is not exactly one row, you get a 406.

Insert rows

POST /api/v1/tables/orders

Send one object or an array of objects as the body. There is no wrapper key.

curl -X POST "https://gridie.ai/api/v1/tables/orders" \ -H "apikey: $GRIDIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Prefer: return=representation" \ -d '[{"customer":"Ada Lovelace","total":42000,"status":"paid"}]'

With Prefer: return=representation you get a 201 and the stored rows. Without it, a bare 204.

When you send several rows, every row must carry the same set of columns. If the columns differ per row, the request alone does not say which values should fall back to defaults, so we reject it with a 400.

Upsert

Add Prefer: resolution=merge-duplicates to overwrite on a primary key collision. This is the header supabase-js sends for .upsert().

curl -X POST "https://gridie.ai/api/v1/tables/orders" \ -H "apikey: $GRIDIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Prefer: resolution=merge-duplicates,return=representation" \ -d '[{"id":1,"status":"shipped"}]'

Use resolution=ignore-duplicates to skip colliding rows instead.

Upsert needs a primary key on the table. Without one there is nothing to call a collision, so the request fails with a 400.

Update rows

PATCH /api/v1/tables/orders

The body holds the new values as a single object, and query parameters decide which rows to change.

curl -X PATCH "https://gridie.ai/api/v1/tables/orders?id=eq.1" \ -H "apikey: $GRIDIE_API_KEY" \ -H "Content-Type: application/json" \ -H "Prefer: return=representation" \ -d '{"status":"archived"}'

A request with no filter is refused. An unfiltered UPDATE rewrites the whole table. If you really do mean every row, say so with a condition that is always true, such as id=gte.0.

Delete rows

DELETE /api/v1/tables/orders
curl -X DELETE "https://gridie.ai/api/v1/tables/orders?status=eq.draft" \ -H "apikey: $GRIDIE_API_KEY" \ -H "Prefer: return=representation"

As with updates, a request without a filter is refused.

How many rows a write returns

Updates and deletes touch every row the filter matches. The rows carried back by Prefer: return=representation stop at 1000, so the response’s Content-Range leaves the total as *.

Content-Range: 0-999/*

Do not read the number of returned rows as the number of rows changed. Past 1000 they differ.

Request headers

HeaderWhat it does
apikeyAuthentication. Authorization: Bearer <key> also works
Prefer: return=representationReturn the written rows. Otherwise 204
Prefer: count=exactCount all matches and report it in Content-Range
Prefer: resolution=merge-duplicatesTurn a POST into an upsert
Prefer: resolution=ignore-duplicatesSkip colliding rows
Accept: application/vnd.pgrst.object+jsonReturn a single row as an object

Connecting with the Supabase client is covered in Supabase Compatibility, and the codes a failed request returns are in Error Codes.

Last updated on

Alert

Input

Enterprise Inquiry

Please leave your inquiry regarding enterprise adoption. Our sales team will contact you shortly.
Attachments