App Recipes

Database, storage, email, auth and deploy in a few lines

Recipes for the backend an app needs: data, files, email, auth and shipping to a URL. Recursiv gives you all of it from one SDK, so you never wire up separate services. Each snippet assumes const r = new Recursiv(), which reads RECURSIV_API_KEY from the environment.

Provision a database

Call ensure on every deploy. It creates the Postgres database the first time and returns the existing one after that, so it is safe to run repeatedly.

1const { data: db } = await r.databases.ensure({
2 project_id: 'proj_123',
3 name: 'main',
4});
5
6console.log(db.name, db.status);

Query the database

Run SQL straight against a project’s database and read the rows back.

1const { data } = await r.databases.query({
2 project_id: 'proj_123',
3 sql: 'SELECT id, email FROM users ORDER BY created_at DESC LIMIT 10',
4});
5
6for (const row of data.rows) {
7 console.log(row.id, row.email);
8}

Upload a file

Storage uploads are two steps: get a presigned URL, then PUT the bytes directly to it. ensureBucket is idempotent.

1import fs from 'node:fs';
2
3await r.storage.ensureBucket({ project_id: 'proj_123', name: 'uploads' });
4
5const { data: { url } } = await r.storage.getUploadUrl({
6 project_id: 'proj_123',
7 bucket_name: 'uploads',
8 key: 'avatars/user_42.png',
9 content_type: 'image/png',
10});
11
12await fetch(url, {
13 method: 'PUT',
14 headers: { 'Content-Type': 'image/png' },
15 body: await fs.promises.readFile('./avatar.png'),
16});

Generate a presigned download URL to hand to a browser or client.

1const { data: { url } } = await r.storage.getDownloadUrl({
2 project_id: 'proj_123',
3 bucket_name: 'uploads',
4 key: 'avatars/user_42.png',
5});
6
7console.log('Download:', url); // time-limited, no credentials needed

Send an email

Send a transactional email. With a BYOK Resend key on the org, it sends from your own domain.

1const { data: result } = await r.email.send({
2 to: 'customer@example.com',
3 subject: 'Your order shipped',
4 html: '<h1>Order #1234</h1><p>On its way.</p>',
5});
6
7console.log(result.id, result.status);

Add email and password auth

Sign a user up and mint an API key for them in one call. Ideal for mobile, where cookies do not persist. Bind the key to the project so users become app members.

1const anon = new Recursiv({ anonymous: true });
2
3const { apiKey, user } = await anon.auth.signUpAndCreateKey(
4 { email: 'new@example.com', password: 'a-strong-passphrase-12', name: 'New User' },
5 { name: 'mobile-app', scopes: ['posts:read', 'posts:write'], projectId: 'proj_123' },
6);
7
8const userClient = new Recursiv({ apiKey });

Add passwordless (OTP) auth

Email a one-time code, then verify it to sign in. Auto-creates the user if they are new.

1const anon = new Recursiv({ anonymous: true });
2
3await anon.auth.sendOtp({ email: 'user@example.com' });
4
5// ...user enters the code from their inbox
6const { apiKey } = await anon.auth.verifyOtpAndCreateKey(
7 { email: 'user@example.com', otp: '123456' },
8 { name: 'web-session', scopes: ['posts:read'], projectId: 'proj_123' },
9);

Scaffold a new app from a template

Create a deployable Next.js or Expo repo server-side. No local gh, git or GitHub account required.

1const { data: repo } = await r.github.createFromTemplate({
2 template: 'nextjs', // or 'expo'
3 name: 'my-app',
4});
5
6console.log(repo.repo_url);

Deploy to a live URL

Link a repo to a project, then deploy. The deployment URL is returned once it is live.

1const { data: project } = await r.projects.create({
2 organization_id: 'org_123',
3 name: 'my-app',
4 repo_url: repo.repo_url,
5});
6
7const { data: deployment } = await r.projects.deploy(project.id, {
8 branch: 'main',
9 type: 'production',
10});
11
12console.log(deployment.status, deployment.deployment_url);

Tail deployment logs

Read build and runtime logs for a deployment to debug a failed ship.

1const { data: logs } = await r.projects.deploymentLogs(project.id, deployment.id);
2console.log(logs.logs);