Most backend-as-a-service platforms stop at data storage. You get a database and an API, but the moment you need server-side logic — sending an email when a form is submitted, processing a payment, transforming data before it's saved — you're back to building your own backend.
Centrali is different. You get data storage, serverless compute functions, webhooks, triggers, and workflow automations — all from the same platform your frontend already talks to. No separate infrastructure. No AWS Lambda. No managing servers.
This guide walks through building a real Next.js app from scratch, deploying it to Vercel, and using Centrali for everything backend.
What You'll Build
A contact form app that:
- Stores submissions in a Centrali collection
- Runs a compute function on each submission (e.g., validate data, enrich it, send a notification)
- Triggers a webhook to notify your Slack channel
- Deploys to Vercel with zero server management
Step 1: Scaffold Your App
npx @centrali-io/create-centrali-app my-app --template=nextjs
cd my-app
npm installThis gives you a Next.js 15 app with TypeScript, TailwindCSS, and the Centrali SDK pre-configured. Two client files are already set up:
src/lib/centrali.ts— a publishable key client for browser-safe operations (listing records, reading collections) and a service account client for server-side operations (creating records, calling functions)
Step 2: Set Up Your Environment
Run the env setup command:
npx @centrali-io/create-centrali-app envIt detects your Next.js template, prompts for your workspace slug, publishable key, and service account credentials, then writes a .env.local file. You can also output Vercel or Netlify CLI commands directly:
npx @centrali-io/create-centrali-app env --format=vercelWhere to get credentials:
- Publishable key: Console > ACCESS > Publishable Keys > Create Key. Select which collections to expose.
- Service account: Console > ACCESS > Service Accounts. Add it to the
workspace_developersgroup.
Step 3: Model Your Data
In the Centrali console, create a contact-submissions collection with these properties:
| Property | Type |
|---|---|
| name | text |
| text | |
| message | text |
| status | text (default: "new") |
That's your schema. No migrations, no ORM configuration, no database provisioning. The SDK discovers the schema automatically.
Step 4: Build the Form
Your scaffolded app already has a working CRUD flow. For a contact form, create a server action that writes to Centrali:
// src/app/actions.ts
'use server'
import { createCentraliServerClient } from '@/lib/centrali'
export async function submitContact(formData: FormData) {
const centrali = createCentraliServerClient()
const record = await centrali.records.create('contact-submissions', {
name: formData.get('name') as string,
email: formData.get('email') as string,
message: formData.get('message') as string,
})
return { success: true, id: record.id }
}The service account client handles authentication. The publishable key client can list submissions on the frontend. Each credential type has exactly the access it needs.
Step 5: Add Backend Logic with Compute Functions
Here's where Centrali goes beyond a typical BaaS. Instead of setting up a separate Lambda or Cloud Function, write a compute function directly in the Centrali console:
Console > COMPUTE > Functions > Create Function
// Validate and enrich contact submissions
export default async function handler({ record, centrali }) {
const { email, name } = record.data
// Validate email
if (!email || !email.includes('@')) {
await centrali.records.update('contact-submissions', record.id, {
status: 'invalid'
})
return { status: 'rejected', reason: 'invalid email' }
}
// Mark as processed
await centrali.records.update('contact-submissions', record.id, {
status: 'processed'
})
return { status: 'processed', name, email }
}Then create a trigger that fires this function whenever a record is created in the contact-submissions collection. Your function runs automatically — no cron jobs, no queue infrastructure, no deployment pipeline.
Step 6: Add a Webhook for Notifications
Want a Slack notification for every new submission? Add a webhook:
Console > COMPUTE > Webhooks > Create Webhook
- Event:
record.created - Collection:
contact-submissions - URL: Your Slack incoming webhook URL
Every time a contact form is submitted, Centrali sends a POST to Slack with the record data. No code needed.
Step 7: Chain It Together with Automations
For more complex flows — like "validate the submission, then if valid, send a welcome email, then update a CRM" — use automations (workflow orchestration):
Console > COMPUTE > Automations > Create Automation
Build a multi-step workflow visually:
- Trigger: record.created on contact-submissions
- Step 1: Run validation function
- Step 2: If status is "processed", run email function
- Step 3: Call external CRM API via HTTP step
Each step can use the output of the previous step. Failed steps can retry, skip, or halt the workflow. You get a full execution log for every run.
Step 8: Deploy to Vercel
Your app is ready. Push to GitHub and deploy:
git init && git add -A && git commit -m "Initial commit"
gh repo create my-app --public --pushThen import in Vercel:
- Go to vercel.com/new
- Import your repository
- Add environment variables (the
envcommand gave you these):
| Variable | Value |
|---|---|
NEXT_PUBLIC_CENTRALI_API_URL | https://centrali.io |
NEXT_PUBLIC_CENTRALI_WORKSPACE | Your workspace slug |
NEXT_PUBLIC_CENTRALI_PK | Your publishable key |
CENTRALI_API_URL | https://centrali.io |
CENTRALI_WORKSPACE | Your workspace slug |
CENTRALI_CLIENT_ID | Your service account ID |
CENTRALI_CLIENT_SECRET | Your service account secret |
- Deploy
Your app is live. The frontend talks to Centrali via publishable key (safe for browsers). Server actions use the service account. Compute functions, triggers, webhooks, and automations all run on Centrali — zero infrastructure on your end.
Prefer Netlify?
Each scaffolded app includes a DEPLOY.md with step-by-step instructions for both Vercel and Netlify. The steps are nearly identical — just different env var commands.
What You Didn't Have to Build
- No database server
- No ORM or migration scripts
- No Lambda functions or serverless framework
- No queue system for async processing
- No webhook infrastructure
- No workflow engine
- No auth server (publishable keys handle frontend, service accounts handle server-side)
Your Next.js app is a thin frontend layer. All the backend complexity — data, compute, events, workflows — lives in Centrali.
Next Steps
- SDK Documentation — full API reference for records, collections, triggers, and more
- Writing Functions — deep dive into compute function patterns
- Automations — build multi-step workflows with loops and conditions
- Search — add full-text search to your app