Generating Python Types
How to generate Python types for your API and Supabase libraries.
Supabase APIs are generated from your database, which means that we can use database introspection to generate type-safe API definitions.
Generating types using Supabase CLI
The Supabase CLI is a single binary Go application that provides everything you need to setup a local development environment.
You can install the CLI via npm or other supported package managers. The minimum required version of the CLI is v2.66.0.
1npm i supabase --save-devLogin with your Personal Access Token:
1npx supabase loginBefore generating types, ensure you initialize your Supabase project:
1npx supabase initGenerate types for your project to produce the database_types.py file:
1npx supabase gen types --lang=python --project-id "$PROJECT_REF" --schema public > database.types.pyor in case of local development:
1npx supabase gen types --lang=python --local > database_types.pyThese types are generated from your database schema. Given a table public.movies, the generated types will look like:
1create table public.movies (2 id bigint generated always as identity primary key,3 name text not null,4 data jsonb null5);1class PublicMovies(BaseModel):2 data: Optional[Json[Any]] = Field(alias="data")3 id: int = Field(alias="id")4 name: str = Field(alias="name")56class PublicMoviesInsert(TypedDict):7 data: NotRequired[Annotated[Json[Any], Field(alias="data")]]8 id: NotRequired[Annotated[int, Field(alias="id")]]9 name: Annotated[str, Field(alias="name")]1011class PublicMoviesUpdate(TypedDict):12 data: NotRequired[Annotated[Json[Any], Field(alias="data")]]13 id: NotRequired[Annotated[int, Field(alias="id")]]14 name: NotRequired[Annotated[str, Field(alias="name")]]Types for select, insert and update
The PublicMovies class is used to parse SELECT results from the movies table, while PublicMoviesInsert and PublicMoviesUpdate are used to format and provide completion for arguments for insert and update respectively.
1from .database_types import PublicMovies, PublicMoviesInsert, PublicMoviesUpdate2from supabase import create_client34client = create_client("YOUR_SUPABASE_URL", "YOUR_SUPABASE_KEY")5movies = client.table("movies")67movies = supabase.table("movies")89# Select10selected = [PublicMovies(m) for m in movies.select("*").execute().data]1112# Insert13inserted = [PublicMovies(m) for m in movies.insert(PublicMoviesInsert(name="foo", data="bar")) \14 .execute().data]1516# Update17updated = [PublicMovies(m) for m in movies.update(PublicMoviesUpdate(name="bar")) \18 .eq("id", 5) \19 .execute().data]Update types automatically with GitHub Actions
One way to keep your type definitions in sync with your database is to set up a GitHub action that runs on a schedule.
Add the following script to your package.json to run it using npm run update-types
1"update-types": "npx supabase gen types --lang=python --project-id \"$PROJECT_REF\" > database_types.py"Create a file .github/workflows/update-types.yml with the following snippet to define the action along with the environment variables. This script will commit new type changes to your repo every night.
1name: Update database types23on:4 schedule:5 # sets the action to run daily. You can modify this to run the action more or less frequently6 - cron: '0 0 * * *'78jobs:9 update:10 runs-on: ubuntu-latest11 permissions:12 contents: write13 env:14 SUPABASE_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}15 PROJECT_REF: <your-project-id>16 steps:17 - uses: actions/checkout@v418 with:19 persist-credentials: false20 fetch-depth: 021 - uses: actions/setup-node@v422 with:23 node-version: 2224 - run: npm run update-types25 - name: check for file changes26 id: git_status27 run: |28 echo "status=$(git status -s)" >> $GITHUB_OUTPUT29 - name: Commit files30 if: ${{contains(steps.git_status.outputs.status, ' ')}}31 run: |32 git add database_types.py33 git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"34 git config --local user.name "github-actions[bot]"35 git commit -m "Update database types" -a36 - name: Push changes37 if: ${{contains(steps.git_status.outputs.status, ' ')}}38 uses: ad-m/github-push-action@master39 with:40 github_token: ${{ secrets.GITHUB_TOKEN }}41 branch: ${{ github.ref }}