Skip to content

Lemony-Click/lemony-starter

Repository files navigation

Lemony Starter

A full-stack monorepo starter with Bun, tRPC, Prisma, React, and shadcn/ui. Includes a working notes app as a reference implementation.

Stack

Layer Technology
Runtime Bun
Monorepo Turborepo
Linting Biome
Database PostgreSQL + Prisma ORM
API tRPC v11 (HTTP standalone)
Frontend React 19 + Vite + Tailwind CSS v4
UI shadcn/ui

Structure

apps/
  server/          – tRPC HTTP API server (Bun, port 3000)
  web/             – Vite + React frontend (port 5173)
packages/
  db/              – Prisma schema, generated client, PrismaClient singleton
  services/        – Business logic (noteService, tagService); depends on db
  trpc/
    server/        – appRouter, routers, tRPC context; depends on services
    client/        – Vanilla tRPC client factory for non-React consumers
  ui/              – Shared shadcn/ui components, Tailwind CSS base styles

Data flow

web (React)
  └─ @workspace/trpc/client  (tRPC react-query hooks)
       └─ HTTP → apps/server
                    └─ @workspace/trpc/server  (appRouter + routers)
                         └─ @workspace/services  (business logic)
                              └─ @workspace/db  (Prisma client)
                                   └─ PostgreSQL

Getting started

Prerequisites

1. Start the dev database

docker compose -f docker-compose.dev.yaml up -d

This starts a PostgreSQL container on localhost:5432 with user postgres, password postgres, and database lemony.

2. Set up environment variables

cp .env.example .env

The default values in .env.example match the dev Docker container and require no changes for local development.

Key variables:

Variable Default Description
DATABASE_URL postgresql://postgres:postgres@localhost:5432/lemony Prisma connection string
PORT 3000 API server port
CORS_ORIGIN http://localhost:5173 Allowed origin for CORS
VITE_SERVER_URL http://localhost:3000/trpc API URL used by the web app

Note: All apps read from the single root .env file. Bun loads it automatically for the server (--env-file in the dev script); Vite reads it via envDir; Prisma CLI commands load it directly in prisma.config.ts.

3. Install dependencies

bun install

4. Push schema to the database & generate Prisma client

bun run db:push      # sync schema to the dev database
bun run db:generate  # regenerate the type-safe client

prisma db push is the recommended workflow for local development — it applies the current schema directly without creating migration files. Use prisma migrate dev when you want to track schema changes as versioned migrations (see Migrations).

5. Start dev servers

bun run dev

Turborepo starts both apps/server and apps/web in parallel with hot-reload.

Service URL
API http://localhost:3000
Web app http://localhost:5173

Production deployment

The production Docker Compose file builds the server into a minimal binary and runs Prisma migrations automatically before the server starts.

POSTGRES_PASSWORD=<your-secret> docker compose up --build

Services started:

  1. postgres — PostgreSQL database
  2. migrate — one-shot container that runs prisma migrate deploy
  3. server — compiled API server (starts after migrate completes)

The web app is not containerised by default. Deploy it to a static host (Vercel, Cloudflare Pages, etc.) and point VITE_SERVER_URL at your production server URL at build time.


Migrations

For schema changes that need to be tracked and deployed safely in production, use Prisma's migration workflow instead of db push:

# Create and apply a new migration (from workspace root)
bun run db:migrate -- --name <describe-your-change>

# Apply pending migrations in CI / production
bun run db:deploy

You can also run Prisma CLI directly from packages/dbDATABASE_URL is loaded automatically from the workspace root .env:

cd packages/db
bunx prisma migrate dev --name <describe-your-change>
bunx prisma migrate deploy

Adding UI components

Components live in packages/ui/src/components and are shared across all apps.

cd packages/ui
bunx shadcn@latest add <component-name>

Import anywhere in the monorepo:

import { Button } from "@workspace/ui/components/button"

Extending the API

Follow these steps to add a new domain (e.g. Post):

1. Add the Prisma model

Edit packages/db/schema.prisma, then sync and regenerate:

cd packages/db
bunx prisma db push
bunx prisma generate

2. Create a service

Create packages/services/src/postService.ts:

import { prisma } from "@workspace/db";

export const postService = {
  async list() {
    return prisma.post.findMany({ orderBy: { createdAt: "desc" } });
  },
  // create, update, delete ...
};

Export it from packages/services/src/index.ts:

export { postService } from "./postService";

3. Create a tRPC router

Create packages/trpc/server/routers/post.ts:

import { z } from "zod";
import { publicProcedure, router } from "../trpc";
import { postService } from "@workspace/services";

export const postRouter = router({
  list: publicProcedure.query(() => postService.list()),
  // ...
});

4. Register the router

Add it to packages/trpc/server/appRouter.ts:

import { postRouter } from "./routers/post";

export const appRouter = router({
  note: noteRouter,
  tag: tagRouter,
  post: postRouter, // ← add this
});

The new procedures are immediately available in the frontend via the typed trpc client with full autocompletion.


Environment & authentication

The tRPC Context (packages/trpc/server/trpc.ts) is intentionally empty in the starter. To add authentication:

  1. Populate Context with your session/user data (e.g. from a JWT or cookie)
  2. Pass the context factory to createHTTPServer in apps/server/src/index.ts
  3. Guard procedures using a protectedProcedure middleware that checks ctx

Useful commands

Command Description
bun run dev Start all dev servers
bun run build Build all packages and apps
bun run typecheck Type-check the entire monorepo
bun run lint Lint the entire monorepo
bun run format Format with Biome
bun run check Lint + format + sort imports (Biome)
bun run db:push Sync schema to dev database
bun run db:migrate Create & apply a migration
bun run db:generate Regenerate Prisma client
cd packages/ui && bunx shadcn@latest add <c> Add a shadcn/ui component

About

Project starter template

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors