Skip to content

Repository files navigation

Feedback SaaS

A full-stack SaaS platform for collecting and managing user feedback. Built as a Turborepo monorepo, powered by Bun.


Workspace Layout

apps/
  auth-web/       # Authentication portal (sign-in, sign-up, password reset)
  admin-web/      # Customer dashboard for managing feedback
  marketing/      # Public marketing website                        [upcoming]
  feedback/       # Embeddable feedback widget for client websites  [upcoming]
  storybook/      # UI component catalogue

packages/
  @feedback-saas/auth    # Better Auth configuration, server events, email via Resend
  @feedback-saas/db      # Drizzle ORM schema, queries, migrations (Neon PostgreSQL)
  @feedback-saas/ui      # Shared React component library (coss ui built on top of Base UI and style with Tailwind v4)
  @feedback-saas/utils   # Shared utility functions

Tech Stack

Bun Turborepo React 19 TanStack Start TypeScript Tailwind CSS v4 Drizzle ORM Neon Better Auth Oxlint

Layer Choice
Runtime / Package manager Bun
Monorepo orchestration Turborepo
Framework TanStack Start + TanStack Router
Data fetching TanStack Query
Forms TanStack Form
Database Drizzle ORM + Neon (serverless Postgres)
Authentication Better Auth + Resend
UI components Base UI + shadcn/ui, styled with coss ui
Styling Tailwind CSS v4
Icons Tabler Icons
i18n Paraglide JS
Error monitoring Sentry
Testing Vitest + Testing Library
Linting / Formatting Oxlint + Oxfmt
Git hooks Lefthook + Commitlint

Getting Started

Prerequisite: Bun ≥ 1.3.14

# Install Bun if you don't have it
curl -fsSL https://bun.sh/install | bash

# Clone and install dependencies
git clone <repo-url> && cd feedback-saas
bun install

Copy the environment variables template and fill in the required values:

cp .env.example .env

Required environment variables:

Variable Description
DATABASE_URL Neon PostgreSQL connection string
BETTER_AUTH_SECRET Random secret for session signing
BETTER_AUTH_URL Auth server base URL
BETTER_AUTH_TRUSTED_ORIGINS Comma-separated allowed origins
GOOGLE_CLIENT_ID Google OAuth client ID
GOOGLE_CLIENT_SECRET Google OAuth client secret
RESEND_API_KEY Resend API key for transactional email
VITE_FEEDBACK_SAAS_AUTH_WEB_URL Public URL of the auth app
VITE_FEEDBACK_SAAS_ADMIN_WEB_URL Public URL of the admin app
VITE_APP_ENV development | staging | production

Optionally seed development data:

bun run seed

Development

Start all apps and watch all packages simultaneously:

bun run dev

Start a specific app only:

bun run dev --filter=@feedback-saas/admin-web
bun run dev --filter=@feedback-saas/auth-web
bun run dev --filter=@feedback-saas/storybook
App Default port
auth-web 3000
admin-web 3001
storybook 6006

Common Commands

# Build everything
bun run build

# Run all tests
bun run test

# Lint the workspace
bun run lint

# Check formatting
bun run format

# Auto-fix formatting
bun run format:write

# Open Drizzle Studio
bun run --filter=@feedback-saas/db db:studio

# Generate a new DB migration
bun run --filter=@feedback-saas/db db:generate

# Storybook (component catalogue)
bun run dev --filter=@feedback-saas/storybook

Adding UI Components

Shared components live in packages/ui and are styled with coss ui, using Base UI primitives.

Install a component from the coss ui registry via the shadcn CLI, run from the repo root:

bunx --bun shadcn@latest add @coss/button -c packages/ui

Deployment

All apps are hosted on Vercel and deploy automatically on every push — no manual steps needed.

App Branch → Environment
auth-web main → production, develop → preview
admin-web main → production, develop → preview
marketing main → production, any other branch → preview
feedback main → production, any other branch → preview
storybook main → production, any other branch → preview

For auth-web and admin-web, only the develop branch produces a preview deployment, with a fixed URL per app, e.g. preview.admin.feedback-saas.XXX for admin-web, preview.auth.feedback-saas.XXX for auth-web. The other apps still get a unique preview URL per branch.

Vercel automatically picks up the Turborepo config and only rebuilds apps affected by a given change.

Database migrations are not run automatically on deploy. Read more in the section Pushing schema changes to Neon.


Git Conventions

Commits are linted against Conventional Commits via Commitlint. Lefthook runs formatting and linting on staged files before every commit.

feat: add feedback widget embed script
fix: resolve session expiry race condition
chore: bump drizzle-orm to 0.45

Database & Auth Schema

Updating the Better Auth schema

Run this whenever you upgrade better-auth or change its configuration in packages/auth/src/index.ts:

cd packages/db
bun x auth@latest generate --config=../auth/src/index.ts --output=./src/schema

This regenerates packages/db/src/schema/auth-schema.ts to match the current Better Auth config. After this, follow the steps in Pushing schema changes to Neon.

Prerequisite: packages/db must have a .env file with DATABASE_URL and BETTER_AUTH_URL set.


Pushing schema changes to Neon

After updating any schema file under packages/db/src/schema/, apply the changes to the database and rebuild the package:

# 1. Push schema changes to Neon
cd packages/db && bun run db:push

# 2. Rebuild the db package so the updated schema is picked up at runtime
bun run --filter=@feedback-saas/db build

Then restart the dev server.

db:push vs db:generate: These are two separate Drizzle workflows — do not mix them.

  • db:push diffs the schema directly against the DB and applies changes. No migration files needed or used.
  • db:generate + db:migrate generates SQL migration files and runs them — use this for production deployments where migration history matters.

For day-to-day development, db:push alone is sufficient.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages