AI‑powered form builder and response collection app built with Next.js, Clerk, Drizzle ORM, Neon Postgres, Tailwind CSS, and OpenAI.
- AI form builder: Chat to describe your form; the AI returns a strict JSON schema.
- Save & manage forms: Persist schemas, name forms, and manage publish/unpublish state.
- Public forms: Share a link (read-only, no auth) that renders inputs from the saved schema and validates required fields.
- Responses & analytics:
- View recent submissions for any form you own.
- See a realtime total submissions counter.
- Export responses as CSV.
- Authentication: Clerk-secured dashboard; user records are provisioned on first visit.
- Postgres schema (Drizzle):
users,forms,form_responses.
- Next.js 15 (App Router), React 19
- Clerk for authentication
- Drizzle ORM with Neon Postgres
- Tailwind CSS v4
- Vercel AI SDK and OpenAI for the builder
- Node 20+ and pnpm
- Neon (or any Postgres) connection string
- Clerk application (keys + redirect URLs)
- OpenAI API key
pnpm installCreate .env.local and set the following variables:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/auth/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/auth/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard
DATABASE_URL=
OPENAI_API_KEY=
# (optional) Used to build share links in the dashboard; defaults to request host
# NEXT_PUBLIC_APP_URL=https://your-domain.comNotes:
DATABASE_URLshould be a full Postgres connection string (e.g., Neon serverless).OPENAI_API_KEYis required for the AI form builder.- The Clerk URLs above match the app routes already in this repo.
pnpm db:pushThis applies the Drizzle schema in db/schema.ts to your Postgres database.
pnpm devOpen http://localhost:3000. Sign up/sign in with Clerk.
- Go to
/dashboardto see your forms. - Click Create New Form to start a draft (a UUID form id is generated).
- The builder is at
/dashboard/form/:id.
- Describe the form you want in natural language.
- The AI returns strict JSON with:
schema:{ name?, description?, fields: [{ name, label, type, required?, ... }] }followUps: questions if constraints are ambiguoushumanisedDescription: short natural-language summary
- You’ll see a live preview. Name the form and Save.
- In
/dashboard, toggle Publish/Unpublish. - Copy the share link to
/form/:idand open it in a new tab.
- Renders inputs from the saved schema.
- Validates
requiredfields client-side. - On submit, persists to
form_responsesand redirects to/form/:id/thank-you.
- Visit
/dashboard/form/:id/responses:- Table of recent responses (last 200).
- Realtime total submissions.
- Export CSV via the page button (downloads from
/dashboard/form/:id/responses/export?format=csv).
- AI builder API (Edge):
src/app/api/ai/form-builder/route.ts - Responses API:
src/app/api/forms/[id]/responses/route.ts - Analytics API:
src/app/api/forms/[id]/analytics/route.ts - CSV export:
src/app/dashboard/form/[id]/responses/export/route.ts - Public form page:
src/app/form/[id]/page.tsx - Builder UI:
src/app/dashboard/form/[id]/Builder.tsx - Response submit action:
src/app/form/[id]/actions.ts - Auth utilities:
src/lib/auth.ts, user provisioning:src/lib/users.ts - DB client:
src/lib/db.ts, schema:db/schema.ts
pnpm dev # Start Next.js in development
pnpm build # Build for production
pnpm start # Start production server
pnpm db:generate # Generate Drizzle SQL snapshots
pnpm db:push # Apply current schema to the database
pnpm eval:dev # Run evalite watcher (optional/dev-only)This repo includes Evalite tests that exercise the AI form builder end‑to‑end and score outputs.
- Location:
src/tests/form-builder.eval.ts - Model: uses the Vercel AI SDK with OpenAI (
gpt-4) - Scoring: a structural scorer checks that outputs are strict JSON with a valid
schema.fieldsarray; you can add additional scorers (including LLM‑as‑a‑judge) easily.
pnpm eval:dev # watches and runs evals on change
# or, once-off:
npx evalite run src/tests/form-builder.eval.tsRequires OPENAI_API_KEY in your environment.
Evalite can trace Vercel AI SDK calls for better visibility. Wrap your model with traceAISDKModel:
// inside an eval task using streamText
import { traceAISDKModel } from "evalite/ai-sdk";
const result = streamText({
model: traceAISDKModel(openai("gpt-4o-mini")),
messages,
});See the Evalite AI SDK examples for details: Evalite × Vercel AI SDK.
You can score generations with an LLM judge via createScorer by prompting a model to grade the output:
import { createScorer } from "evalite";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";
export const JudgeScorer = createScorer({
name: "LLM Judge",
description: "Grades schema quality (0..1)",
scorer: async ({ output }) => {
const r = await streamText({
model: openai("gpt-4o-mini"),
system: "Return ONLY a number 0..1 for quality of the provided schema.",
prompt: `Schema JSON:\n${output}`,
});
const text = await r.text;
const score = Number.parseFloat(text.trim());
return Number.isFinite(score) ? Math.max(0, Math.min(1, score)) : 0;
},
});Add JudgeScorer to the scorers: [] array in your eval to record the judgment alongside structural checks.
- Set the same environment variables in your hosting provider.
- Ensure
DATABASE_URL,CLERK_*, andOPENAI_API_KEYare configured. - The AI builder uses
gpt-4via the Vercel AI SDK.
- 401 from analytics/responses APIs: ensure you’re signed in and own the form.
- AI responses empty or errors: check
OPENAI_API_KEYand usage limits. - DB errors: verify
DATABASE_URLand thatpnpm db:pushsucceeded. - Share links wrong domain: set
NEXT_PUBLIC_APP_URLin production.
Proprietary – for internal/demo use unless a license is added.