TypeScript library that translates SQL queries to the equivalent PostgREST-compitable HTTP requests and client library code. Works on both browser and server.
PostgREST is a REST API that auto-generates endpoints based on relations in your database (public schema only by default). It uses JWTs and RLS policies to handle authorization.
The PostgREST API supports a lot of SQL-like features including:
- Vertical filtering (select only the columns you care about)
- Horizontal filtering (filter rows by comparing data in columns:
=,>,<,in,like, etc) - Sorting
- Limit and offset
- Resource embeddings (joins to other relations using foreign keys)
- Aggregate operations (
count(),sum(),avg(),min(),max()) - Nested
AND/ORexpressions - Aliasing and casting
- JSON columns (selecting, filtering, and sorting)
This library takes the SQL input and translates it to 1-to-1 to the equivalent PostgREST syntax. Any unsupported SQL will throw an error.
The following SQL:
select
title,
description
from
books
where
description ilike '%cheese%'
order by
title desc
limit
5
offset
10Will get translated to:
cURL
curl -G http://localhost:54321/rest/v1/books \
-d "select=title,description" \
-d "description=ilike.*cheese*" \
-d "order=title.desc" \
-d "limit=5" \
-d "offset=10"Raw HTTP
GET /rest/v1/books?select=title,description&description=ilike.*cheese*&order=title.desc&limit=5&offset=10 HTTP/1.1
Host: localhost:54321supabase-js
const { data, error } = await supabase
.from('books')
.select(
`
title,
description
`
)
.ilike('description', '%cheese%')
.order('title', { ascending: false })
.range(10, 15)npm i @supabase/sql-to-restyarn add @supabase/sql-to-restNote: This library is pre-1.0, so expect slight API changes over time.
import { processSql, renderHttp, formatCurl } from '@supabase/sql-to-rest'
// Process SQL into intermediate PostgREST AST
const statement = await processSql(`
select
*
from
books
`)
// Render the AST into an HTTP request
const httpRequest = await renderHttp(statement)
// Format the HTTP request as a cURL command (requires base URL)
const curlCommand = formatCurl('http://localhost:54321/rest/v1', httpRequest)
console.log(curlCommand)
// curl http://localhost:54321/rest/v1/books
// Or use it directly
const response = await fetch(`http://localhost:54321/rest/v1${httpRequest.fullPath}`, {
method: httpRequest.method,
})Takes a SQL string and converts it into a PostgREST abstract syntax tree (AST) called a Statement. This is an intermediate object that can later be rendered to your language/protocol of choice.
import { processSql } from '@supabase/sql-to-rest'
const statement = await processSql(`
select
*
from
books
`)Outputs a Promise<Statement>:
{
type: 'select',
from: 'books',
targets: [
{
type: 'column-target',
column: '*',
alias: undefined,
},
],
filter: undefined,
sorts: [],
limit: undefined
}Takes the intermediate Statement and renders it as an HTTP request.
import { processSql, renderHttp } from '@supabase/sql-to-rest'
const statement = await processSql(`
select
*
from
books
`)
const httpRequest = await renderHttp(statement)Outputs a Promise<HttpRequest>:
{
method: 'GET',
path: '/books',
params: URLSearchParams {},
fullPath: [Getter] // combines path with the query params
}An HttpRequest can also be formatted as a cURL command or as raw HTTP.
import {
// ...
formatCurl,
} from '@supabase/sql-to-rest'
// ...
const curlCommand = formatCurl('http://localhost:54321/rest/v1', httpRequest)Outputs:
curl http://localhost:54321/rest/v1/booksimport {
// ...
formatHttp,
} from '@supabase/sql-to-rest'
// ...
const rawHttp = formatHttp('http://localhost:54321/rest/v1', httpRequest)Outputs:
GET /rest/v1/books HTTP/1.1
Host: localhost:54321Takes the intermediate Statement and renders it as supabase-js client code.
import { processSql, renderSupabaseJs } from '@supabase/sql-to-rest'
const statement = await processSql(`
select
*
from
books
`)
const { code } = await renderSupabaseJs(statement)Outputs a Promise<SupabaseJsQuery>, where code contains:
const { data, error } = await supabase.from('books').select()The rendered JS code is automatically formatted using prettier.
- The SQL string is parsed into a PostgreSQL abstract syntax tree (AST) using
pg-parser, a JavaScript SQL parser that uses C code from the official PostgreSQL codebase (compiled to WASM). Supports Postgres 17 syntax. - The PostgreSQL AST is translated into a much smaller and simpler PostgREST AST. Since PostgREST supports a subset of SQL syntax, any unsupported SQL operation will throw an
UnsupportedErrorwith a description of exactly what wasn't supported. - The intermediate PostgREST AST can be rendered to your language/protocol of choice. Currently supports HTTP (with
cURLand raw HTTP formatters), andsupabase-jscode (which wraps PostgREST). Other languages are on the roadmap (PR's welcome!)
-
selectstatements (GETrequests) -
insertstatements (POSTrequests)-
on conflict update(upsert)
-
-
updatestatements (PATCHrequests) -
deletestatements (DELETErequests) -
explainstatements (Execution plan)
-
=(eq) -
>(gt) -
>=(gte) -
<(lt) -
<=(lte) -
<>or!=(neq) -
like(like) -
ilike(ilike) -
~(match) -
~*(imatch) -
in(in) -
is(is): partial support, onlyis nullfor now -
is distinct from(isdistinct) -
@@(fts,plfts,phfts,wfts) -
@>(cs) -
<@(cd) -
&&(ov) -
<<(sl) -
>>(sr) -
&<(nxr) -
&>(nxl) -
-|-(adj)
-
not(not) -
or(or) -
and(and) -
all(all) -
any(any)
-
asc(asc) -
desc(desc) -
nulls first(nullsfirst) -
nulls last(nullslast)
-
limit(limit) -
offset(offset) - HTTP range headers
-
count() -
sum() -
avg() -
max() -
min()
- aggregate over entire table
- aggregate on joined table column
- aggregate with
group by - aggregate with
group byon joined table column
Joins (Resource Embedding)
SQL joins are supported using PostgREST resource embeddings with the spread ... syntax (flattens joined table into primary table).
- column aliases
- table aliases
- column casts (in select target only)
- aggregate function casts (in select target only)
JSON columns (eg. select metadata->'person'->>'name') are supported in the following places:
- select targets
- filters
- sorts
- HTTP
- cURL formatter
- Raw HTTP formatter
-
supabase-js -
supabase-flutter -
supabase-swift -
supabase-py -
supabase-csharp -
supabase-kt
Thankfully no. We use pg-parser which compiles source code from the real PostgreSQL parser to WASM and wraps it in JavaScript bindings.
This means we never have to worry about the SQL itself getting parsed incorrectly - it uses the exact same code as the actual PostgreSQL database. This library uses code from PostgreSQL 17.
It can't. PostgREST only supports a subset of SQL-like features (by design), so this library only translates features that can be mapped 1-to-1.
When it detects SQL that doesn't translate (eg. sub-queries), it will throw an UnsupportedError with a description of exactly what couldn't be translated.
We've built unit tests for every feature supported. The vast majority of PostgREST features have been implemented, but it doesn't cover 100% yet (see Roadmap). If you discover an error in the translation, please submit an issue.
MIT