Golang client for PostgREST. The goal of this library is to make an "ORM-like" restful interface.
Full documentation can be found here.
go get github.com/supabase-community/postgrest-gopackage main
import (
"context"
"encoding/json"
"fmt"
"github.com/supabase-community/postgrest-go"
)
func main() {
// Create a new client
client := postgrest.NewClient("http://localhost:3000/rest/v1", "public", nil)
if client.ClientError != nil {
panic(client.ClientError)
}
// Select data
response, err := client.From("users").Select("*", nil).Execute(context.Background())
if err != nil {
panic(err)
}
if response.Error != nil {
panic(response.Error)
}
// Print results
data, _ := json.Marshal(response.Data)
fmt.Println(string(data))
}// Select with filters
opts := &postgrest.SelectOptions{Count: "exact"}
response, err := client.
From("users").
Select("id, name, email", opts).
Eq("status", "active").
Limit(10, nil).
Order("name", &postgrest.OrderOptions{Ascending: true}).
Execute(context.Background())// Insert a single row
insertOpts := &postgrest.InsertOptions{Count: "exact"}
response, err := client.
From("users").
Insert(map[string]interface{}{
"name": "John Doe",
"email": "[email protected]",
}, insertOpts).
Execute(context.Background())// Update rows
updateOpts := &postgrest.UpdateOptions{Count: "exact"}
response, err := client.
From("users").
Update(map[string]interface{}{
"status": "inactive",
}, updateOpts).
Eq("id", 1).
Execute(context.Background())// Delete rows
deleteOpts := &postgrest.DeleteOptions{Count: "exact"}
response, err := client.
From("users").
Delete(deleteOpts).
Eq("status", "inactive").
Execute(context.Background())// Upsert (insert or update)
upsertOpts := &postgrest.UpsertOptions{
OnConflict: "email",
IgnoreDuplicates: false,
Count: "exact",
}
response, err := client.
From("users").
Upsert(map[string]interface{}{
"email": "[email protected]",
"name": "John Doe",
}, upsertOpts).
Execute(context.Background())// Call a PostgreSQL function
rpcOpts := &postgrest.RpcOptions{Count: "exact"}
response, err := client.
Rpc("get_user_by_email", map[string]interface{}{
"email": "[email protected]",
}, rpcOpts).
Execute(context.Background())// Multiple filters
response, err := client.
From("users").
Select("*", nil).
Eq("status", "active").
Gte("age", 18).
Lte("age", 65).
In("role", []interface{}{"admin", "user"}).
Like("name", "%John%").
Execute(context.Background())
// Text search
textSearchOpts := &postgrest.TextSearchOptions{
Type: "websearch",
Config: "english",
}
response, err := client.
From("posts").
Select("*", nil).
TextSearch("content", "golang tutorial", textSearchOpts).
Execute(context.Background())// Get a single row
response, err := client.
From("users").
Select("*", nil).
Eq("id", 1).
Limit(1, nil).
Single().
Execute(context.Background())
// Get a single row or null (maybeSingle)
response, err := client.
From("users").
Select("*", nil).
Eq("id", 999).
MaybeSingle().
Execute(context.Background())// Unmarshal directly into a struct
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var users []User
count, err := client.
From("users").
Select("*", nil).
Eq("status", "active").
ExecuteTo(context.Background(), &users)// Switch to a different schema
client = client.Schema("private")
response, err := client.From("sensitive_data").Select("*", nil).Execute(context.Background())response, err := client.From("users").Select("*", nil).Execute(context.Background())
if err != nil {
// Network or parsing error
panic(err)
}
if response.Error != nil {
// PostgREST error
fmt.Printf("Error: %s (Code: %s)\n", response.Error.Message, response.Error.Code)
fmt.Printf("Details: %s\n", response.Error.Details)
fmt.Printf("Hint: %s\n", response.Error.Hint)
}// Throw errors instead of returning them in response
response, err := client.
From("users").
Select("*", nil).
ThrowOnError().
Execute(context.Background())
// If there's an error, it will be thrown hereNewClient(url, schema, headers)- Create a new clientFrom(table)- Start a query on a tableRpc(function, args, opts)- Call a PostgreSQL functionSchema(schema)- Switch to a different schemaSetApiKey(key)- Set API key headerSetAuthToken(token)- Set authorization tokenChangeSchema(schema)- Change schema for subsequent requests
Select(columns, opts)- Select columnsInsert(values, opts)- Insert rowsUpdate(values, opts)- Update rowsUpsert(values, opts)- Upsert rowsDelete(opts)- Delete rows
Eq(column, value)- EqualNeq(column, value)- Not equalGt(column, value)- Greater thanGte(column, value)- Greater than or equalLt(column, value)- Less thanLte(column, value)- Less than or equalLike(column, pattern)- Case-sensitive pattern matchIlike(column, pattern)- Case-insensitive pattern matchIs(column, value)- IS operator (for NULL checks)In(column, values)- IN operatorContains(column, value)- Contains (for arrays/jsonb)ContainedBy(column, value)- Contained byTextSearch(column, query, opts)- Full-text searchMatch(query)- Match multiple columnsNot(column, operator, value)- Negate operatorOr(filters, opts)- OR condition
Order(column, opts)- Order resultsLimit(count, opts)- Limit resultsRange(from, to, opts)- Range resultsSingle()- Get single resultMaybeSingle()- Get single result or nullSelect(columns)- Select after insert/update/deleteCSV()- Return CSV formatGeoJSON()- Return GeoJSON formatExplain(opts)- Get query planRollback()- Rollback transactionMaxAffected(value)- Set max affected rows
type PostgrestResponse[T any] struct {
Error *PostgrestError `json:"error,omitempty"`
Data T `json:"data,omitempty"`
Count *int64 `json:"count,omitempty"`
Status int `json:"status"`
StatusText string `json:"statusText"`
}Some tests are implemented to run against mocked Postgrest endpoints using httpmock. To run unit tests:
go test ./...Integration tests use testcontainers-go to spin up real PostgreSQL and PostgREST containers for testing. This ensures tests run against actual PostgREST instances.
Option 1: Using Testcontainers (Default)
Integration tests will automatically use testcontainers to start PostgreSQL and PostgREST containers:
go test -v -run TestIntegrationOption 2: Using Existing Services
If you have a running PostgREST instance, you can use it instead:
export POSTGREST_URL=http://localhost:3000
export USE_TESTCONTAINERS=false
go test -v -run TestIntegrationOption 3: Using Docker Compose
You can also use the provided docker-compose.yaml to start services manually:
docker-compose up -d
export POSTGREST_URL=http://localhost:3000
export USE_TESTCONTAINERS=false
go test -v -run TestIntegration
docker-compose downThe integration tests cover:
- ✅ Basic SELECT queries
- ✅ Filtering (Eq, In, Like, etc.)
- ✅ Ordering and limiting
- ✅ Single and MaybeSingle results
- ✅ INSERT operations
- ✅ UPDATE operations
- ✅ DELETE operations
- ✅ RPC (Remote Procedure Calls)
- ✅ Schema switching
- ✅ Complex queries with multiple filters
- ✅ Error handling
- ✅ Relationships and joins
The integration tests use the schema defined in test/00-schema.sql and seed data from test/01-dummy-data.sql. These files define:
userstable with various data typeschannelsandmessagestables for relationship testingkitchen_sinktable for comprehensive type testingmovie,person,profiletables for foreign key testing- PostgreSQL functions for RPC testing
- Multiple schemas (
publicandpersonal) for schema switching tests
Made with contrib.rocks.
This repo is licensed under the Apache License.
We are building the features of Firebase using enterprise-grade, open source products. We support existing communities wherever possible, and if the products don't exist we build them and open source them ourselves. Thanks to these sponsors who are making the OSS ecosystem better for everyone.
