Newform is a Typeform-style form builder SaaS built with a type-safe TypeScript monorepo. It supports authenticated form creation, public and unlisted form sharing, dynamic field types, multi-page forms, conditional logic, themes, response analytics, CSV export, QR sharing, seeded demo data, and an admin panel.
Newform gives creators a complete form publishing workflow:
- Create and manage forms from a protected dashboard.
- Configure dynamic field types, options, validation, pages, and conditional visibility.
- Publish forms with public or unlisted visibility.
- Share forms through stable slug URLs and QR codes.
- Accept public submissions without requiring respondents to log in.
- Review responses, analytics, and CSV exports.
- Moderate users and forms through an admin panel.
The project is designed as a SaaS foundation, not only a hackathon prototype. Core business logic lives in shared service packages, tRPC keeps API contracts end-to-end typed, and Drizzle/Zod keep database and validation boundaries explicit.
- Monorepo: pnpm workspaces, Turborepo
- Web: Next.js App Router, React, Tailwind CSS, shadcn-style UI components
- API: Express, tRPC, trpc-to-openapi, Scalar API docs
- Database: PostgreSQL, Drizzle ORM, Drizzle Kit migrations
- Validation: Zod
- Auth: Cookie-based JWT auth, email/password, optional Google OAuth
- Language: TypeScript
.
├── apps
│ ├── api # Express API, tRPC HTTP server, OpenAPI, Scalar docs
│ └── web # Next.js frontend application
├── packages
│ ├── database # Drizzle database client, schemas, migrations, seed data
│ ├── eslint-config # Shared ESLint configs
│ ├── logger # Shared logger package
│ ├── services # Business/domain logic
│ ├── trpc # tRPC routers, procedures, schemas, typed client exports
│ └── typescript-config # Shared TypeScript configs
├── docs # Implementation notes and project plans
├── package.json
├── pnpm-workspace.yaml
└── turbo.jsonLocation:
apps/webResponsibilities:
- Landing page, pricing page, templates page.
- Auth pages for login/signup.
- Protected creator dashboard.
- Form builder UI.
- Public form renderer at
/f/[slug]. - Admin panel routes under
/admin. - Typed API access through
@repo/trpc.
Location:
apps/apiResponsibilities:
- Starts the Express HTTP server.
- Mounts tRPC at
/trpc. - Mounts OpenAPI-compatible routes at
/api. - Serves OpenAPI JSON at
/openapi.json. - Serves Scalar documentation at
/docs. - Handles OAuth callback routes when providers are configured.
Defines the API contract:
- root router
- public/protected/admin procedures
- auth routes
- form routes
- admin routes
- OpenAPI metadata
- typed client exports
Contains domain logic:
- user authentication and OAuth account linking
- form lifecycle, public lookup, cloning
- field creation, validation, conditional visibility config
- public response submission validation
- response analytics and CSV export
- theme assignment
- admin dashboard, moderation, and audit logging
Contains persistence concerns:
- Drizzle database client
- table models
- migrations
- seed script
- Create, edit, publish, unpublish, and archive forms.
- Public and unlisted visibility modes.
- Custom slug URLs.
- Thank-you title and message.
- Expiry date and response limit settings.
- Theme assignment.
- Preview before publishing.
- Clone form into a draft copy.
- QR code sharing for published forms.
Supported field types:
- Short text
- Long text
- Number
- Single select
- Multi select
- Checkbox
- Rating
- Date
Supported configuration:
- Required/optional fields.
- Options for select, multi-select, and checkbox groups.
- Text length validation.
- Number min/max validation.
- Rating scale validation.
- Date min/max validation.
- Fields can be grouped by page.
- Public respondents move through visible pages step by step.
- Pages with no visible fields are skipped.
- Conditional fields can depend on supported source fields.
- Hidden required fields do not block submission.
- Hidden answers are not persisted.
- Server-side validation enforces visibility and required rules.
- Public submissions without respondent login.
- Server-side answer validation.
- Honeypot spam protection.
- IP + slug rate limiting.
- Transactional submission persistence.
- Response events and email event rows.
- Creator response table.
- Pagination.
- CSV export.
- Analytics cards and field breakdowns.
- Admin-only backend procedures.
- Suspended users blocked from protected procedures.
- Seeded admin account.
- User role/status management.
- Self-demotion and self-suspension protection.
- Last-admin and last-active-admin protection.
- Form moderation: force unpublish, archive, restore.
- Submission metadata list without exposing answer values.
- Audit logs for admin mutations.
- Transactional admin mutations and audit logs.
- Confirmation dialogs for destructive actions.
- Filters and pagination on admin list pages.
Seeded accounts:
Admin
Email: admin@example.com
Password: password123
Demo creator
Email: demo@example.com
Password: password123Create a root .env file. The workspace scripts load environment variables with dotenv-cli.
Required:
DATABASE_URL=postgresql://...
JWT_SECRET=your-secret
WEB_URL=http://localhost:3000
API_URL=http://localhost:3001
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_APP_URL=http://localhost:3000Optional OAuth variables:
GOOGLE_OAUTH_CLIENT_ID=
GOOGLE_OAUTH_CLIENT_SECRET=
GOOGLE_OAUTH_REDIRECT_URI=Production domain recommendation:
NEXT_PUBLIC_APP_URL=https://www.newform.inInstall dependencies:
pnpm installRun database migrations:
pnpm db:migrateSeed demo data:
pnpm db:seedStart development servers:
pnpm devCommon local URLs:
Web: http://localhost:3000
API: http://localhost:3001
Scalar docs: http://localhost:3001/docs
OpenAPI JSON: http://localhost:3001/openapi.jsonGenerate Drizzle migrations:
pnpm db:generateApply migrations:
pnpm db:migrateSeed database:
pnpm db:seedDevelopment note:
- This project is still in development.
- If local migration history becomes inconsistent, it is acceptable to reset the development database, rerun migrations, and reseed.
- Do not use development reset workflows against production data.
When the API server is running:
Scalar docs: /docs
OpenAPI JSON: /openapi.json
tRPC endpoint: /trpc
OpenAPI-compatible route prefix: /apiThe Scalar docs are generated from tRPC route metadata through trpc-to-openapi.
Type check:`
pnpm check-typesBuild:
pnpm buildFormat:
pnpm formatLint:
pnpm lintKnown lint note:
pnpm lintcurrently has known pre-existing ESLint configuration/warning blockers.pnpm check-typesandpnpm buildare the primary verified commands for the current implementation state.
Before final submission or deployment, verify:
- Landing page opens.
- Pricing page opens.
- Templates page shows only public published forms.
- Demo creator can log in.
- Admin can log in.
- Creator dashboard shows seeded forms.
- Creator can create a form.
- Creator can add fields and validations.
- Creator can create multi-page form flow.
- Creator can configure conditional visibility.
- Creator can preview, publish, unpublish, clone, and share a form.
- Public
/f/[slug]form can be submitted without login. - Expired/closed forms show a graceful unavailable state.
- Response limit is enforced.
- Creator can view responses.
- Creator can export CSV.
- Analytics update after submissions.
- Unlisted forms do not appear in templates but work by direct link.
- Legacy
/form/[form_id]does not expose draft/unpublished form fields. - Admin can view dashboard, users, forms, submissions, and audit logs.
- Admin destructive actions require confirmation.
- Admin cannot demote or suspend themselves.
Recommended deployment split:
- Web: Vercel
- API: Render, Railway, Fly.io, or similar Node hosting
- Database: Neon, Supabase, Railway Postgres, or managed PostgreSQL
Required production checks:
- Set production
DATABASE_URL. - Set a strong
JWT_SECRET. - Set
NEXT_PUBLIC_API_URLto the deployed API URL. - Set
NEXT_PUBLIC_APP_URLto the deployed web URL. - Run migrations against production database.
- Seed only intentional demo/admin data.
- Verify
/docsand/openapi.jsonon the deployed API. - Verify public form submission from a logged-out browser session.
- Real email delivery is not connected; email events are recorded in the database.
- Payments are not implemented.
- Password-protected forms are not implemented.
- Rate limiting is currently in-memory and should be moved to Redis/Upstash before horizontal scaling.
- Password hashing/JWT session hardening is planned for a future production security pass.
- Analytics are computed in application code and can be optimized with SQL/materialized aggregates as data grows.
- Keep app routes thin.
- Put business rules in
packages/services. - Put API contract and procedure boundaries in
packages/trpc. - Put persistence models and migrations in
packages/database. - Keep frontend custom feature components under
apps/web/custom/components. - Preserve end-to-end type safety with Drizzle, Zod, tRPC, and typed hooks.
- Avoid unsafe typing patterns such as
any,as any, andas unknown as.