Prerequisite: You should have installed
Docker and followed the
Development setup guide.
Overview
Tuturuuu utilizes Supabase for database management and authentication. This guide explains how to work efficiently with Supabase in the local development workflow.Basic Commands
Starting Supabase
To start a local Supabase instance tailored for Tuturuuu:apps/database/supabase/config.toml:
| Service | URL / Port |
|---|---|
| REST/Auth API | http://localhost:8001 |
| Postgres database | postgresql://postgres:postgres@localhost:8002/postgres |
| Studio (dashboard) | http://localhost:8003 |
| InBucket (test emails) | http://localhost:8004 |
Run
bun sb:status at any time to print the live URLs, keys, and the DB
connection string for your running instance. Prefer reading these values from
bun sb:status over hard-coding them, since they are the source of truth for
the current container.Stopping Supabase
To stop the local Supabase instance:Checking Status
To view the current URLs and status of your local Supabase instance:Development Workflow
Recommended Startup
When developing for Tuturuuu, you have several options to start your environment:-
Standard Approach: Start Next.js apps and Supabase separately.
-
Enhanced Development Experience: Use the
devxcommand for a streamlined setup.This command:- Stops any running Supabase instance and saves current data as backup
- Installs all dependencies
- Starts a new Supabase instance (using backed up data)
- Starts all Next.js apps in development mode
-
Fresh Database Setup: When switching branches with potential schema changes.
This command (
bun sb:stop && bun sb:start && bun sb:reset && bun dev):- Stops any running Supabase instance (without backup)
- Starts a new Supabase instance
- Resets the database to use the latest schema and seed data
- Starts all Next.js apps in development mode
bun devrs:web,bun devrs:tasks) that reset Supabase and then start only that app’s dev server.
Syncing With Schema Changes
If you’re keeping your Next.js server running but need to reset your database to match the current branch’s schema:Database Schema Management
Making Schema Changes
There are two approaches to modifying the database schema:1. Using the Supabase UI
- Navigate to your local Supabase Studio at http://localhost:8003
- Make your changes through the UI
- Generate a migration file:
This creates a migration file based on the differences between your current schema and the previous state.
2. Creating Manual Migrations
For more control, you can create empty migration files and populate them manually:apps/database/supabase/migrations
using the provided migration name. If you omit the name, the helper uses
new_migration.
Applying Migrations
After creating a migration, apply it to your local database:This same process is used to keep our production database up-to-date with the
schema defined in the
production branch.Generating TypeScript Types
After schema changes, regenerate the TypeScript types to keep your code in sync with the database schema:public, private, and storage schemas. Keep
server-owned/private tables out of Supabase REST exposure, but still regenerate
types after migrations so server-only helpers and storage paths can import
accurate generated row shapes.
Alternatively, you can use the shorthand:
This step is automatically performed when running
bun sb:reset, making it
useful when catching up with a new branch’s schema.Using Generated TypeScript Types
The Supabase-generated TypeScript types are available atpackages/types/src/supabase.ts. These types are accessible to all apps that have the @tuturuuu/types package installed.
You can use these types to ensure type safety when working with Supabase data:
With supabase-js v2, table types flow from the client itself rather than a
generic on .from(). The Tuturuuu Supabase helpers in @tuturuuu/supabase are
already typed with the generated Database type, so queries are type-safe out of
the box. Reach for the Database type directly when you need a specific row
shape:
Short-hand Type Access
For more convenient access to common table types, Tuturuuu also provides short-hand type definitions inpackages/types/src/db.ts. These are easier to use and remember than the full database type paths:
db.ts for tables you frequently work with. This is especially useful for tables that have complex structures or need additional client-side properties.
This ensures that your code correctly interacts with the database schema, reducing runtime errors and improving development experience.
Migration Files
All migration files are stored inapps/database/supabase/migrations. These files:
- Contain SQL commands that create and modify the database schema
- Are executed in order based on the timestamp prefix in their filenames
- Include descriptive names after the timestamp to help developers understand their purpose
Local Authentication
A local mail server (InBucket) is automatically set up by Supabase to handle authentication emails. You can access it at http://localhost:8004. With InBucket, you can:- Receive all authentication emails sent by your local Supabase instance
- Test any email combination without needing actual mail delivery
- View password reset links, confirmation emails, and other authentication flows
- Troubleshoot email templates and content
Playwright E2E Safety
Web E2E runs must use the local Supabase stack. For local debugging and patch iteration, run the web app and Playwright on the native machine so code changes are picked up quickly and server logs stay visible. Avoidbun test:e2e, bun --cwd apps/web test:e2e, and
bun test:e2e:web:docker as the first local debugging path. Those commands
route through scripts/run-web-e2e-docker.js, which writes
tmp/e2e/web.env, resets local Supabase, builds and runs the production-style
Docker web stack, and then runs Playwright. Reserve that Dockerized path for
explicit CI-parity checks or production-Docker runtime bugs.
For the native workflow, start or reset local Supabase with the database package
scripts, start apps/web natively with the local E2E environment, and run the
focused Playwright spec directly from apps/web. Native web server Supabase
URLs should resolve to http://127.0.0.1:8001; host.docker.internal is only
for the Dockerized web stack.
Do not point E2E at a cloud Supabase project. The Playwright global setup fails
fast if BASE_URL is not local or if NEXT_PUBLIC_SUPABASE_URL,
SUPABASE_SERVER_URL, or SUPABASE_URL resolves outside the local Supabase
origins on port 8001.
Further Information
For more details about Supabase CLI usage, refer to the Supabase CLI documentation.Row Level Security (RLS)
Row Level Security (RLS) is a powerful Postgres feature that allows you to control access to rows in a database table based on the user making the request. In Tuturuuu, we use RLS extensively to ensure data security.Enabling RLS
RLS should be enabled on all tables in exposed schemas (likepublic). When creating tables through the Supabase UI, RLS is enabled by default. For tables created using SQL, you need to explicitly enable RLS:
Creating RLS Policies
Policies define the conditions under which users can access or modify data. Here are some common patterns used in Tuturuuu:Organization-based Access
In Tuturuuu, most resources belong to an organization (workspace). Here’s how to create policies for organization-based access:Role-based Access
For more granular control based on user roles:Performance Optimization for RLS
For better performance in your RLS policies:-
Wrap function calls in subqueries:
-
Use security definer functions for complex access logic:
-
Add explicit filters in your queries even when you have RLS:
Testing RLS Policies
Tuturuuu ships a large suite of database tests inapps/database/supabase/tests/. These are pgTAP test files
(each one wraps its assertions in a transaction and rolls back), and they run
against the local Supabase instance through the Supabase CLI.
To run the database test suite locally:
-
Make sure a local Supabase instance is running (
bun sb:start). -
Add or edit a
*.sqltest file inapps/database/supabase/tests/. -
Run the tests through the database workspace’s Supabase CLI wrapper:
run-supabase.jsresolves the pinned local Supabase binary and invokessupabase test db, which executes everytests/*.sqlfile against the local database. There is no dedicatedbun sb:testalias — the test runner is the Supabase CLI itself.
plan(),
is(), and throws_ok() to assert behavior. A trimmed example:
Look through the existing files in
apps/database/supabase/tests/ (for
example user-profile-rls.sql and the private-schema-* suites) to follow the
established pgTAP patterns for impersonating roles and asserting RLS behavior.Database Triggers
Triggers in Postgres allow you to automatically execute a function when a specified database event occurs (INSERT, UPDATE, DELETE). In Tuturuuu, we use triggers for various purposes like:- Maintaining audit logs
- Syncing data between tables
- Enforcing complex business rules
Creating Triggers
Here’s how to create a trigger in your Tuturuuu development workflow:- First, create a trigger function:
- Then, create the trigger:
Common Triggers in Tuturuuu
Audit Logging
Automated Timestamps
Testing Triggers
You can test triggers by running SQL commands in the local Supabase instance and verifying the results:Seeding Your Database
Database seeding is the process of populating your database with initial data. In Tuturuuu, we use seeding to:- Create test users and workspaces for local development
- Initialize lookup tables with standard values
- Ensure a consistent starting point for all developers
Seed Files Location
In Tuturuuu, seed files are stored inapps/database/supabase/seed.sql. This file is automatically executed when you run bun sb:reset or start a fresh Supabase instance.
Real Examples from Tuturuuu’s Seed File
Let’s look at some real examples from Tuturuuu’s seed.sql file:1. Authentication Users
The seed file creates five default test users with pre-set passwords:password123, making it easy to log in for testing.
2. Workspaces
The seed creates several workspaces for testing different scenarios:3. Workspace Members and Roles
The seed also sets up relationships between users and workspaces with different roles:4. Workspace Features Configuration
The seed file configures workspace features using secrets:5. Domain-specific Data
The seed includes domain-specific data for different workspace types. For example, user group metric data:user_group_metrics.is_weighted = false keeps a metric visible for entry and category views without counting it toward report totals or averages. Metric categories live in user_group_metric_categories and are linked through user_group_metric_category_links.
When renaming or recreating these metric tables, keep authenticated CRUD grants and service-role grants in the same migration as the table change. The group indicators dashboard reads the renamed tables through protected API/server paths after workspace permission checks, and missing grants can surface as permission denied for table user_group_metric_categories.
When code uses createAdminClient() for these protected reads, name the local client sbAdmin. Do not name a service-role client supabase; reserve that name for request-scoped or user-scoped clients so route authorization is easy to audit.
Creating Seed Data
Here’s how to create and modify seed data:- Edit the
apps/database/supabase/seed.sqlfile - Add SQL statements to insert your data
- Run
bun sb:resetto apply the seed data
Creating a Custom Seed File
Sometimes you might want to create a custom seed file for specific testing scenarios:- Create a new SQL file in the
apps/database/supabasedirectory - Add your custom seed data
- Run it with the Supabase CLI:
Exporting Current Data as Seed
You can also export your current database data to use as seed data:Recommended Seeding Workflow
For Tuturuuu development, we recommend:- Start with a fresh database:
bun sb:reset - Make changes through the UI or your app
- When you’re satisfied, export the data:
bun supabase db dump --data-only > apps/database/supabase/new_seed.sql - Edit the generated SQL to keep only what you need
- Update the main
seed.sqlfile with your changes - Test by running
bun sb:resetagain
AI Integration with Vercel AI SDK
Tuturuuu uses Vercel’s AI SDK for its AI features, utilizing structured data generation capabilities that integrate with Supabase. This section covers how to work with AI features in the development workflow.Overview of AI SDK in Tuturuuu
The AI SDK standardizes integrating various AI models across supported providers into Tuturuuu applications. It enables structured data generation, tool calling, and streaming responses to create rich AI-powered features. The main libraries used are:ai- Core Vercel AI SDK package@ai-sdk/google- Provider-specific integration for Google models@tuturuuu/supabase- Supabase client with Tuturuuu-specific utilities
Generating Structured Data
Tuturuuu uses the AI SDK’s structured data generation capabilities to create typed responses from AI models. This approach ensures type safety and consistent data structures for features like:- Flashcards generation
- Quiz generation
- Learning plans
- Task management
Example: Flashcard Generation
The structured data pattern used in Tuturuuu follows this workflow:- Define a schema using Zod
- Connect to Supabase for authentication and workspace validation
- Generate structured data using the AI SDK
- Stream the response to the client
Available Models
Tuturuuu supports multiple AI models through Vercel AI SDK. You can define which models are available in your application by updating themodels.ts file in the packages/ai directory:
Creating Custom Schema Types
To create new structured data types for AI generation, add your schema definition to thepackages/ai/src/object/types.ts file:
Integration with Supabase
Tuturuuu’s AI features leverage Supabase for:- Authentication - Validating users before making AI requests
- Authorization - Checking workspace permissions via
workspace_secrets - Feature Flags - Using
workspace_secretsto enable/disable AI features per workspace - Storage - Storing AI-generated content for later use
workspace_secrets table:
Testing AI Features Locally
When testing AI features in your local environment:-
Ensure you have the required API keys set in your
.env.localfile: -
Verify the workspace has the necessary feature flags enabled in your local database
-
Use the AI-enabled accounts from the seed data (
local@tuturuuu.com) as they often have additional permissions
Error Handling
When integrating AI features, implement proper error handling to account for:- Missing API keys
- Model unavailability
- Invalid user input
- Exceeded token limits