Complete implementation including:
- Landing page with hero, features, how-it-works, pricing
- Employee management (CRUD with soft delete)
- AI constraint parser (Anthropic Claude API)
- German labor law templates (ArbZG §3, §5, §9)
- HiGHS ILP solver for optimal fair schedules
- Schedule calendar result view (employee × date grid)
- Shift framework configuration (periods + shifts)
- Subscription tiers: Free / Pro / Business
- PocketBase setup script with collection creation + seed data
- .env.example with all required variables documented
Pages: employees, constraints (list/new/templates), schedules (list/new/[id]),
settings (organization/shifts/billing), dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nuxt PocketBase Starter
A full-stack starter template for rapidly building and shipping apps with Nuxt 4, PocketBase, and Capacitor (Android/iOS).
What's Included
- Frontend — Nuxt 4 SPA with Vue 3, Nuxt UI, Tailwind CSS v4, Pinia state management
- Backend — PocketBase (SQLite, REST API, Realtime WebSocket), Dockerized with migrations and hooks
- Authentication — Email OTP, Google OAuth, Apple OAuth (auto-creates users on first login)
- Push Notifications — FCM via Deno sidecar service, works on Android and web
- Mobile — Capacitor for Android and iOS with deep linking support
- i18n — English and German, frontend and backend (localized email templates)
- Dev Tooling — ESLint, TypeScript, Vitest, Playwright, Mailpit (dev email viewer)
Prerequisites
Quick Start
1. Clone and install
git clone <repository-url>
cd nuxt-pocketbase-starter
pnpm install
2. Configure environment
cp .env.example .env
Edit .env to set your values. The defaults work for local development — you only need to configure OAuth credentials and Firebase if you want those features.
3. Start the backend
pnpm pocketbase:start
This starts three Docker containers:
| Service | URL | Purpose |
|---|---|---|
| PocketBase | http://localhost:8090 | API + Admin UI (/_/) |
| Sidecar | http://localhost:8091 | FCM push notification delivery |
| Mailpit | http://localhost:8025 | Dev email viewer (catches all outgoing mail) |
4. Start the frontend
In a second terminal:
pnpm dev
The app is available at http://localhost:3000.
Available Commands
| Command | Description |
|---|---|
pnpm dev |
Start Nuxt dev server |
pnpm build |
Production build |
pnpm preview |
Preview production build locally |
pnpm lint |
Run ESLint |
pnpm typecheck |
Run TypeScript type checking |
pnpm test |
Run tests (Vitest) |
pnpm pocketbase:start |
Start PocketBase + sidecar + Mailpit (Docker) |
pnpm pocketbase:stop |
Stop all backend containers |
pnpm pocketbase:types |
Regenerate TypeScript types from PocketBase schema |
pnpm assets |
Generate mobile app icons and splash screens |
Environment Variables
See .env.example for all variables. Key ones:
| Variable | Description |
|---|---|
APP_NAME |
Application display name |
APP_ID |
App identifier (e.g., com.example.myapp) |
NUXT_PUBLIC_APP_URL |
Frontend URL (default: http://localhost:3000) |
NUXT_PUBLIC_POCKETBASE_URL |
PocketBase API URL (default: http://127.0.0.1:8090) |
SUPERUSER_EMAIL / SUPERUSER_PW |
PocketBase admin credentials |
AUTH_GOOGLE_CLIENT_ID / SECRET |
Google OAuth credentials |
AUTH_APPLE_CLIENT_ID / SECRET |
Apple OAuth credentials |
SIDECAR_SECRET |
Shared secret between PocketBase and sidecar |
GOOGLE_CREDENTIALS_JSON |
Firebase service account JSON (for FCM push) |
Project Structure
├── app/ # Nuxt frontend
│ ├── pages/ # File-based routes
│ ├── stores/ # Pinia stores (user, counter, notifications, avatar)
│ ├── components/ # Vue components (App/, Counter/, Profile/)
│ ├── composables/ # usePocketBase()
│ ├── plugins/ # PocketBase client init
│ ├── middleware/ # Auth guard, user fetch
│ ├── i18n/locales/ # Frontend translations (en, de)
│ └── types/ # TypeScript types
├── pocketbase/
│ ├── pb_migrations/ # Database migrations (run on startup)
│ ├── pb_hooks/ # Server-side hooks and custom endpoints
│ │ ├── templates/ # Localized HTML email templates
│ │ └── locales/ # Backend translations
│ ├── sidecar/ # Deno service for FCM push delivery
│ ├── docker-compose.yml
│ └── Dockerfile
├── android/ # Capacitor Android project
├── ios/ # Capacitor iOS project
├── Dockerfile # Nuxt production image
└── capacitor.config.ts # Mobile app configuration
Customizing the Template
Rename the app
- Update
APP_NAMEandAPP_IDin.env - Update
package.jsonname and version - For mobile: regenerate assets with
pnpm assets
Add a new backend collection
- Create a migration file in
pocketbase/pb_migrations/(next number in sequence, e.g.,5_my_table.js) - Restart PocketBase:
pnpm pocketbase:stop && pnpm pocketbase:start - Regenerate types:
pnpm pocketbase:types - Create a Pinia store in
app/stores/using the generated types
Add a new language
- Add translation file to
app/i18n/locales/(e.g.,fr.json) - Register the locale in
nuxt.config.tsunderi18n.locales - Add backend translations in
pocketbase/pb_hooks/locales/(e.g.,fr.json) - Add email templates in
pocketbase/pb_hooks/templates/fr/ - Update
LanguageCodetype inapp/types/i18n.types.ts
Add a custom API endpoint
Create a hook file in pocketbase/pb_hooks/ (e.g., myEndpoint.pb.js) using routerAdd(). See resetCounter.pb.js for an example. Hooks use PocketBase's JSVM — not Node.js.
Mobile Development
Android
pnpm build
npx cap sync android
npx cap open android # Opens Android Studio
For push notifications, place your google-services.json at creds/fcm-google-services.json — it gets copied to the Android project automatically.
iOS
pnpm build
npx cap sync ios
npx cap open ios # Opens Xcode
Requires a Mac with Xcode and an Apple Developer account.
CI/CD
Gitea Actions workflows in .gitea/workflows/ trigger on pushes to the release branch (and manual workflow_dispatch):
| Workflow | What it does |
|---|---|
frontend.yaml |
Builds the Nuxt Docker image and pushes it to the Gitea Container Registry |
backend.yaml |
Builds the PocketBase Docker image and pushes it to the Gitea Container Registry |
android.yaml |
Builds the Android app via Fastlane and releases to Google Play |
ios.yaml |
Builds the iOS app via Fastlane and uploads to App Store Connect |
Required Gitea configuration
Repository variables (Settings → Actions → Variables):
| Variable | Example |
|---|---|
NUXT_PUBLIC_APP_URL |
https://your-domain.com |
NUXT_PUBLIC_POCKETBASE_URL |
https://pb.your-domain.com |
Repository secrets (Settings → Actions → Secrets):
| Secret | Purpose |
|---|---|
REGISTRY_TOKEN |
Gitea personal access token with package:write scope |
PLAY_STORE_JSON_KEY_DATA |
Google Play service account JSON (Android release) |
APP_STORE_CONNECT_API_KEY_ID |
App Store Connect API key ID (iOS release) |
APP_STORE_CONNECT_API_ISSUER_ID |
App Store Connect issuer ID |
APP_STORE_CONNECT_API_KEY_CONTENT |
Base64-encoded App Store Connect .p8 key |
IOS_CERTIFICATE_BASE64 |
Base64-encoded .p12 signing certificate |
IOS_CERTIFICATE_PASSWORD |
Password for the signing certificate |
IOS_PROVISIONING_PROFILE_BASE64 |
Base64-encoded provisioning profile |
Image tagging
Images are pushed to the Gitea Container Registry with two tags:
<registry>/<owner>/<repo>-frontend:<version>(frompackage.json)<registry>/<owner>/<repo>-frontend:latest<registry>/<owner>/<repo>-backend:<version>(frompackage.json)<registry>/<owner>/<repo>-backend:latest
Deployment
Both the frontend and backend are published as Docker images via CI. Deploy them together with Docker Compose on any server.
Production Docker Compose
Create a docker-compose.yml on your server:
services:
frontend:
image: git.your-domain.com/owner/repo-frontend:latest # Nuxt image from CI
ports:
- 3000:3000
restart: unless-stopped
pocketbase:
image: git.your-domain.com/owner/repo-backend:latest # PocketBase image from CI
ports:
- 8090:8090
- 8091:8091
volumes:
- pb_data:/pb/pb_data
env_file:
- .env
restart: unless-stopped
volumes:
pb_data:
Place a production .env next to it with your secrets (SUPERUSER_EMAIL, SUPERUSER_PW, SIDECAR_SECRET, OAuth credentials, GOOGLE_CREDENTIALS_JSON, etc.).
docker compose pull && docker compose up -d
Put a reverse proxy (Caddy, Traefik, nginx) in front to handle TLS and route traffic to port 3000 (frontend) and 8090 (PocketBase API).
Building images manually
If you're not using CI, you can build locally:
# Frontend
docker build \
--build-arg NUXT_PUBLIC_APP_URL=https://your-domain.com \
--build-arg NUXT_PUBLIC_POCKETBASE_URL=https://pb.your-domain.com \
-t nuxt-app .
# Backend
cd pocketbase
docker build --target prod -t pocketbase-app .
License
MIT