Backend & SDK
Every app Codeezly builds ships with a typed client SDK already wired in. There is no install step and no setup for the core pillars — import what you need and call it. All calls run same-origin to your app's own backend at /api/apps/[id]/*.
The SDK lives at @/lib/codeezly (the file src/lib/codeezly.ts is injected into your project automatically). It exposes seven pillars: db for data, auth for user accounts, ai for key-free AI, payments for Stripe, email for transactional mail, storage for files, and intl for locale-aware formatting. Just import the ones you use:
import { db, auth, ai, payments, email, storage, intl } from '@/lib/codeezly'Working with data
Each app gets its own multi-tenant PostgreSQL database. You work with it through named collections — no schema migrations to write by hand.
- Get a typed handle with
db.collection<Todo>('todos'), then callcreate,list,get,update, andremove. - Rows are multi-tenant: pass a visibility of 'public' or 'private' on create, and pass mine: true when listing to scope results to the signed-in end user.
- Use
todos.search(...)to search and filter server-side across the whole dataset — not just the rows you happened to load on the client.
import { db } from '@/lib/codeezly'
type Todo = { id: string; title: string; done: boolean }
const todos = db.collection<Todo>('todos')
// Create a row (defaults to private — visible only to its owner)
const created = await todos.create(
{ title: 'Buy milk', done: false },
{ visibility: 'private' },
)
// List rows — pass mine: true to scope to the signed-in end user
const mine = await todos.list({ limit: 20, offset: 0, mine: true })
// Read, update, delete by id
const one = await todos.get(created.id)
await todos.update(created.id, { done: true })
await todos.remove(created.id)
// Server-side search/filter across the WHOLE dataset (not client-side)
const matches = await todos.search({ title: 'milk', done: false })User accounts
The auth pillar manages end-user accounts inside your built app — the people who sign up and use what you ship, separate from your Codeezly account.
auth.signUpwith an email and password registers a new end user.auth.signInwith an email and password signs an existing user in.auth.signOut()— end the current session.auth.currentUser()— read the signed-in user, or null if no one is signed in.
Pair auth with a mine: true list query and private visibility to give each user their own data.
Built-in AI
This is the headline: your app needs no API key to use AI. The SDK talks to Codeezly's AI gateway for you.
ai.config()reports availability, the tiers on offer, and billing details — so you can show or hide AI features at runtime.ai.chattakes your messages, plus an optional system prompt and model, and returns the completion text. The model argument is optional.ai.complete(prompt)is a convenience helper for a single prompt.
import { ai } from '@/lib/codeezly'
// Inspect what's available — no API key required in app code
const cfg = await ai.config()
// -> { available: boolean, tiers: string[], enabled: boolean, billing: string }
if (cfg.available) {
const { text } = await ai.chat({
system: 'You are a concise assistant.',
messages: [{ role: 'user', content: 'Summarize this todo list in one line.' }],
// model is optional — Codeezly picks a sensible default
})
console.log(text)
}
// Single-prompt convenience helper
const { text } = await ai.complete('Write a friendly welcome headline.')How AI is billed: calls are charged against the app owner's Codeezly credit balance, so the people using your app never need a key of their own.
- AI usage is billed to the app owner's Codeezly credit balance — there is nothing for end users to configure.
- You stay in control: each app has a per-app opt-out toggle (on by default) and a monthly spend cap, both under Publish → AI.
- Prefer your own provider? Set
ANTHROPIC_API_KEY,OPENAI_API_KEY, orGOOGLE_API_KEYin project Variables and that provider is billed directly — Codeezly stops metering the usage.
Payments
Take real Stripe payments. Charges go straight to the app owner's own Stripe account.
payments.config()reports whether payments are configured, the publishable key, and the currency.payments.checkout(...)starts a Stripe Checkout session and returns a URL to redirect to.
To enable payments, the app owner sets STRIPE_SECRET_KEY (and optionally STRIPE_PUBLISHABLE_KEY and STRIPE_WEBHOOK_SECRET) in project Variables.
import { payments } from '@/lib/codeezly'
const cfg = await payments.config()
// -> { configured: boolean, publishableKey: string, currency: string }
if (cfg.configured) {
// Charges go to the app OWNER's own Stripe account
const session = await payments.checkout({
lineItems: [{ name: 'Pro plan', amount: 1900, quantity: 1 }],
currency: cfg.currency,
successUrl: window.location.origin + '/thanks',
cancelUrl: window.location.href,
})
window.location.href = session.url
}Send real transactional email from your app.
email.config()reports whether email is configured and which provider is active.email.send(...)sends a message with a recipient, subject, and an HTML or text body.
The app owner enables email by adding credentials in project Variables: RESEND_API_KEY (plus optional EMAIL_FROM), or SENDGRID_API_KEY, or SMTP_HOST / SMTP_USER / SMTP_PASS.
import { email } from '@/lib/codeezly'
const cfg = await email.config()
// -> { configured: boolean, provider: string }
if (cfg.configured) {
await email.send({
to: 'customer@example.com',
subject: 'Welcome aboard!',
html: '<h1>Thanks for signing up</h1><p>We are glad you are here.</p>',
text: 'Thanks for signing up. We are glad you are here.',
})
}File storage
Store files and media — uploads, images, attachments — without standing up your own bucket.
storage.config()reports whether storage is available and the maximum file size.storage.upload(file)uploads a file and returns its stored path.storage.remove(path)deletes a stored file.
Locale-aware formatting
Format numbers, currency, and dates for any locale inside the built app.
intl.setLocale(code)— set the active locale.intl.number(n)— format a number for the current locale.intl.currency(n, code)— format an amount in a given currency.intl.date(d)— format a date.intl.relativeTime(d)— format a relative time like "3 days ago".
Right-to-left layouts (for example Arabic and Urdu) are handled automatically.
Setting Variables
Variables is the workspace panel where the app owner stores encrypted, per-project secrets and environment variables. This is where keys like STRIPE_SECRET_KEY, RESEND_API_KEY, and ANTHROPIC_API_KEY live.
- Open your project, then open the Variables panel in the workspace.
- Add a variable with its name (for example STRIPE_SECRET_KEY) and its value.
- Save. The SDK picks the new value up automatically — no code changes needed.
Values are encrypted at rest and scoped to your project, so they are never exposed in your app's client code.