AI
This page covers how each AI feature works.
NativeExpress includes three AI features: a streaming chat, an image generation studio, and an object scanner. All three talk to OpenRouter through Supabase Edge Functions, using the Vercel AI SDK .
Prerequisites
Complete the Supabase project setup, set your edge function secrets, and deploy the functions.
None of the AI features work until OPENROUTER_API_KEY is set as a Supabase function secret. If a message sends and nothing comes back, check that first.
How it works
Every AI feature is built the same way:
The provider key is kept only in Supabase function secrets, never in your app bundle or on the device. The client sends a Supabase access token, and the function derives the user from it rather than trusting a user id in the request body.
| Feature | Function | Client entry point |
|---|---|---|
| Chat | supabase/functions/chat | src/lib/ai/chatTransport.ts |
| Image generation | supabase/functions/generate | src/lib/ai/generateImage.ts |
| Scan | supabase/functions/identify | src/app/(protected)/(tabs)/scan.tsx |
Where data is stored
| Table | Holds |
|---|---|
threads | One row per conversation: owner, model, title, updated_at |
messages | One row per message: thread_id, role, and parts as JSON |
generations | One row per generated image: prompt, model, style, aspect ratio, image_path |
scans | One row per scan: image_path and the structured result JSON |
All four have owner-scoped row-level security: a user can only read and write their own rows.
Images are stored in a private Supabase Storage bucket called chat-media, under {userId}/…, and its policies scope each signed-in user to objects under their own prefix. Rows store the stable object path rather than a signed URL, because signed URLs expire, and the app signs the path on demand for display.
Configuration
config.js and the edge functions read the same file, supabase/functions/_utils/ai.config.json, so the app and the server can’t disagree about a model, a style preset, an aspect ratio or the free-message allowance:
ai: require('./supabase/functions/_utils/ai.config.json'),Editing that file is one change, but a running edge function only sees it after a redeploy. See Models for what changes where.