Learn

Navigate through learn topics

Auto-Generated APIs

How Supabase creates instant REST and GraphQL endpoints from your tables

Last updated: 8/15/2025

The moment you create a table in Supabase, it becomes a fully functional REST API. No backend code required.

Instant API Creation

Create a table called products:

CREATE TABLE products (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  price DECIMAL(10,2),
  category TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

You immediately get these endpoints:

  • GET /rest/v1/products (List all products)
  • POST /rest/v1/products (Create a new product)
  • PATCH /rest/v1/products?id=eq.{id} (Update a product)
  • DELETE /rest/v1/products?id=eq.{id} (Delete a product)

Basic REST Operations

Reading Data

# Get all products
curl "https://your-project.supabase.co/rest/v1/products" \
  -H "apikey: your-anon-key"

# Get specific columns
curl "https://your-project.supabase.co/rest/v1/products?select=name,price" \
  -H "apikey: your-anon-key"

# Get one product by ID
curl "https://your-project.supabase.co/rest/v1/products?id=eq.123&select=*" \
  -H "apikey: your-anon-key"

Filtering with Query Parameters

Supabase uses PostgREST operators for filtering:

# Equal to
?name=eq.iPhone

# Not equal
?price=neq.0

# Greater than
?price=gt.100

# Less than or equal
?price=lte.500

# Pattern matching
?name=like.*Phone*
?email=ilike.*@gmail.com

# In a list
?category=in.(electronics,books)

# Date ranges
?created_at=gte.2025-01-01&created_at=lt.2025-02-01

Real-World Examples

# Products under $100 in electronics category
curl "https://your-project.supabase.co/rest/v1/products?category=eq.electronics&price=lt.100&select=name,price" \
  -H "apikey: your-anon-key"

# Recent orders with customer info
curl "https://your-project.supabase.co/rest/v1/orders?created_at=gte.2025-01-01&select=id,total,created_at,customers(name,email)" \
  -H "apikey: your-anon-key"

Sorting and Pagination

# Sort by price (ascending)
?order=price.asc

# Sort by multiple columns
?order=category.asc,price.desc

# Limit results
?limit=10

# Pagination (skip first 20, get next 10)
?limit=10&offset=20

# Range-based pagination
Range: 20-29

Nested Relationships

Query related data in a single request:

# Get customers with their orders
curl "https://your-project.supabase.co/rest/v1/customers?select=name,email,orders(id,total,created_at)" \
  -H "apikey: your-anon-key"

# Get orders with customer and order items
curl "https://your-project.supabase.co/rest/v1/orders?select=id,total,customers(name),order_items(quantity,products(name,price))" \
  -H "apikey: your-anon-key"

Writing Data

Creating Records

# Create a new product
curl -X POST "https://your-project.supabase.co/rest/v1/products" \
  -H "apikey: your-service-role-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "New Product",
    "price": 29.99,
    "category": "electronics"
  }'

Updating Records

# Update product price
curl -X PATCH "https://your-project.supabase.co/rest/v1/products?id=eq.123" \
  -H "apikey: your-service-role-key" \
  -H "Content-Type: application/json" \
  -d '{"price": 19.99}'

Deleting Records

# Delete a product
curl -X DELETE "https://your-project.supabase.co/rest/v1/products?id=eq.123" \
  -H "apikey: your-service-role-key"

Using from JavaScript

Basic Queries

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

const supabase = createClient(
  'https://your-project.supabase.co',
  'your-anon-key'
);

// Get all products
const { data: products } = await supabase
  .from('products')
  .select('*');

// Filter and sort
const { data: expensiveProducts } = await supabase
  .from('products')
  .select('name, price')
  .gt('price', 100)
  .order('price', { ascending: false })
  .limit(10);

Relationships

// Get customers with their recent orders
const { data: customers } = await supabase
  .from('customers')
  .select(`
    name,
    email,
    orders (
      id,
      total,
      created_at
    )
  `)
  .order('created_at', { 
    foreignTable: 'orders', 
    ascending: false 
  });

Insert and Update

// Create new product
const { data: newProduct, error } = await supabase
  .from('products')
  .insert({
    name: 'New Product',
    price: 29.99,
    category: 'electronics'
  })
  .select();

// Update existing product
const { data: updatedProduct, error } = await supabase
  .from('products')
  .update({ price: 19.99 })
  .eq('id', productId)
  .select();

// Delete product
const { error } = await supabase
  .from('products')
  .delete()
  .eq('id', productId);

Database Functions as API Endpoints

Create custom logic as PostgreSQL functions:

-- Create a function to get sales summary
CREATE OR REPLACE FUNCTION get_sales_summary(start_date DATE, end_date DATE)
RETURNS TABLE(
  total_orders INTEGER,
  total_revenue DECIMAL,
  average_order DECIMAL
) AS $$
BEGIN
  RETURN QUERY
  SELECT 
    COUNT(*)::INTEGER,
    SUM(total),
    AVG(total)
  FROM orders
  WHERE created_at BETWEEN start_date AND end_date;
END;
$$ LANGUAGE plpgsql;

Call it as an API:

// Call the function
const { data: summary } = await supabase
  .rpc('get_sales_summary', {
    start_date: '2025-01-01',
    end_date: '2025-01-31'
  });

GraphQL Alternative

Enable GraphQL in your project settings for more flexible queries:

query GetCustomersWithOrders {
  customersCollection {
    edges {
      node {
        id
        name
        email
        ordersCollection {
          edges {
            node {
              id
              total
              created_at
            }
          }
        }
      }
    }
  }
}

API Performance Tips

Use select to limit columns:

// Good: only get what you need
.select('id, name, price')

// Avoid: unnecessary data transfer
.select('*')

Use count for pagination:

const { count } = await supabase
  .from('products')
  .select('*', { count: 'exact' })
  .range(0, 9);

Index frequently filtered columns:

-- Speed up category filters
CREATE INDEX idx_products_category ON products(category);

-- Speed up price range queries
CREATE INDEX idx_products_price ON products(price);

Error Handling

const { data, error } = await supabase
  .from('products')
  .select('*')
  .eq('id', productId);

if (error) {
  console.error('Database error:', error.message);
  return;
}

// Use the data
console.log('Product:', data);

Auto-generated APIs turn your database into a backend service instantly. Focus on your data model and let Supabase handle the API layer.

Related Topics

Continue building your Supabase knowledge: