Skip to main content

Introduction

Supabase provides a GraphQL API powered by pg_graphql, allowing you to query your PostgreSQL database using GraphQL. The API is automatically generated from your database schema.
GraphQL support in Supabase is currently in beta. The API is production-ready but may receive breaking changes.

Endpoint

Your GraphQL API is available at:
https://<PROJECT_REF>.supabase.co/graphql/v1

Authentication

Authenticate requests using the same headers as the REST API:
POST https://<PROJECT_REF>.supabase.co/graphql/v1
Content-Type: application/json
apikey: YOUR_ANON_KEY
Authorization: Bearer YOUR_JWT_TOKEN

{
  "query": "query { ... }"
}
apikey
string
required
Your Supabase API key.
Authorization
string
Bearer token for authenticated requests.

Basic Query

Query your database using GraphQL syntax:
query {
  countriesCollection {
    edges {
      node {
        id
        name
        code
        capital
      }
    }
  }
}

Response

{
  "data": {
    "countriesCollection": {
      "edges": [
        {
          "node": {
            "id": 1,
            "name": "United States",
            "code": "US",
            "capital": "Washington, D.C."
          }
        },
        {
          "node": {
            "id": 2,
            "name": "Canada",
            "code": "CA",
            "capital": "Ottawa"
          }
        }
      ]
    }
  }
}

Schema Reflection

The GraphQL schema is automatically generated from your database schema. Each table becomes a collection type with the following structure:
  • <table>Collection - Query multiple rows
  • <table> - Query a single row (if primary key is provided)
  • insert<Table>Collection - Insert rows
  • update<Table>Collection - Update rows
  • deleteFrom<Table>Collection - Delete rows

Example Table

CREATE TABLE countries (
  id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  name TEXT NOT NULL,
  code TEXT NOT NULL,
  capital TEXT,
  population BIGINT
);

Generated Types

type Country {
  id: BigInt!
  name: String!
  code: String!
  capital: String
  population: BigInt
}

type CountriesConnection {
  edges: [CountriesEdge!]!
  pageInfo: PageInfo!
}

type CountriesEdge {
  node: Country!
  cursor: String!
}

type Query {
  countriesCollection(
    first: Int
    last: Int
    before: Cursor
    after: Cursor
    filter: CountryFilter
    orderBy: [CountryOrderBy!]
  ): CountriesConnection
}

Filtering

Filter results using the filter argument:
query {
  countriesCollection(
    filter: {
      name: { eq: "Albania" }
    }
  ) {
    edges {
      node {
        id
        name
        capital
      }
    }
  }
}

Filter Operators

OperatorDescriptionExample
eqEqual{ name: { eq: "Albania" } }
neqNot equal{ name: { neq: "Albania" } }
gtGreater than{ population: { gt: 1000000 } }
gteGreater than or equal{ population: { gte: 1000000 } }
ltLess than{ population: { lt: 1000000 } }
lteLess than or equal{ population: { lte: 1000000 } }
inIn list{ code: { in: ["US", "CA"] } }
isIs null/not null{ capital: { is: null } }
likePattern match{ name: { like: "%United%" } }
ilikeCase-insensitive match{ name: { ilike: "%united%" } }

Combining Filters

Use and, or, and not for complex filters:
query {
  countriesCollection(
    filter: {
      and: [
        { population: { gt: 1000000 } },
        { or: [
          { name: { like: "%A%" } },
          { code: { in: ["US", "CA"] } }
        ]}
      ]
    }
  ) {
    edges {
      node {
        name
        population
      }
    }
  }
}

Ordering

Sort results using the orderBy argument:
query {
  countriesCollection(
    orderBy: [{ name: AscNullsLast }]
  ) {
    edges {
      node {
        name
        population
      }
    }
  }
}

Order Options

  • AscNullsFirst - Ascending with nulls first
  • AscNullsLast - Ascending with nulls last
  • DescNullsFirst - Descending with nulls first
  • DescNullsLast - Descending with nulls last

Multiple Columns

orderBy: [
  { name: AscNullsLast },
  { population: DescNullsLast }
]

Pagination

GraphQL uses cursor-based pagination:

First N Records

query {
  countriesCollection(first: 10) {
    edges {
      node {
        name
      }
      cursor
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
  }
}
first
Int
Number of records to return from the beginning.
after
Cursor
Return records after this cursor.

Next Page

query {
  countriesCollection(
    first: 10,
    after: "WyJuYW1lIiwgIkFsYmFuaWEiXQ=="
  ) {
    edges {
      node {
        name
      }
    }
  }
}

Last N Records

query {
  countriesCollection(last: 10) {
    edges {
      node {
        name
      }
    }
  }
}
last
Int
Number of records to return from the end.
before
Cursor
Return records before this cursor.

Relationships

Query related data using foreign keys:
query {
  countriesCollection {
    edges {
      node {
        id
        name
        citiesCollection {
          edges {
            node {
              name
              population
            }
          }
        }
      }
    }
  }
}
query {
  countriesCollection {
    edges {
      node {
        name
        citiesCollection(
          filter: { population: { gt: 1000000 } }
        ) {
          edges {
            node {
              name
              population
            }
          }
        }
      }
    }
  }
}

Variables

Use variables to make queries reusable:
query GetCountry($code: String!) {
  countriesCollection(
    filter: { code: { eq: $code } }
  ) {
    edges {
      node {
        id
        name
        capital
      }
    }
  }
}

Variables JSON

{
  "code": "US"
}

Fragments

Reuse query parts with fragments:
fragment CountryDetails on Country {
  id
  name
  code
  capital
  population
}

query {
  countriesCollection {
    edges {
      node {
        ...CountryDetails
        citiesCollection(first: 5) {
          edges {
            node {
              name
            }
          }
        }
      }
    }
  }
}

Introspection

Explore your schema using introspection:
query {
  __schema {
    types {
      name
      kind
    }
  }
}

Get Type Details

query {
  __type(name: "Country") {
    name
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}

Row Level Security

GraphQL respects your PostgreSQL Row Level Security policies:
CREATE POLICY "Users can read own data"
  ON countries
  FOR SELECT
  USING (auth.uid() = user_id);
The GraphQL API will automatically apply these policies based on the authenticated user.

GraphQL Playground

Access the GraphQL Playground in your project settings to explore your schema and test queries interactively.

Limitations

  • Mutations are in beta and may have limitations
  • Some PostgreSQL features may not be fully supported
  • Complex computed fields require custom functions

Best Practices

Only query the fields you need to reduce payload size:
query {
  countriesCollection {
    edges {
      node {
        id
        name
      }
    }
  }
}
Always paginate large datasets using cursor-based pagination.
Use GraphQL variables instead of string interpolation to prevent injection attacks.
Always enable Row Level Security on your tables to protect your data.

Next Steps

Queries

Learn about advanced querying

Mutations

Modify data with mutations