Author: Creator of Blend Design System
Purpose: Full infrastructure deployment + NPM publishing guide
Last Updated: April 21, 2026
- Architecture Overview
- Prerequisites
- Phase 1: Firebase Setup
- Phase 2: GCP Cloud SQL (PostgreSQL)
- Phase 3: Backend Deployment (Cloud Run)
- Phase 4: Frontend Deployment (Firebase Hosting)
- Phase 5: NPM Publishing
- Configuration Files
- Database Operations
- Troubleshooting
- Quick Reference
Internet
│
├────────────────────────────────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ Firebase Hosting │ │ Cloud Run │
│ (Studio Frontend)│ │ (Backend API) │
│ │ │ │
│ /studio/* → SPA │ │ /api/* endpoints │
│ /api/* → rewrite │◄────────────────│ Express + Prisma │
│ to Cloud Run │ └──────────┬───────────┘
└──────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Secret Manager │ │
│ │ • DB password │ │
│ │ • JWT secrets │ │
│ │ • Firebase private key │ │
│ └─────────────────────────────────┘ │
│ ▼
│ ┌──────────────────────┐
│ │ Cloud SQL │
│ │ (PostgreSQL 16) │
│ │ │
│ │ • Users, Teams │
│ │ • Branches, Versions │
│ │ • Audit Logs │
│ └──────────────────────┘
│
▼
┌──────────────────┐
│ Firebase │
│ • Auth (Google) │
│ • Firestore │
│ (brand JSON) │
└──────────────────┘
Data Split:
- PostgreSQL: Users, teams, roles, relational data, audit logs
- Firestore: Brand configs (JSON blobs), snapshots, versions
- Secret Manager: All secrets (DB passwords, JWT, Firebase keys)
- Node.js 18+, pnpm 10+
- gcloud CLI installed and authenticated (
gcloud auth login) - Firebase CLI:
npm install -g firebase-tools - npm account with @juspay scope access
- A GCP project with billing enabled
- Go to Firebase Console
- Click Create a project
- Name:
blend-studio-prod(or your choice) - Enable Google Analytics (optional)
- Note the Project ID (e.g.,
blend-studio-prod)
- Go to Authentication → Sign-in method
- Click Google
- Toggle Enable
- Add authorized domains:
localhost:3000(dev)localhost:5173(dev)studio.blend.juspay.design(production)blend-studio-prod.web.app(Firebase default)
- Click Save
- Go to Google Cloud Console → APIs & Services → Credentials
- Find your OAuth 2.0 Client ID (auto-created by Firebase)
- Add authorized origins:
http://localhost:3000 http://localhost:5173 https://studio.blend.juspay.design https://blend-studio-prod.web.app - Add redirect URIs:
http://localhost:3000/__/auth/handler https://studio.blend.juspay.design/__/auth/handler https://blend-studio-prod.web.app/__/auth/handler
- Go to Firestore Database
- Click Create database
- Select Start in production mode
- Choose location:
us-central1 - Deploy rules:
firebase deploy --only firestore:rules
- Go to Project Settings (gear icon)
- Scroll to Your apps
- Click Web (
</>) - Nickname:
blend-studio-web - Click Register
- Copy the firebaseConfig — you'll need this for environment variables
- Go to Project Settings → Service accounts
- Click Generate new private key
- Save JSON file securely (never commit!)
- You'll need:
project_id,client_email,private_key
gcloud services enable \
run.googleapis.com \
sqladmin.googleapis.com \
secretmanager.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
iam.googleapis.com# Create instance
gcloud sql instances create blend-db \
--database-version=POSTGRES_16 \
--tier=db-f1-micro \
--region=us-central1 \
--storage-auto-increase
# Set admin password
gcloud sql users set-password admin \
--instance=blend-db \
--password="YOUR_STRONG_PASSWORD_HERE"
# Create database
gcloud sql databases create blend_studio \
--instance=blend-db
# Get connection name
gcloud sql instances describe blend-db --format="value(connectionName)"
# Output: YOUR_PROJECT:us-central1:blend-db# Database password
echo -n "YOUR_STRONG_PASSWORD_HERE" | \
gcloud secrets create blend-backend-db-password --data-file=-
# JWT secret
openssl rand -base64 48 | \
gcloud secrets create blend-backend-jwt-secret --data-file=-
# JWT refresh secret
openssl rand -base64 48 | \
gcloud secrets create blend-backend-jwt-refresh-secret --data-file=-
# Firebase private key (paste the actual key content)
echo -n "-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQE...
-----END PRIVATE KEY-----" | \
gcloud secrets create blend-backend-firebase-key --data-file=-# Create service account
gcloud iam service-accounts create blend-backend-sa \
--display-name="Blend Backend Service Account"
# Grant Cloud SQL access
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:blend-backend-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
# Grant Secret Manager access
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
--member="serviceAccount:blend-backend-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"Create apps/backend/.env.production:
NODE_ENV=production
PORT=3001
# Cloud SQL
INSTANCE_CONNECTION_NAME=YOUR_PROJECT:us-central1:blend-db
DATABASE_NAME=blend_studio
DATABASE_USER=admin
# DATABASE_PASSWORD comes from Secret Manager
# JWT
# JWT_SECRET and JWT_REFRESH_SECRET come from Secret Manager
# Google OAuth
GOOGLE_CLIENT_ID=your_client_id_from_cloud_console
GOOGLE_CLIENT_SECRET=your_client_secret
# Frontend URL for CORS
FRONTEND_URL=https://studio.blend.juspay.design
# Firebase Admin
FIREBASE_PROJECT_ID=YOUR_PROJECT_ID
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxxxx@YOUR_PROJECT_ID.iam.gserviceaccount.com
# FIREBASE_PRIVATE_KEY comes from Secret Manager# Build and push container
gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/blend-backend
# Deploy to Cloud Run
gcloud run deploy blend-backend \
--image gcr.io/YOUR_PROJECT_ID/blend-backend:latest \
--region us-central1 \
--platform managed \
--no-allow-unauthenticated \
--add-cloudsql-instances YOUR_PROJECT:us-central1:blend-db \
--set-env-vars "NODE_ENV=production,PORT=3001,INSTANCE_CONNECTION_NAME=YOUR_PROJECT:us-central1:blend-db,DATABASE_NAME=blend_studio,DATABASE_USER=admin,FRONTEND_URL=https://studio.blend.juspay.design,FIREBASE_PROJECT_ID=YOUR_PROJECT_ID,FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxxxx@YOUR_PROJECT_ID.iam.gserviceaccount.com,GOOGLE_CLIENT_ID=your_client_id,GOOGLE_CLIENT_SECRET=your_client_secret" \
--set-secrets "DATABASE_PASSWORD=blend-backend-db-password:latest,JWT_SECRET=blend-backend-jwt-secret:latest,JWT_REFRESH_SECRET=blend-backend-jwt-refresh-secret:latest,FIREBASE_PRIVATE_KEY=blend-backend-firebase-key:latest" \
--memory 512Mi \
--cpu 1 \
--min-instances 0 \
--max-instances 10 \
--service-account blend-backend-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com
# Get the deployed URL
gcloud run services describe blend-backend \
--region us-central1 \
--format="value(status.url)"
# Output: https://blend-backend-xxxxx-uc.a.run.appOption A: Via Cloud Run Job
# Temporary: Run migration as startup command
gcloud run services update blend-backend \
--region us-central1 \
--command "sh" \
--args "-c,npx prisma migrate deploy && node dist/server.js"Option B: Via Cloud SQL Proxy (Local)
# Download proxy
curl -o cloud_sql_proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2/cloud-sql-proxy.linux.amd64
chmod +x cloud_sql_proxy
# Start proxy
./cloud_sql_proxy YOUR_PROJECT:us-central1:blend-db --port 5433 &
# Run migrations
cd apps/backend
DATABASE_URL="postgresql://admin:YOUR_PASSWORD@localhost:5433/blend_studio" \
npx prisma migrate deploy# Test health endpoint
curl https://blend-backend-xxxxx-uc.a.run.app/health
# Expected: {"status":"ok","timestamp":"...","version":"0.1.0"}Create apps/blend-studio/.env.production:
# Firebase Client Config
VITE_FIREBASE_API_KEY=AIzaSyxxxxxxxxxxxxxxxxxxx
VITE_FIREBASE_AUTH_DOMAIN=blend-studio-prod.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=blend-studio-prod
VITE_FIREBASE_STORAGE_BUCKET=blend-studio-prod.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=123456789012
VITE_FIREBASE_APP_ID=1:123456789012:web:xxxxxxxxxxxx
# API Base URL
# Leave empty when using Firebase Hosting rewrites (recommended)
VITE_API_BASE_URL=
# Disable mock data
VITE_USE_MOCK_DATA=falseCreate/update firebase.json in project root:
{
"hosting": {
"public": "apps/blend-studio/dist",
"ignore": ["firebase.json", "**/node_modules/**"],
"rewrites": [
{
"source": "/api/**",
"run": {
"serviceId": "blend-backend",
"region": "us-central1"
}
},
{
"source": "/studio/**",
"destination": "/studio/index.html"
}
],
"headers": [
{
"source": "/studio/**/*.{js,css,svg,png,jpg,woff2}",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "/studio/**/*.html",
"headers": [
{
"key": "Cache-Control",
"value": "no-cache"
}
]
}
]
}
}Create .firebaserc:
{
"projects": {
"production": "YOUR_PROJECT_ID",
"staging": "YOUR_STAGING_PROJECT_ID"
}
}cd apps/blend-studio
# Install dependencies
pnpm install
# Build for production
pnpm build
# Verify dist folder
ls -la dist/# Deploy to production
firebase deploy --only hosting --project production
# Or deploy to staging
firebase deploy --only hosting --project staging
# Your app will be at:
# https://YOUR_PROJECT_ID.web.app
# https://YOUR_PROJECT_ID.firebaseapp.com- Go to Firebase Console → Hosting
- Click Add custom domain
- Enter:
studio.blend.juspay.design - Follow DNS verification steps
- Wait for SSL provisioning (< 1 hour)
# Login to npm
npm login
# Verify access
npm whoami
npm access list packages @juspay# Build CLI
cd packages/cli
pnpm build
# Verify binary
node dist/index.js --help
# Publish
npm publish --access public
# Verify
npm view blend-studio
# Test global install
npm install -g blend-studio
blend-studio --versionNote: The CLI depends on @juspay/blend-design-system which already includes the token engine at @juspay/blend-design-system/tokens.
# Version bump (root)
pnpm version:blend patch # or minor/major
# Build
pnpm -w run build:blend
# Dry run
pnpm -w run publish:blend:dry
# Publish
pnpm -w run publish:blend
# Or manually:
cd packages/blend
pnpm publish --access publicimport { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig(({ mode }) => ({
plugins: [react()],
base: mode === 'production' ? '/studio/' : '/',
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
build: {
outDir: 'dist',
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
},
},
},
},
}))See apps/blend-studio/database/SCHEMA.md for complete database schema documentation.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Allow authenticated users to read branches
match /branches/{branchId} {
allow read: if request.auth != null;
allow write: if request.auth != null &&
resource.data.owner == request.auth.uid;
}
// Versions are immutable
match /branches/{branchId}/versions/{versionId} {
allow read: if request.auth != null;
allow write: if false; // Immutable after creation
}
}
}
# Create local database
createdb blend_studio
# Set environment
export DATABASE_URL=postgresql://postgres:password@localhost:5432/blend_studio
# Run migrations
cd apps/blend-studio
pnpm db:generate
pnpm db:migrate
# Seed database
pnpm db:seed# Via Cloud SQL Proxy
./cloud_sql_proxy YOUR_PROJECT:us-central1:blend-db --port 5433 &
cd apps/backend
DATABASE_URL="postgresql://admin:PASSWORD@localhost:5433/blend_studio" \
npx prisma migrate deploy# Export
gcloud sql export sql blend-db gs://YOUR_BUCKET/backup-$(date +%Y%m%d).sql
# Import
gcloud sql import sql blend-db gs://YOUR_BUCKET/backup-20240421.sql# Cause: Firestore rules not deployed
firebase deploy --only firestore:rules# Cause: FRONTEND_URL mismatch
# Fix: Use Firebase Hosting rewrites instead of CORS
# Or update FRONTEND_URL
gcloud run services update blend-backend \
--region us-central1 \
--set-env-vars "FRONTEND_URL=https://studio.blend.juspay.design"# Verify instance exists
gcloud sql instances describe blend-db
# Check service account permissions
gcloud projects get-iam-policy YOUR_PROJECT_ID \
--flatten="bindings[].members" \
--format='table(bindings.role)' \
--filter="bindings.members:blend-backend-sa"
# Verify Cloud SQL instance attached to Cloud Run
gcloud run services describe blend-backend --region us-central1# Reset migrations (dev only)
npx prisma migrate reset
# Or mark as applied
npx prisma migrate resolve --applied 20240101_migration_name# Check if logged in
npm whoami
# Check permissions
npm access list packages @juspay
# May need organization admin to add you# Clean and rebuild
rm -rf node_modules dist
pnpm install
pnpm build| Variable | Required | Description |
|---|---|---|
VITE_FIREBASE_API_KEY |
Yes | Firebase public API key |
VITE_FIREBASE_AUTH_DOMAIN |
Yes | e.g., project.firebaseapp.com |
VITE_FIREBASE_PROJECT_ID |
Yes | Firebase project ID |
VITE_FIREBASE_STORAGE_BUCKET |
No | Cloud Storage bucket |
VITE_FIREBASE_MESSAGING_SENDER_ID |
No | Push notifications |
VITE_FIREBASE_APP_ID |
Yes | Firebase web app ID |
VITE_API_BASE_URL |
No | Backend URL (empty for Firebase rewrites) |
VITE_USE_MOCK_DATA |
No | Set true to skip Firebase |
| Variable | Required | Description |
|---|---|---|
INSTANCE_CONNECTION_NAME |
Yes | Cloud SQL connection name |
DATABASE_NAME |
Yes | Database name |
DATABASE_USER |
Yes | Database username |
DATABASE_PASSWORD |
Yes | From Secret Manager |
JWT_SECRET |
Yes | From Secret Manager |
JWT_REFRESH_SECRET |
Yes | From Secret Manager |
GOOGLE_CLIENT_ID |
Yes | OAuth client ID |
GOOGLE_CLIENT_SECRET |
Yes | OAuth client secret |
FRONTEND_URL |
Yes | Allowed CORS origin |
FIREBASE_PROJECT_ID |
Yes | Firebase project ID |
FIREBASE_CLIENT_EMAIL |
Yes | Service account email |
FIREBASE_PRIVATE_KEY |
Yes | From Secret Manager |
# === NPM ===
cd packages/cli && npm publish --access public # Publish CLI
pnpm -w run publish:blend # Publish components- Firebase project created and configured
- Google OAuth credentials set up
- Firestore database enabled
- Cloud SQL instance created
- Secrets stored in Secret Manager
- Service account created with proper IAM roles
- Environment files created (.env.production)
- Backend built and deployed to Cloud Run
- Database migrations run successfully
- Backend health check passes
- Frontend built successfully
- Firebase hosting configured with rewrites
- Frontend deployed to Firebase Hosting
- Custom domain configured (if applicable)
- Authentication works (Google Sign-in)
- Brand creation works
- Token generation works
- Database connections stable
- Monitoring/alerting configured (optional)
- CLI dependencies updated (depends on
@juspay/blend-design-system) - CLI built and tested
- CLI published to NPM
- Components version bumped
- Components built and published
-
Monitor: Set up Cloud Monitoring alerts for:
- High error rates in Cloud Run
- Database connection limits
- Firebase Auth anomalies
-
Scale: Adjust Cloud Run min/max instances based on traffic
-
Backup: Automate daily database backups via Cloud Scheduler
-
Security: Rotate secrets every 90 days
Questions? Check the troubleshooting section or refer to:
apps/blend-studio/database/SCHEMA.md— Database schemapackages/cli/README.md— CLI documentationpackages/blend/README.md— Component library documentation