A production-ready template for building full-stack applications with SvelteKit 2, Svelte 5, BetterAuth authentication, Drizzle ORM, and Cloudflare D1 (SQLite).
| Technology | Purpose |
|---|---|
| SvelteKit 2 | Full-stack framework |
| Svelte 5 | UI framework with runes |
| BetterAuth | Authentication library |
| Drizzle ORM | Type-safe SQL ORM |
| Cloudflare D1 | Serverless SQLite database |
| Cloudflare Workers | Edge deployment platform |
npm install -g wrangler)To use this as a template for your own project (removing git history):
# Clone the repository
git clone https://github.com/YOUR_USERNAME/sveltekit-guestbook.git my-app
# Navigate to the project
cd my-app
# Remove the existing git history
rm -rf .git
# Initialize a fresh git repository
git init
# Install dependencies
npm install
# Make your first commit
git add .
git commit -m "Initial commit from template"
cp .env.example .env
.env:# Local SQLite database path (for development)
DATABASE_URL=file:local.db
# BetterAuth configuration
BETTER_AUTH_URL=http://localhost:5173
BETTER_AUTH_SECRET="your-secure-secret-here"
# Google OAuth (optional - remove from auth.ts if not using)
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
Note: Generate a secure secret for
BETTER_AUTH_SECRETusingopenssl rand -base64 32
If you want to use Google OAuth:
http://localhost:5173/api/auth/callback/google (development).env fileIf you don't need Google OAuth, remove the socialProviders section from src/lib/auth.ts.
This template uses Drizzle ORM with two database configurations:
Database schemas are defined in src/lib/server/db/:
better-auth-schema.ts - Authentication tables (user, session, account, verification)schema.ts - Your application tables + re-exports auth schemanpm run db:generate
npm run db:push
npm run db:migrate
npm run db:studio
wrangler login
wrangler d1 create your-database-name
wrangler.jsonc with your database ID:{
"d1_databases": [
{
"binding": "DB",
"database_name": "your-database-name",
"database_id": "your-database-id-from-step-2",
"migrations_dir": "./src/lib/server/db/migrations"
}
]
}
npm run cf-typegen
After modifying your schema files:
npm run db:generate
This creates a new SQL migration file in src/lib/server/db/migrations/.
To test migrations with Wrangler's local D1 emulator:
wrangler d1 migrations apply your-database-name --local
To apply migrations to your production Cloudflare D1 database:
wrangler d1 migrations apply your-database-name --remote
# Local
wrangler d1 migrations list your-database-name --local
# Remote
wrangler d1 migrations list your-database-name --remote
# Local
wrangler d1 execute your-database-name --local --command "SELECT * FROM user"
# Remote
wrangler d1 execute your-database-name --remote --command "SELECT * FROM user"
Start the development server:
npm run dev
The app will be available at http://localhost:5173.
| Command | Description |
|---|---|
npm run dev |
Start development server |
npm run build |
Build for production |
npm run preview |
Build and preview with Wrangler |
npm run check |
Type-check the project |
npm run check:watch |
Type-check in watch mode |
npm run deploy |
Build and deploy to Cloudflare |
npm run cf-typegen |
Generate Cloudflare binding types |
npm run db:generate |
Generate Drizzle migrations |
npm run db:push |
Push schema to local database |
npm run db:migrate |
Run migrations on local database |
npm run db:studio |
Open Drizzle Studio |
src/
├── app.d.ts # TypeScript declarations (App.Locals, Platform)
├── hooks.server.ts # Server hooks (auth, db initialization, route protection)
├── lib/
│ ├── auth.ts # BetterAuth server configuration
│ ├── auth-client.ts # BetterAuth client for browser
│ ├── components/ # Svelte components
│ └── server/
│ └── db/
│ ├── index.ts # Database client factory
│ ├── schema.ts # App database schema
│ ├── better-auth-schema.ts # Auth tables schema
│ └── migrations/ # SQL migration files
└── routes/
├── +layout.svelte # Root layout
├── +page.svelte # Landing page (public)
├── login/ # Login page (redirects if authenticated)
├── register/ # Registration page (redirects if authenticated)
└── app/ # Protected app pages (requires authentication)
Routes are protected in hooks.server.ts:
/app/* - Requires authentication (redirects to /login if not logged in)/login, /register - Redirects to /app if already logged inServer-side (in +page.server.ts or +server.ts):
export const load: PageServerLoad = async ({ locals }) => {
// Access the authenticated user
const user = locals.user;
// Access the database
const data = await locals.db.select().from(myTable);
return { user, data };
};
Client-side (in .svelte files):
<script lang="ts">
import { authClient } from "$lib/auth-client";
// Sign up
async function register() {
await authClient.signUp.email({
name: "John Doe",
email: "[email protected]",
password: "securepassword",
callbackURL: "/app"
});
}
// Sign in with email
async function login() {
await authClient.signIn.email({
email: "[email protected]",
password: "securepassword",
callbackURL: "/app"
});
}
// Sign in with Google
async function loginWithGoogle() {
await authClient.signIn.social({
provider: "google",
callbackURL: "/app"
});
}
// Sign out
async function logout() {
await authClient.signOut();
}
</script>
wrangler d1 migrations apply your-database-name --remote
wrangler secret put BETTER_AUTH_SECRET
wrangler secret put BETTER_AUTH_URL
wrangler secret put GOOGLE_CLIENT_ID # if using Google OAuth
wrangler secret put GOOGLE_CLIENT_SECRET # if using Google OAuth
npm run deploy
Set these secrets in Cloudflare (do NOT commit to git):
| Secret | Description |
|---|---|
BETTER_AUTH_SECRET |
Secure random string for session encryption |
BETTER_AUTH_URL |
Your production URL (e.g., https://myapp.workers.dev) |
GOOGLE_CLIENT_ID |
Google OAuth Client ID (if using) |
GOOGLE_CLIENT_SECRET |
Google OAuth Client Secret (if using) |
src/lib/server/db/schema.ts:import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const posts = sqliteTable("posts", {
id: integer().primaryKey(),
title: text().notNull(),
content: text().notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});
npm run db:generate
npm run db:push
# or
wrangler d1 migrations apply your-database-name --local
wrangler d1 migrations apply your-database-name --remote
Ensure your .env file has DATABASE_URL set for local development.
src/lib/server/db/migrations/migrations_dir path in wrangler.jsonc is correctRun npm run cf-typegen to regenerate Cloudflare types, then npm run check to verify.
MIT