Database as a Service for coding agents

AI‑friendly backend
in one prompt.

Describe your app. MoonDB ships the schema, REST API, auth, file storage, AI endpoints. Deploy nothing. Configure nothing. Agent‑friendly — agent‑optional.

How it works Sign up →
Copied! Now paste it into your AI agent
Free. We pay for the schema generation.
Try:

Have an agent? Connect via MCP →

One MCP server.
Three editors. Zero glue code.

Install the MoonDB MCP server in Cursor, Claude Code, or Windsurf — your agent gets a native toolkit for managing projects, schema, and data, plus a built-in primer (initialize.instructions) that briefs the model on MoonDB conventions before the first call. No "ask the user for an API key" loop, no copy-pasted prompt. Auth via your mk_… account key.

Cursor .cursor/mcp.json
Drop this into .cursor/mcp.json at your project root.
{ "mcpServers": { "moondb": { "url": "https://moondb.ai/mcp", "headers": { "X-API-Key": "mk_..." } } } }
Claude Code CLI command
One command. The server is registered globally.
# add the server claude mcp add --transport http \ moondb https://moondb.ai/mcp \ --header "X-API-Key: mk_..." # your prompt: "Use moondb to spin up a backend for a blog — users, posts, and comments. Owner-scoped writes."
Windsurf ~/.codeium/windsurf/mcp_config.json
Add to Windsurf's MCP config file.
{ "mcpServers": { "moondb": { "serverUrl": "https://moondb.ai/mcp", "headers": { "X-API-Key": "mk_..." } } } }
Project mgmt create_project list_projects rotate_keys
Schema set_schema validate_schema get_schema seed get_reference
Data & AI query get_row insert update_row delete_row ai_call

Your agent already knows
how to use this.

Context injection

Project-aware agent rules

# One endpoint. Full context. GET /p/{id}/v1/llm-context # Returns your exact schema, # endpoints, auth flow, types. # Paste into .cursorrules or # CLAUDE.md — zero hallucination.
Every project auto-generates agent instructions with your actual table names, column types, and API endpoints. No guessing.
Self-correcting

Errors that teach

400 { "code": "FK_NOT_FOUND", "message": "ref user_id: no row 'x'", "suggestion": "Create the user first with POST /p/.../api/users, then retry." }
Every error tells the agent exactly what went wrong and what to do next. Agents converge on a fix in one retry instead of spiraling.
Zero config

JSON in, backend out

# Agent sends this: { "tables": { "todos": { "title": "string required", "done": "bool default false" } } } # REST API is live. Auth works. # No deploy. No config files.
The agent describes the data model in plain JSON. MoonDB handles tables, indexes, triggers, access control, and the full CRUD API automatically.

Everything a backend needs,
nothing it doesn't.

{ }

Declarative schema

Send JSON. Get a database. The service handles diffing, migrations, and rollbacks. No SQL required.

/

Auto REST API

GET POST PATCH DELETE on every table. Filters, sort, pagination, joins built in.

@

Built-in auth

Email/password signup, JWT tokens, refresh flow. Owner-scoped access control per table.

^

File storage

R2-backed uploads. Mark a column type: "file" and multipart uploads just work.

!

Error suggestions

Every error includes a suggestion field with actionable fix instructions. Agents self-correct.

~

Edge-first

Runs on the edge in 300+ cities. Scales to zero. No cold starts. Sub-50ms latency worldwide.

Add AI to your app with one schema field.

Define AI endpoints in your schema — pick a model, write a prompt template, and call POST /ai/endpoint_name. No API keys, no proxy, no infra.

schema.json
{ "tables": { ... }, "ai_endpoints": { "summarize": { "model": "gemma", "prompt": "Summarize: {{text}}" }, "generate_cover": { "model": "flux-dev", "prompt": "{{description}}, vibrant" } } }
request
POST /p/{id}/ai/summarize { "text": "MoonDB is a..." } { "result": "An AI-friendly DBaaS...", "model": "gemma", "credits_used": 3 }
Gemma 4 26B
GPT-OSS 120B
FLUX.1 Schnell
FLUX.2 Dev
4 models 2 text + 2 image. Powered by Cloudflare Workers AI.
Credit-based billing Free monthly allowance per plan. Buy more when needed.
Template params Use {{param}} placeholders. Pass values at call time.
Access control Public, auth, or admin per endpoint. Same as your data API.

Predictable, usage-based plans.

New Moon
$0
  • Projects 1
  • DB storage 500 MB
  • File storage 1 GB
  • API reads 500K / mo
  • API writes 10K / mo
  • Emails / mo 100
  • AI credits / mo 5K
Start free
Half Moon
$9 / mo
  • Projects 10
  • DB storage 2 GB
  • File storage 25 GB
  • API reads 5M / mo
  • API writes 200K / mo
  • Emails / mo 1K
  • AI credits / mo 25K
Sign up → checkout
Eclipse
$99 / mo
  • Projects 200
  • DB storage 50 GB
  • File storage 500 GB
  • API reads 100M / mo
  • API writes 5M / mo
  • Emails / mo 25K
  • AI credits / mo 500K
Sign up → checkout

DB storage is total across all projects. Max 10 GB per individual project.

Ready to build?

Copy the prompt below, paste it into your AI agent, and have a working backend in under a minute.

Your agent's backend instructions.

Paste into your agent's system prompt, or download as a file. Your agent will set up the entire backend itself.

Agent Prompt
# MoonDB Backend This project uses MoonDB (https://moondb.ai) as its backend. MoonDB is a declarative DBaaS — describe your data model in JSON, get a REST API, auth, file storage, and AI endpoints automatically. ## Setup (do this once) Preferred path — install the MCP server (Cursor, Claude Code, Windsurf): Cursor: drop into .cursor/mcp.json { "mcpServers": { "moondb": { "url": "https://moondb.ai/mcp", "headers": { "X-API-Key": "mk_..." } } } } Claude Code: claude mcp add --transport http moondb https://moondb.ai/mcp \ --header "X-API-Key: mk_..." Windsurf: add to ~/.codeium/windsurf/mcp_config.json (same shape as Cursor, with "serverUrl" instead of "url") Docs: https://moondb.ai/mcp With MCP installed you get 14 native tools (create_project, set_schema, validate_schema, seed, query, get_row, insert, update_row, delete_row, ai_call, get_reference, ...) plus an initialize.instructions primer that briefs the model on MoonDB conventions. You can ignore the REST setup below if MCP is available. Otherwise (REST): 1. Get the API key (mk_...). Ask the user. New account: POST https://moondb.ai/v1/accounts/signup { email, password } → api_key Existing: https://moondb.ai/dashboard → Account. 2. Create a project: POST https://moondb.ai/v1/projects X-API-Key: mk_... { "name": "my-app" } → { data: { id, admin_key: "sk_...", public_key: "pk_..." } } - save admin_key (sk_...) in .env as MOONDB_ADMIN_KEY — NEVER commit - save public_key (pk_...) — safe for client-side code 3. Apply a schema: PUT https://moondb.ai/p/{project_id}/v1/schema X-Admin-Key: sk_... { "tables": { ... } } 4. Fetch the project-scoped reference (single source of truth, always in sync): GET https://moondb.ai/p/{project_id}/v1/llm-context (human-readable, the agent prompt) GET https://moondb.ai/p/{project_id}/v1/openapi.json (OpenAPI 3.0.3) Re-fetch after every schema change. Replace this Setup section with the output. Faster: pick a starter schema at https://moondb.ai/templates (todo / blog / habit-tracker / bookmarks / recipes / team-chat) and POST it as the schema in step 3 — one PUT, full backend. ## Schema definition Column types: string, text, int, float, bool, date, datetime, json, enum, ref, file Short-form: "string required unique", "int default 0", "ref users required", "bool default true" Modifiers: required, unique, index, default <value>, min, max, max_length Object form (advanced): "status": { "type": "enum", "values": ["draft", "published"], "default": "draft" } "task_id": { "type": "ref", "table": "tasks", "on_delete": "cascade", "required": true } "avatar": { "type": "file", "accept": ["image/*"], "max_size_mb": 5 } "score": { "type": "int", "min": 0, "max": 100 } Table options: "auth_table": true — auto-creates email + password_hash, enables /auth/* endpoints (signup, login, refresh, me, change/forgot/reset) "verify_email": true — sends verification email on signup (requires auth_table). Auto-creates email_verified, verification_token, verification_token_expires_at columns. "access": { "read": "public", "create": "authenticated", "update": "owner", "delete": "owner" } "owner_field": "user_id" — required when any access rule is "owner". Must be a ref column pointing to the auth table. MoonDB compares its value with the user id from the JWT — users can only read/update/delete their own rows. Server forces this field to JWT.sub on INSERT and ignores client values on UPDATE (non-admin). For auth_table itself use "id" (user owns their own row). "unique": [["col_a", "col_b"]] — composite unique constraints Schema options (top-level): "timestamps": true (default), "soft_delete": false (default) on_delete for refs: "cascade" | "restrict" (default) | "set_null" Enum values must match [a-zA-Z0-9_-]. Adding values is non-destructive; removing them requires "confirm_destructive": true. Built-in columns (never define these): id, created_at, updated_at, deleted_at (if soft_delete), email (auth_table), password_hash (auth_table), email_verified, verification_token, verification_token_expires_at (if verify_email), reset_token, reset_token_expires_at (auth_table) ## Schema management PUT https://moondb.ai/v1/schema { "tables": {...} } → apply (admin key) PUT https://moondb.ai/v1/schema { "tables": {...}, "confirm_destructive": true } GET https://moondb.ai/v1/schema → current schema (public key OK) POST https://moondb.ai/v1/schema/validate { "tables": {...} } → dry-run (admin key) GET https://moondb.ai/v1/schema/history → version history (admin key) POST https://moondb.ai/v1/schema/seed { "users": [...], "habits": [...] } → seed rows (admin key) Send the full schema every time — MoonDB diffs against the current version and auto-migrates. Destructive changes (drop column, change type, narrow enum, drop table) return a preview unless "confirm_destructive": true. Seed supports cross-references via "@<table>.<index>" syntax (index is the position in the seed array). For an auth_table, set "password" (string) on each row — it's auto-hashed into password_hash. Example: { "users": [{ "email": "a@x.io", "password": "secret" }, { "email": "b@x.io", "password": "secret" }], "habits": [{ "user_id": "@users.0", "name": "Run" }, { "user_id": "@users.1", "name": "Swim" }] } ## REST API GET https://moondb.ai/api/{table}?field=op.value&sort=field.desc&limit=N GET https://moondb.ai/api/{table}/{id} POST https://moondb.ai/api/{table} { ...fields } PATCH https://moondb.ai/api/{table}/{id} { ...fields } — partial update PUT https://moondb.ai/api/{table}/{id} { ...fields } — full replace (clears omitted non-default columns) DELETE https://moondb.ai/api/{table}/{id} POST https://moondb.ai/api/{table}/bulk [ {...}, {...} ] — atomic, up to 100 rows Responses: List: { "data": [...], "meta": { "total": N, "limit": N, "offset": N, "has_more": bool, "next_cursor": "eyJ..." | null } } Single: { "data": { ...row } } Error: { "error": { "code": "...", "message": "...", "suggestion": "..." } } Filter operators: eq, neq, gt, gte, lt, lte, like, in, is_null, not_null Examples: ?status=eq.active ?priority=in.high,medium ?name=like.foo% ?deleted_at=is_null ?score=gte.10 Sort: ?sort=created_at.desc Multiple: ?sort=col1.asc&sort=col2.desc Pagination: Offset (small tables, cheap totals): ?limit=20&offset=40 Cursor (large tables, keyset; use meta.next_cursor from the previous page): ?limit=20&sort=created_at.desc&cursor=eyJ... (opaque — never edit) Select: ?select=id,name — return only specific fields Include related: ?include=user_id — inline ref-target row (max 4; ref table must have public/auth read access) OpenAPI spec: GET https://moondb.ai/v1/openapi.json Auto-generated from the current schema. Feed it to Postman / Swagger UI / openapi-generator / any SDK generator. Cache-Control: 60s. ## Auth (when auth_table is set) POST https://moondb.ai/auth/signup { email, password } → { data: { token, refresh_token, user } } POST https://moondb.ai/auth/login { email, password } → { data: { token, refresh_token, user } } POST https://moondb.ai/auth/refresh { refresh_token } → { data: { token, refresh_token } } (rotates) GET https://moondb.ai/auth/me Authorization: Bearer {token} → { data: { user } } PATCH https://moondb.ai/auth/me { display_name, ... } → { data: { user } } POST https://moondb.ai/auth/logout Authorization: Bearer {token} (revokes refresh) Token expires in 1 hour. Use refresh_token (30 days, rotates on every use). Headers: Authorization: Bearer {token} for auth, X-Public-Key: pk_... for public reads (browser code), X-Admin-Key: sk_... server-side only. Password management: POST https://moondb.ai/auth/change-password { current_password, new_password } (Bearer token) POST https://moondb.ai/auth/forgot-password { email, redirect_url } → 200 always (no enum leak) POST https://moondb.ai/auth/reset-password { token, new_password } → resets password forgot-password requires redirect_url whose origin is in the project's ALLOWED REDIRECT ORIGINS allowlist. Without configuration the endpoint 400s with VALIDATION_REDIRECT_URL. Configure once per project: GET https://moondb.ai/v1/redirect-origins (admin key) PUT https://moondb.ai/v1/redirect-origins { "allowed_redirect_origins": ["https://myapp.com"] } (admin key) Email verification (when verify_email: true on auth table): On signup, a verification email is sent automatically (response has _warning). GET https://moondb.ai/auth/verify?token=... → confirms email (link in email body) POST https://moondb.ai/auth/resend-verification → resends (requires Bearer) user.email_verified is 0 or 1. External auth (Clerk, Auth0, Supabase, Firebase, Cognito, Kinde, WorkOS, Stytch): PUT https://moondb.ai/v1/auth-config { "provider": "external", "jwks_url": "https://...", "user_id_claim": "sub", "audience": "..." } DELETE https://moondb.ai/v1/auth-config → remove external auth Validates RS256 JWTs via JWKS. Users auto-created on first request. jwks_url is restricted to known-IdP host suffixes (https only, no userinfo). audience defaults to project.id if not supplied — defends against cross- project token replay when multiple projects share the same IdP tenant. Key rotation: POST https://moondb.ai/v1/rotate-keys → { admin_key?, public_key? } (admin key) POST https://moondb.ai/v1/rotate-jwt-secret → { rotated: true } (admin key) Rotating the JWT secret invalidates every outstanding end-user access token. Users continue with their refresh token (which is project-bound but lives in the registry, not signed by the secret) — or re-login. Changing a user's password (via change-password or reset-password) bumps a per-row password version that invalidates all currently-issued access tokens for that user. New JWTs after password change carry the new version. ## File storage (for `file` type columns) POST https://moondb.ai/storage/upload multipart/form-data → { data: { id, url, size } } Form fields: file (required), table, column (optional, validates against schema file column constraints), row_id (optional, ties the file to a row). When row_id is set the caller must own that row. GET https://moondb.ai/storage/{file_id} → 302 to a presigned R2 URL (or direct stream) DELETE https://moondb.ai/storage/{file_id} → { deleted: true } Upload requires auth (Bearer or admin key). Downloads are served as attachment with X-Content-Type-Options: nosniff for everything except a whitelist of safe inline MIMEs (image/png|jpeg|gif|webp, video/mp4|webm, audio/*) — defence against stored XSS via .html/.svg upload on the API origin. After upload, store the returned url or id in the row's file column via PATCH /api/{table}/{id}. Admin-uploaded files (no Bearer at upload time) are admin-only on read. ## AI endpoints Define endpoints in schema under "ai_endpoints" (same PUT /v1/schema call): { "tables": { ... }, "ai_endpoints": { "summarize": { "model": "gemma", "prompt": "Summarize: {{text}}", "access": "auth" } } } Call: POST https://moondb.ai/ai/{name} { "param": "value" } Text response: { "data": { "result": "...", "model": "...", "credits_used": N } } Image response: { "data": { "image_base64": "<base64>", "content_type": "image/png", "model": "...", "credits_used": N } } Image models return base64-encoded data, NOT a URL. Text models: gemma (fast & cheap, vision), gpt-oss (most intelligent) Image models: flux-schnell (fast), flux-dev (photorealistic) Vision (image analysis): gemma supports image input. Pass "image" in the request body as a base64 data URI or an https URL: POST https://moondb.ai/ai/{name} { "text": "what is this?", "image": "data:image/png;base64,..." } POST https://moondb.ai/ai/{name} { "text": "describe", "image": "https://example.com/photo.jpg" } The prompt template handles {{text}}, the image is added automatically. Access: "public" | "auth" (default) | "admin" Credits: each plan includes free credits/month; check GET /v1/ai/balance → { data: { free_credits, free_credits_limit, purchased_credits, total, resets_at } } Buy more: POST /v1/ai/topup. Balance never goes negative — over-budget calls fail with AI_INSUFFICIENT_CREDITS (402) before the model is invoked. ## Conventions - Never expose the admin key (sk_…) in client code. Store in .env as MOONDB_ADMIN_KEY. Use it server-side only. - Send Authorization: Bearer <token> once a user has logged in. - Send X-Public-Key: pk_… for public-read endpoints from browser code. - Every error response has a "suggestion" field — read it to self-correct. Don't guess: the suggestion tells you the exact next call. - Headers and bodies are JSON. CORS is open (no credentials). - Rate limits are per-project (RPM by plan) and per-IP for auth endpoints.

Supabase vs MoonDB

SupabaseMoonDB
Setup timeDashboard + SQL migrations3 API calls
Schema formatRaw SQL / migrationsDeclarative JSON
Agent-friendlyNeeds docs & SQL knowledgeSelf-describing API
AuthBuilt-in (GoTrue)Built-in (JWT + JWKS)
File storageBuilt-in (S3)Built-in (R2)
AI endpointsEdge Functions (custom)Schema-defined, zero code
Error guidanceGeneric errorsSuggestion field on every error
Auto-migrationsManual SQL diffsAuto-diff & migrate
PricingFree → $25/moFree → $9/mo
Best forFull-stack developersCoding agents & vibe coding

MoonDB is purpose-built for AI coding workflows. Full comparison →