noboil

Introduction

One schema. Typed backend. Auto forms. Zero boilerplate.

What is noboil?

noboil is a fullstack framework that generates your entire backend from a Zod schema. Define your tables once — get CRUD, real-time subscriptions, file uploads, typesafe forms, multi-tenancy, and more. All type-safe, all zero-config.

Pick a database when you scaffold (Convex or SpacetimeDB), then write code against the noboil API. The docs show one snippet per concept; the Convex vs SpacetimeDB page lists the small set of places where the underlying database constraints surface.

From zero to CRUD in 60 seconds

bunx noboil@latest init my-app
cd my-app

Pick your database, choose whether to include demo apps, and you have a working app with auth, typed CRUD, real-time subscriptions, and file uploads.

Define a schema, get an API

import { schema, file, files } from 'noboil/convex/schema'
import { array, boolean, object, string, enum as zenum } from 'zod/v4'

export const s = schema({
  owned: {
    blog: object({
      title: string().min(1, 'Required'),
      content: string().min(3),
      category: zenum(['tech', 'life', 'tutorial']),
      published: boolean(),
      coverImage: file().nullable().optional(),
      tags: array(string()).max(5).optional()
    })
  }
})
import { noboil } from 'noboil/convex/server'
// ...
export const api = noboil({ ...config, tables: ({ table }) => ({
  blog: table(s.blog, { rateLimit: { max: 10, window: 60_000 }, search: 'content' })
}) })

That's it. Five endpoints (create, update, rm, list, read) with auth, ownership, Zod validation, file upload, cursor pagination, rate limiting, and conflict detection — all generated. create, update, and rm accept single or bulk input (up to 100 items).

Add a table in one command

noboil convex add todo --fields="title:string,done:boolean"
# or
noboil stdb add todo --fields="title:string,done:boolean"

This generates a Zod schema, CRUD endpoints, and a React page component. Every field name is type-checked — misspell title and TypeScript catches it.

Table types: owned (user-scoped), org (multi-tenant), singleton (one per user), cache (external API), child (nested under parent).

Ship with prebuilt components or go headless

Prebuilt UI — 16 typed form fields, multi-step wizards, editor management, error boundaries, and access control guards. Pass a Zod schema and get a working form:

import { Form, useFormMutation } from 'noboil/convex/components'

const form = useFormMutation({ mutation: api.blog.create, schema: s.blog })

<Form form={form} render={({ Text, Choose, Toggle, Submit }) => (
  <>
    <Text name="title" />
    <Choose name="category" />
    <Toggle name="published" />
    <Submit>Create</Submit>
  </>
)} />

Headless hooks — Every component's logic is available as a standalone hook. Use useStepper without StepForm, useList without any UI, useBulkSelection for multi-select state, useSearch for debounced search. Bring your own design system.

What you get from one schema

FeatureWhat happens
CRUD endpointslist, create, update, rm with auth, pagination, and rate limiting
Real-timeLive subscriptions out of the box — ~39ms latency on SpacetimeDB
Type safetyEnd-to-end from database to UI. Misspell a field name → compile error
FormsAuto-generated with validation, file uploads, conflict detection, auto-save
Multi-tenancyOrganizations, roles, invites, join requests, per-item ACL
File uploadsDrag-and-drop with compression, progress tracking, and storage cleanup
Soft deleteBuilt-in with undo toast and restore
DevToolsSchema playground, query inspector, subscription monitor

CLI tooling

Three CLIs ship with noboil:

CLIPurpose
noboilProject scaffolding (init), health checks (doctor), upstream sync (sync), eject (eject)
noboil convexAdd tables, validate schema, migrate, visualize, generate docs
noboil stdbAdd tables, validate schema, local dev workflow, switch targets, generate docs

See CLI Reference for the full command reference.

Eject anytime

noboil is designed for incremental ejection. Replace one factory at a time while the rest of your app keeps running. See Ejecting.

On this page