Blog

Host APIs: own the UI, borrow the boring security, bring your own keys

How AtroUI Host APIs work: thin Next.js routes, hardened handlers in atroui/api/*, BYOK for forms and AI tools, install modes, rate limits, and what you still own in production.

Most UI kits stop at a nice form. You still write /api/contact yourself: validation, a spam honeypot, body size caps, rate limits, and the mail send. AI workspaces add more glue: the compose pipeline, provider errors, and what happens when the key is missing.

AtroUI’s answer is Host APIs. You copy UI from the shadcn registry, keep a thin app/api/*/route.ts stub on your Next.js app, and call production-minded handlers in the published atroui package (atroui/api/*). Secrets stay in your env. AtroUI never ships shared keys and does not run paid AI on atroui.com.

The line we use everywhere:

Own the UI in your repo. Borrow the boring API security. Bring your own keys.

This post is the long version of Host APIs. It covers why the split exists, how install modes work, what is wired today, and what you still own in production.

What a Host API is

Host is the app that hosts the route: your Next.js deployment. API is that backend handler. Together: Host API.

Three layers fit together:

text
Browser  →  your /api/contact  →  atroui/api/contact (library)
                                      ↓
                            your SMTP / Resend / HF / xAI

UI: registry items like @atroui/contact-form or @atroui/og-workspace. The CLI copies source into your repo. You edit CONTENT, rebrand, and delete what you do not need.

Route stub: @atroui/api-contact (and siblings) drop a thin App Router file that forwards POST to the package handler.

Handler: atroui/api/contact|waitlist|newsletter|generate|thumbnail|scope. Shared validation, honeypot, body caps, rate limits, and mail or AI wiring. You upgrade this with npm instead of re-vendoring native image deps into every app.

ts
import { handleContactPost } from "atroui/api/contact"

export const runtime = "nodejs"

export async function POST(req: Request) {
  return handleContactPost(req)
}

Host APIs are not a SaaS API on atroui.com that you call with an AtroUI key. They are not required for pure marketing sections (heroes, footers, who bands). Those stay registry UI only.

They target the Next.js App Router today. If your stack is not Next, you can still own the UI via the CLI. Host APIs are the optional backend chapter for Next hosts. See What is AtroUI? for the wider catalog story.

Why this stands out

Typical kits ship buttons and forms UI, then leave the API to you. Shared demo hosts often bake in studio keys. That is fine for a playground. It is wrong for a library that claims you own production.

AtroUI ships named exports with boring security already in place, refuses to hold consumer secrets, and documents rate-limit upgrades instead of pretending in-memory limits are multi-region safe.

That hybrid is rare: copy-paste UI plus production-minded handlers, with BYOK as a product rule, not a footnote.

Three install modes

Never lead with npm i atroui for pure UI. The matrix is identical on Host APIs, Installation, and the READMEs.

bash
# 1) Registry UI only (no npm package)
npx shadcn@latest add @atroui/home-hero

# 2) Forms: package + thin route stubs
npm i atroui
# next.config.ts → transpilePackages: ["atroui"]
npx shadcn@latest add @atroui/contact-form @atroui/api-contact
# same pattern: waitlist, newsletter

# 3) AI tools: same package setup
npx shadcn@latest add @atroui/og-workspace @atroui/api-generate
npx shadcn@latest add @atroui/thumbnail-workspace @atroui/api-thumbnail
npx shadcn@latest add @atroui/scope-chat @atroui/api-scope

transpilePackages: ["atroui"] matters when you import Host API handlers from the package. It does not matter when you only copy heroes. More context: transpilePackages and Turbopack and why npm stayed for handlers.

From zero to a live contact form

A concrete path for forms:

1. Register the catalog and add the form plus route (see Installation).

2. Install atroui and set transpilePackages.

3. Put mail secrets in the host env. Never commit them.

4. Deploy your Next app. The browser posts to your /api/contact.

bash
CONTACT_EMAIL_TO=hello@acme.test
CONTACT_EMAIL_FROM=noreply@acme.test
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=…
SMTP_PASSWORD=…
# or
RESEND_API_KEY=…
RESEND_AUDIENCE_ID=…   # newsletter audience subscribe

Without mail config, handlers return 503. That means “host not configured,” not “the client sent a bad payload.” Bots that fill the honeypot field get a fake 200 and no send.

AI tools: UI, route, and BYOK

OG, thumbnail, and scope follow the same pattern. Preview and rule-based paths work without keys so demos stay useful. Full AI generation returns 503 until providers are set, including on atroui.com.

bash
HUGGINGFACE_API_KEY=…   # OG / thumbnail AI
GEMINI_API_KEY=…          # optional freeform / Pro image
XAI_API_KEY=…             # optional scope LLM + thumbnail Pro

What works without keys: OG and thumbnail preview-only downloads; scope chat rule-based replies when XAI_API_KEY is unset.

Supported engines today: Hugging Face, Gemini, and xAI where each feature needs them. Dropping OPENAI_API_KEY or ANTHROPIC_API_KEY in .env does not auto-wire those vendors. BYOK means bring supported keys. More providers are deliberate future work. Until then you can fork the thin route and call your own SDK while keeping the UI.

Catalog entries tagged Host API in the sidebar link back to the Host APIs guide and the matching component docs.

Security defaults in the handlers

These are the boring controls handlers already apply. Per-route numbers live on the docs page:

Honeypot JSON field honeypot: filled bots get a quiet fake success.

Body cap 8 MB JSON; contact attachment cap 5 MB decoded.

Per-IP sliding windows (15 minutes): contact 5 · waitlist/newsletter 10 · scope 30 · generate/thumbnail preview 30 · AI 5.

429 means rate limit exceeded (retryAfterSec). 503 means missing mail or AI config.

Handlers cover common copy-paste route mistakes. You still own auth, CDN/WAF abuse controls, email deliverability, provider spend, and shared rate-limit storage across instances. Full threat model: Host APIs.

Production rate limiting: memory vs Redis

Default checkRateLimit is an in-memory sliding window per Node process. That is fine locally and on a single instance. On multi-region Vercel (or any multi-instance host), each process has its own map, so effective limits multiply.

Set Upstash Redis REST or Vercel KV env vars on the host. The same API switches to a shared backend automatically. You do not fork handlers. Redis is optional; memory stays the default. If Redis is unreachable, handlers fall back to memory so forms stay up. On Vercel production without a store, you get a one-time console warning.

bash
# Upstash Redis REST (recommended)
UPSTASH_REDIS_REST_URL=https://….upstash.io
UPSTASH_REDIS_REST_TOKEN=…

# or Vercel KV (same REST protocol)
KV_REST_API_URL=…
KV_REST_API_TOKEN=…

Why handlers stay on npm

UI moved to the registry so you own files day one (npm to shadcn registry). Handlers stayed in atroui on purpose.

Contact and waitlist are mostly TypeScript. OG and thumbnail pull in Satori, resvg, sharp, fonts, and compose logic. Vendoring that into every consumer via the CLI would ship native .node addons and font paths into app trees in painful ways. A versioned package keeps one upgrade path for security and compose fixes while UI remains editable source in your repo.

What this product optimizes for

Speed from “pretty form” to “posts to my /api with sane defaults.”

A hard boundary: AtroUI never holds your Resend, HF, or xAI bill.

Honest install modes: CLI for UI, npm only when you need handlers.

Docs and demos that fail closed (503 / preview / rules) instead of burning a shared studio key.

Next steps

Canonical guide: Host APIs.

CLI setup: Installation. Catalog: Registry.

Try a form: Contact form.

How the split happened: Why we moved to the shadcn registry.

Fresh app walkthrough: Install AtroUI in a Next.js App Router project.