Skip to main content

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 call create, list, get, update, and remove.
  • 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.signUp with an email and password registers a new end user.
  • auth.signIn with 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.chat takes 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, or GOOGLE_API_KEY in 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
}

Email

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.

  1. Open your project, then open the Variables panel in the workspace.
  2. Add a variable with its name (for example STRIPE_SECRET_KEY) and its value.
  3. 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.