Step 5: Determine Services
Phase: plan
Context
You have the confirmed app_spec. Now determine which run402 services the app needs.
What to do
Based on the app spec, determine which run402 services are required. Every app uses at least database + static hosting.
run402 services checklist
| Service | When needed | run402 feature |
|---|---|---|
| Database | Always — every app stores data | Postgres via PostgREST API. Tables created via SQL migration endpoint. |
| REST API | Always — how the frontend reads/writes data | Auto-generated from database schema. CRUD + filters at /rest/v1/{table}. |
| Authentication | When app_spec.features.auth is true |
Email/password signup + login at /auth/v1/signup and /auth/v1/token. |
| Row-Level Security | When users should only see their own data, or when some data is public and some private | Expose manifest with policies: user_owns_rows, public_read_authenticated_write, public_read_write_UNRESTRICTED, custom. Tables are dark by default — they need an expose: true entry to appear in the REST API. |
| File Storage | When app_spec.features.file_uploads is true |
Content-addressed CDN. Browser uploads go through a deployed function that calls assets.put; server-side uploads use (await r.project(id)).assets.put(). Reads use GET /storage/v1/blob/:key, or the returned paste-and-go cdnUrl. |
| Serverless Functions | When server-side logic is needed that can't run in the browser (e.g., password hashing, matching algorithms, secret operations) | Node 22 Fetch functions: export default async (req) => Response. Deployed via unified deploy (functions.replace in the ReleaseSpec). Called from frontend at /functions/v1/{name} (API-key protected) or via same-origin web routes (routes.replace). |
| AI Image Generation | When the app creates images from text prompts (e.g., sticker makers, art generators) | Generate images via POST /generate-image/v1 ($0.03/image, x402-gated). SDK: r.ai.generateImage({ prompt, aspect }). Returns image bytes to upload to File Storage. |
| Static Hosting | Always — how the app is deployed | Unified deploy: (await r.project(id)).apply({ site: { replace }, subdomains: { set } }) (SDK 2.0+). HTTP wire is POST /apply/v1/plans + /commit. Returns a shareable URL. |
Database table planning
For each data type in app_spec.features.data_types, plan a database table. Consider:
- What columns does each table need?
- Which tables reference each other (foreign keys)?
- Which RLS template applies to each table?
- Does any table need seed data (e.g., default categories, sample content)?
Do NOT tell the user about tables, columns, or SQL. Just note this internally for the build.
Expected output
required_services— List of run402 services needed with details:{ "database": true, "tables": [ {"name": "todos", "columns": ["id", "task", "done", "user_id", "created_at"], "rls": "user_owns_rows"} ], "auth": true, "file_storage": false, "static_hosting": true, "seed_data": false }