Database
Teta uses Supabase as the database layer for your apps. Supabase provides a full PostgreSQL database with real-time subscriptions, row-level security, and a REST API. You can create tables, query data, and manage your schema entirely through the AI chat or by writing code directly.
Supabase as the Database Layer
Every Teta project comes with a Supabase instance. Supabase gives you:
- PostgreSQL - A powerful, open-source relational database with support for JSON, full-text search, and advanced queries.
- REST API - Automatically generated API endpoints for all your tables. No need to write backend code for basic CRUD operations.
- Real-time - Subscribe to database changes and receive updates instantly via WebSocket.
- Row-Level Security - Fine-grained access control at the database level, so you can define who can read, insert, update, or delete specific rows.
- Dashboard - A web interface to browse your data, run SQL, and manage your schema.
Creating Tables via AI Chat
The fastest way to set up your database schema is to describe it in chat. The AI creates tables, columns, indexes, and relationships for you.
Example prompts:
- "Create a table called
postswith columns: id (uuid, primary key), title (text), content (text), author_id (uuid, references auth.users), published (boolean, default false), created_at (timestamptz, default now)." - "I need a database for a task management app. Create tables for projects, tasks, and team members with appropriate relationships."
- "Add a
commentstable that references thepoststable with a foreign key onpost_id."
The AI generates the SQL, runs it against your Supabase database, and confirms the schema was created. It also sets up row-level security policies when appropriate.
CRUD Operations
Once your tables exist, the AI can generate SvelteKit code to create, read, update, and delete data. Supabase provides a JavaScript client that makes queries straightforward.
Reading data:
// src/routes/posts/+page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals: { supabase } }) => {
const { data: posts, error } = await supabase
.from('posts')
.select('*')
.eq('published', true)
.order('created_at', { ascending: false });
return { posts: posts ?? [] };
};
Inserting data:
// src/routes/posts/new/+page.server.ts
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request, locals: { supabase } }) => {
const formData = await request.formData();
const title = formData.get('title') as string;
const content = formData.get('content') as string;
const { error } = await supabase
.from('posts')
.insert({ title, content, published: false });
if (error) return { error: error.message };
return { success: true };
}
};
Updating data:
const { error } = await supabase
.from('posts')
.update({ published: true })
.eq('id', postId);
Deleting data:
const { error } = await supabase
.from('posts')
.delete()
.eq('id', postId);
You do not need to write these queries by hand. Ask the AI to "Add a form to create new posts" or "Add a delete button to each post card," and it will generate the complete implementation.
Real-Time Subscriptions
Supabase real-time lets your app react to database changes as they happen. This is useful for chat apps, live dashboards, collaborative features, and any scenario where multiple users interact with the same data.
// Subscribe to new comments on a post
const channel = supabase
.channel('comments')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'comments',
filter: `post_id=eq.${postId}`
},
(payload) => {
comments = [...comments, payload.new];
}
)
.subscribe();
Ask the AI to "Add real-time updates to the comments section" and it will set up the subscription, handle the incoming data, and update the UI automatically.
Row-Level Security (RLS)
Row-level security is a PostgreSQL feature that controls which rows a user can access. With RLS enabled, every query is filtered based on policies you define. This is critical for multi-user apps where users should only see their own data.
Common RLS patterns:
- Users can only read their own data:
auth.uid() = user_id - Anyone can read, only authors can edit: SELECT policy for all, UPDATE/DELETE policy checking
auth.uid() = author_id - Public read, authenticated write: SELECT policy with no restrictions, INSERT policy requiring
auth.role() = 'authenticated'
The AI sets up RLS policies when it creates tables, based on the context of your app. You can also ask explicitly: "Add row-level security so users can only see their own tasks."
Important: RLS is enabled by default on new Supabase tables. Without policies, no data is accessible. The AI handles this, but be aware of it if you create tables manually.
Environment Variables
Teta automatically configures the Supabase connection for your project. The following environment variables are set:
PUBLIC_SUPABASE_URL- Your Supabase project URL.PUBLIC_SUPABASE_ANON_KEY- The public anonymous key for client-side access.
These are available in your SvelteKit code and are safe to expose to the client. The anon key only provides access allowed by your RLS policies, not unrestricted database access.
For server-side operations that need elevated permissions (bypassing RLS), use the service role key. This should only be used in server-side code and never exposed to the client.
Querying from Server and Client
SvelteKit gives you two contexts for database queries:
Server-side (+page.server.ts, +server.ts, hooks.server.ts):
- Runs on the server, not visible to the client
- Can use the service role key for admin operations
- Best for data loading, form handling, and sensitive operations
Client-side (+page.svelte, +layout.svelte):
- Runs in the browser
- Uses the anon key, restricted by RLS policies
- Best for real-time subscriptions and interactive updates
The AI chooses the appropriate context based on what you are building. Data loading goes in server files, real-time features go in client files.
Example: Building a Comments System
Here is a practical example of how everything comes together. Ask the AI: "Add a comments system to my blog. Users should be able to post comments on any article, and comments should appear in real time."
The AI will:
- Create the table: A
commentstable withid,post_id,user_id,content,created_at. - Set up RLS: Anyone can read comments, only authenticated users can create them, users can only delete their own comments.
- Build the server loader: Fetch comments for the current post in
+page.server.ts. - Create the comment form: A form component that submits new comments via a SvelteKit form action.
- Add real-time: Subscribe to new comments so they appear for all viewers without refreshing.
- Style the UI: A comment list with avatars, timestamps, and a clean layout.
This entire feature can be built in a few chat messages, with the AI handling the database schema, security, backend logic, and frontend UI.
FAQ
Can I use the Supabase dashboard directly?
Yes. Your Supabase project has a full web dashboard where you can browse tables, run SQL queries, manage users, and configure settings. Access it through the Supabase website using your project credentials.
Is my data backed up?
Supabase automatically manages PostgreSQL backups. Daily backups are included with your plan. For point-in-time recovery and more frequent backups, check your Supabase plan details.
Can I import existing data?
Yes. You can import data via CSV through the Supabase dashboard, use SQL INSERT statements in the SQL editor, or ask the AI to write a migration script that seeds your database with initial data.
What happens if I exceed the database limits?
Supabase has usage tiers based on your plan. If you approach limits on storage, API requests, or connections, Supabase will notify you. You can upgrade your plan or optimize your queries. The AI can help optimize slow queries if you describe the issue.
Can I use raw SQL instead of the Supabase client?
Yes. Supabase supports raw SQL through the rpc function and the SQL editor in the dashboard. You can also ask the AI to write raw SQL queries when the Supabase client syntax is not sufficient for complex operations like joins, aggregations, or window functions.