create-nextcap
v0.1.0
Published
Create Next.js + Capacitor apps with Cloudflare Workers & OTA updates
Maintainers
Readme
create-nextcap
CLI untuk scaffolding project Next.js + Capacitor dengan deploy ke Cloudflare Workers dan sistem OTA update built-in.
Satu codebase → Web + iOS + Android, deploy tanpa Vercel, update tanpa submit ke store.
Stack
| Teknologi | Fungsi | |-----------|--------| | Next.js 16 | Framework React (App Router, SSR, API Routes) | | Capacitor | Native wrapper iOS & Android | | Cloudflare Workers | Hosting (via OpenNext) | | Cloudflare R2 | Storage bundle OTA | | Tailwind CSS 4 | Styling | | @capgo/capacitor-updater | OTA update engine | | npm workspaces | Monorepo |
Prerequisites
- Node.js 18+
- Android Studio — untuk development Android (download)
- Xcode — untuk development iOS, macOS only (download via App Store)
- Cloudflare account — untuk deploy (sign up gratis)
Xcode dan Android Studio hanya dibutuhkan saat development native. Web development (
npm run dev) tidak memerlukan keduanya.
Quick Start
npx create-nextcap my-app
cd my-app
npm run devCLI akan menanyakan:
| Prompt | Contoh | Keterangan |
|--------|--------|------------|
| Project name | my-app | Nama folder & package |
| Display name | My App | Nama yang tampil di device |
| Bundle ID | com.example.myapp | ID unik app di store |
| Deploy domain | app.example.com | Domain Cloudflare Workers |
Struktur Project
my-app/
├── package.json # Monorepo root (npm workspaces)
├── OTA.md # Dokumentasi lengkap OTA
│
├── apps/
│ └── mobile/ # Next.js app
│ ├── app/ # Pages (App Router)
│ │ ├── layout.tsx # Root layout + OTA provider
│ │ ├── page.tsx # Halaman utama
│ │ ├── globals.css
│ │ └── api/ota/ # API routes (jalan di Cloudflare)
│ │ ├── update/route.ts # POST — cek update (dipanggil plugin)
│ │ ├── upload/route.ts # POST — upload bundle baru
│ │ ├── download/[version]/route.ts # GET — download bundle zip
│ │ ├── manifest/route.ts # GET — lihat semua versi
│ │ └── rollback/route.ts # POST — rollback ke versi lama
│ │
│ ├── lib/
│ │ ├── r2.ts # Helper akses R2
│ │ ├── auth.ts # Validasi API key
│ │ ├── OTAProvider.tsx # Init OTA di native platform
│ │ └── ota-updater.ts # Event listeners capacitor-updater
│ │
│ ├── scripts/
│ │ └── ota-deploy.sh # Build → zip → upload ke API
│ │
│ ├── capacitor.config.dev.ts # Dev: live reload, OTA off
│ ├── capacitor.config.prod.ts # Prod: OTA on, auto update
│ ├── next.config.ts # Toggle static/server mode
│ ├── wrangler.json # Cloudflare Workers + R2 binding
│ ├── .env.example # Template env vars
│ ├── .dev.vars.example # Template Cloudflare local vars
│ │
│ ├── android/ # Native Android (auto-generated)
│ └── ios/ # Native iOS (auto-generated)
│
└── packages/
└── shared/ # Shared TypeScript types
└── src/
├── index.ts
└── ota.ts # OTAManifest, OTAUpdateRequest, dllDua Mode Build
Satu codebase, dua output:
| Mode | Command | Output | Tujuan |
|------|---------|--------|--------|
| Server | npm run deploy | SSR + API routes | Deploy ke Cloudflare Workers |
| Static | npm run build:cap | HTML/CSS/JS static | Bundle Capacitor (mobile) |
Saat build:cap, folder app/api/ sementara di-rename agar Next.js tidak compile API routes dalam mode static export. Setelah build selesai, folder dikembalikan otomatis.
Commands
# ─── Development ─────────────────────────────────────────
npm run dev # Dev server (pages + API)
npm run dev:ios # Dev + buka Xcode
npm run dev:android # Dev + buka Android Studio
# ─── Build Native ───────────────────────────────────────
npm run build:ios # Build + open Xcode
npm run build:android # Build + open Android Studio
npm run build:cap # Build static saja
# ─── Deploy ─────────────────────────────────────────────
npm run deploy # Deploy ke Cloudflare Workers
# ─── OTA ────────────────────────────────────────────────
npm run ota:deploy # Build + zip + upload OTA bundleSetup Cloudflare
1. Login
npx wrangler login2. Buat R2 Bucket
npx wrangler r2 bucket create <project-name>-ota3. Setup Environment Variables
cd apps/mobile
# Untuk local development
cp .dev.vars.example .dev.vars
# Untuk OTA deploy script
cp .env.example .env.localEdit kedua file:
.dev.vars — Cloudflare bindings lokal:
OTA_API_KEY=your-secret-key-here.env.local — OTA deploy script:
OTA_API_URL=https://app.example.com
OTA_API_KEY=your-secret-key-here4. Deploy
npm run deployOutput: https://<project>.<account>.workers.dev
5. Set Secret di Cloudflare Dashboard
- Workers & Pages → worker kamu → Settings → Variables and Secrets
- Tambah
OTA_API_KEY→ isi value → klik Encrypt
6. Custom Domain (opsional)
- Workers & Pages → worker kamu → Settings → Domains & Routes
- Add Custom Domain → masukkan domain kamu
- Pastikan domain nameserver-nya sudah di Cloudflare
7. Verifikasi
curl https://app.example.com/api/ota/manifest
# → {"latest":{"ios":"0.0.0","android":"0.0.0"},"versions":[]}OTA Update
Push update ke user tanpa submit ulang ke store. Berlaku untuk perubahan JS/HTML/CSS.
Deploy OTA
# 1. Bump version di apps/mobile/package.json
# 2. Jalankan:
OTA_API_URL=https://app.example.com \
OTA_API_KEY=your-secret \
npm run ota:deployScript melakukan: build static → zip → upload ke R2 → update manifest.
Opsi
# Platform tertentu
npm run ota:deploy -- android
npm run ota:deploy -- ios
# Dengan catatan rilis
npm run ota:deploy -- all "Fix bug checkout"
# Force update
npm run ota:deploy -- all "Critical fix" true
# Minimum native version
npm run ota:deploy -- all "New feature" false 10Flow di Device
User buka app
│
▼
OTAProvider → notifyAppReady()
│ (wajib, kalau tidak dipanggil → auto rollback)
▼
Plugin cek /api/ota/update
│
├── Tidak ada update → app jalan normal
│
└── Ada update → download di background
│
▼
User buka app lagi → pakai bundle baru
│
├── notifyAppReady() → berhasil!
└── Crash → auto rollback ke versi sebelumnyaRollback
Otomatis: Kalau bundle baru crash dan notifyAppReady() tidak terpanggil, plugin auto rollback.
Manual:
curl -X POST https://app.example.com/api/ota/rollback \
-H "Authorization: Bearer your-secret" \
-H "Content-Type: application/json" \
-d '{"version": "0.1.0", "platform": "all"}'Cek versi aktif
curl https://app.example.com/api/ota/manifestAPI Endpoints
| Method | Endpoint | Auth | Deskripsi |
|--------|----------|------|-----------|
| POST | /api/ota/update | - | Cek update (dipanggil oleh plugin) |
| GET | /api/ota/download/:version | - | Download bundle zip |
| GET | /api/ota/manifest | - | Lihat semua versi |
| POST | /api/ota/upload | Bearer | Upload bundle baru |
| POST | /api/ota/rollback | Bearer | Rollback ke versi tertentu |
OTA vs Store?
| Perubahan | Deploy via | |-----------|------------| | Fix bug UI, tambah halaman, ubah logic JS, update styling | OTA | | Install plugin native baru, update versi plugin native, ubah appId/permissions | Store |
Prinsip: perubahan JS/HTML/CSS → OTA. Perubahan native code → store.
Environment Variables
| Variable | File | Deskripsi |
|----------|------|-----------|
| OTA_API_KEY | .dev.vars, .env.local, Cloudflare Dashboard | Secret key autentikasi |
| OTA_API_URL | .env.local | URL server |
| CAPACITOR_BUILD | Otomatis | 1 = static export |
Cloudflare Worker Size Limit
| Plan | Limit | Harga | |------|-------|-------| | Free | 3 MB | Gratis | | Paid | 10 MB | $5/bulan |
Yang dihitung hanya server-side code (API routes + SSR). Static assets (CSS, JS client, gambar, font) diserve lewat CDN, tidak masuk hitungan. Bundle OTA disimpan di R2.
Icon & Splash Screen
Project ini tidak menyertakan icon dan splash screen. Generate sendiri:
cd apps/mobile
# Taruh file sumber di assets/:
# icon-only.png (1024x1024, logo saja)
# icon-foreground.png (1024x1024, logo dengan padding)
# icon-background.png (1024x1024, warna background)
# splash.png (2732x2732, splash screen)
# splash-dark.png (2732x2732, splash screen dark mode)
npx @capacitor/assets generateIni auto-generate semua ukuran icon dan splash screen untuk iOS dan Android.
License
MIT
