Getting Started
Common Workflows

Common Workflows

Learn the most common development workflows in HiveForge.

User Management Workflows

Register a New User

// apps/web/src/lib/auth.ts
import { createClient } from '@/lib/supabase/client'
 
export async function signUp(email: string, password: string) {
  const supabase = createClient()
 
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
    options: {
      emailRedirectTo: `${window.location.origin}/auth/callback`,
    },
  })
 
  if (error) throw error
  return data
}

Update User Profile

export async function updateProfile(userId: string, profile: {
  full_name?: string
  avatar_url?: string
  bio?: string
}) {
  const supabase = createClient()
 
  const { data, error } = await supabase
    .from('profiles')
    .update(profile)
    .eq('id', userId)
    .select()
    .single()
 
  if (error) throw error
  return data
}

Organization Workflows

Create Organization

// Frontend
import { createOrganization } from '@/lib/api/organizations'
 
const org = await createOrganization({
  name: 'Acme Corp',
  slug: 'acme-corp',
  tier: 'pro',
})
# Backend
from app.services.organizations import OrganizationService
 
org = await OrganizationService.create(
    name="Acme Corp",
    slug="acme-corp",
    owner_id=user_id,
    tier="pro"
)

Add Members to Organization

// Invite member
await inviteMember({
  organizationId: org.id,
  email: 'member@example.com',
  role: 'member',
})
 
// Accept invitation
await acceptInvitation({
  invitationId: invitation.id,
})

Switch Active Organization

// Set active organization in session
import { setActiveOrganization } from '@/lib/organizations'
 
await setActiveOrganization(orgId)
// This updates the user's session and redirects to org dashboard

Billing Workflows

Subscribe to Plan

import { createCheckoutSession } from '@/lib/stripe'
 
// Create Stripe checkout session
const session = await createCheckoutSession({
  organizationId: org.id,
  priceId: 'price_pro_monthly',
  successUrl: '/dashboard/billing/success',
  cancelUrl: '/dashboard/billing',
})
 
// Redirect to Stripe Checkout
window.location.href = session.url

Handle Webhook Events

# apps/api/app/routers/billing.py
from fastapi import Request
import stripe
 
@router.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")
 
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
        )
    except ValueError:
        raise HTTPException(status_code=400)
 
    # Handle different event types
    if event.type == "customer.subscription.created":
        await handle_subscription_created(event.data.object)
    elif event.type == "customer.subscription.updated":
        await handle_subscription_updated(event.data.object)
    elif event.type == "customer.subscription.deleted":
        await handle_subscription_deleted(event.data.object)
 
    return {"status": "success"}

Update Subscription

// Change plan
await updateSubscription({
  organizationId: org.id,
  newPriceId: 'price_enterprise_monthly',
})
 
// Cancel subscription
await cancelSubscription({
  organizationId: org.id,
  cancelAtPeriodEnd: true,
})
 
// Reactivate subscription
await reactivateSubscription({
  organizationId: org.id,
})

Permission Workflows

Check User Permissions

// Frontend
import { usePermissions } from '@/hooks/usePermissions'
 
function MyComponent() {
  const { hasPermission, can } = usePermissions()
 
  // Check single permission
  const canEdit = hasPermission('organizations.update')
 
  // Check multiple permissions (OR)
  const canManage = can(['organizations.update', 'organizations.delete'])
 
  return (
    <div>
      {canEdit && <EditButton />}
      {canManage && <DeleteButton />}
    </div>
  )
}
# Backend
from app.services.permissions import check_permission
 
@router.put("/organizations/{org_id}")
async def update_organization(
    org_id: str,
    data: OrganizationUpdate,
    user: User = Depends(get_current_user)
):
    # Check permission
    has_permission = await check_permission(
        user.id, org_id, "organizations.update"
    )
    if not has_permission:
        raise HTTPException(status_code=403, detail="Permission denied")
 
    # Update organization
    org = await OrganizationService.update(org_id, data)
    return org

Assign Roles

// Change user role in organization
await updateMemberRole({
  organizationId: org.id,
  userId: user.id,
  role: 'admin', // owner, admin, member, viewer
})

API Key Workflows

Generate API Key

const apiKey = await createAPIKey({
  organizationId: org.id,
  name: 'Production API Key',
  scopes: ['read', 'write'],
  expiresAt: new Date('2025-12-31'),
})
 
// Save the key securely - it's only shown once!
console.log(apiKey.key) // "hf_live_..."

Use API Key

# Make authenticated requests
curl http://localhost:8000/api/users/me \
  -H "X-API-Key: hf_live_..."

Revoke API Key

await revokeAPIKey({
  organizationId: org.id,
  keyId: apiKey.id,
})

Email Workflows

Send Transactional Email

# Backend
from app.services.email import EmailService
 
await EmailService.send(
    to="user@example.com",
    template="welcome",
    data={
        "user_name": "John Doe",
        "verify_url": "https://app.hiveforge.dev/verify/..."
    }
)

Create Custom Email Template

// apps/web/emails/custom-notification.tsx
import {
  Html,
  Head,
  Body,
  Container,
  Section,
  Text,
  Button,
  Hr,
} from '@react-email/components'
 
interface CustomNotificationProps {
  userName: string
  message: string
  actionUrl: string
  actionText: string
}
 
export default function CustomNotification({
  userName,
  message,
  actionUrl,
  actionText,
}: CustomNotificationProps) {
  return (
    <Html>
      <Head />
      <Body style={main}>
        <Container style={container}>
          <Section>
            <Text style={heading}>Hi {userName},</Text>
            <Text style={paragraph}>{message}</Text>
            <Button style={button} href={actionUrl}>
              {actionText}
            </Button>
          </Section>
          <Hr style={hr} />
          <Text style={footer}>
            HiveForge - Your SaaS Platform
          </Text>
        </Container>
      </Body>
    </Html>
  )
}
 
const main = { backgroundColor: '#f6f9fc', fontFamily: 'sans-serif' }
const container = { backgroundColor: '#ffffff', margin: '0 auto', padding: '20px' }
const heading = { fontSize: '24px', fontWeight: 'bold' }
const paragraph = { fontSize: '16px', lineHeight: '26px' }
const button = { backgroundColor: '#5469d4', color: '#fff', padding: '12px 20px' }
const hr = { borderColor: '#e6ebf1', margin: '20px 0' }
const footer = { color: '#8898aa', fontSize: '12px' }

Database Workflows

Create Migration

# Create new migration file
pnpm db:migration:create add_custom_field
 
# Edit the migration in supabase/migrations/
# Apply migration
pnpm db:migrate

Query with RLS

// Data automatically filtered by RLS policies
const supabase = createClient()
 
// Only returns data user has access to
const { data: orgs } = await supabase
  .from('organizations')
  .select('*')
 
// With specific filters
const { data: members } = await supabase
  .from('organization_members')
  .select('*, profiles(*)')
  .eq('organization_id', orgId)
  .in('role', ['admin', 'owner'])

Seed Development Data

// supabase/seed.sql or custom seed script
import { createClient } from '@supabase/supabase-js'
 
async function seed() {
  const supabase = createClient(url, serviceKey)
 
  // Create test organization
  const { data: org } = await supabase
    .from('organizations')
    .insert({
      name: 'Test Org',
      slug: 'test-org',
      tier: 'pro',
    })
    .select()
    .single()
 
  // Create test users
  for (let i = 1; i <= 5; i++) {
    await supabase.auth.admin.createUser({
      email: `user${i}@test.com`,
      password: 'test123',
      email_confirm: true,
    })
  }
}

AI Integration Workflows

Generate Content

import { generateText } from '@/lib/ai'
 
const description = await generateText({
  prompt: 'Write a product description for a project management tool',
  maxTokens: 200,
  temperature: 0.7,
})
# Backend
from app.services.ai import AIService
 
result = await AIService.generate(
    prompt="Write a product description for a project management tool",
    max_tokens=200,
    temperature=0.7
)

Stream AI Response

// Frontend with streaming
const stream = await fetch('/api/ai/generate', {
  method: 'POST',
  body: JSON.stringify({ prompt, stream: true }),
})
 
const reader = stream.body.getReader()
const decoder = new TextDecoder()
 
while (true) {
  const { done, value } = await reader.read()
  if (done) break
 
  const chunk = decoder.decode(value)
  // Update UI with chunk
  setGeneratedText(prev => prev + chunk)
}

Webhook Workflows

Register Webhook

const webhook = await createWebhook({
  organizationId: org.id,
  url: 'https://api.example.com/webhooks',
  events: [
    'organization.member.added',
    'subscription.updated',
    'api_key.created',
  ],
  secret: 'whsec_...',
})

Receive Webhook

// Your webhook endpoint
import crypto from 'crypto'
 
export async function POST(request: Request) {
  const payload = await request.text()
  const signature = request.headers.get('x-hiveforge-signature')
 
  // Verify signature
  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(payload)
    .digest('hex')
 
  if (signature !== expectedSignature) {
    return new Response('Invalid signature', { status: 401 })
  }
 
  // Process event
  const event = JSON.parse(payload)
 
  switch (event.type) {
    case 'organization.member.added':
      await handleMemberAdded(event.data)
      break
    case 'subscription.updated':
      await handleSubscriptionUpdated(event.data)
      break
  }
 
  return new Response('OK')
}

Testing Workflows

Write Component Tests

// apps/web/src/components/__tests__/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from '../Button'
 
describe('Button', () => {
  it('renders with text', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })
 
  it('calls onClick when clicked', () => {
    const onClick = jest.fn()
    render(<Button onClick={onClick}>Click me</Button>)
 
    fireEvent.click(screen.getByText('Click me'))
    expect(onClick).toHaveBeenCalledTimes(1)
  })
})

Write API Tests

# apps/api/tests/test_organizations.py
import pytest
from httpx import AsyncClient
 
@pytest.mark.asyncio
async def test_create_organization(client: AsyncClient, auth_headers):
    response = await client.post(
        "/api/organizations",
        headers=auth_headers,
        json={
            "name": "Test Org",
            "slug": "test-org",
            "tier": "pro"
        }
    )
 
    assert response.status_code == 201
    data = response.json()
    assert data["name"] == "Test Org"
    assert data["slug"] == "test-org"
 
@pytest.mark.asyncio
async def test_list_organizations(client: AsyncClient, auth_headers):
    response = await client.get(
        "/api/organizations",
        headers=auth_headers
    )
 
    assert response.status_code == 200
    data = response.json()
    assert isinstance(data, list)

E2E Tests

// e2e/auth.spec.ts
import { test, expect } from '@playwright/test'
 
test('user can sign up and login', async ({ page }) => {
  // Sign up
  await page.goto('/auth/signup')
  await page.fill('[name="email"]', 'test@example.com')
  await page.fill('[name="password"]', 'password123')
  await page.click('button[type="submit"]')
 
  // Verify redirect
  await expect(page).toHaveURL('/dashboard')
 
  // Check user is logged in
  await expect(page.locator('text=Dashboard')).toBeVisible()
})

Deployment Workflows

Deploy Frontend

# Build and deploy to Netlify
pnpm build:web
netlify deploy --prod --dir=apps/web/out
 
# Or use Git integration for auto-deploy
git push origin main

Deploy Backend

# Deploy to Railway
railway up
 
# Or use Docker
docker build -t hiveforge-api -f apps/api/Dockerfile .
docker push your-registry/hiveforge-api:latest

Run Migrations in Production

# Using Supabase CLI
supabase db push --project-id your-project-id
 
# Or via API migration endpoint
curl -X POST https://api.hiveforge.dev/admin/migrate \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Monitoring Workflows

View Logs

# Frontend logs (Netlify)
netlify logs --prod
 
# Backend logs (Railway)
railway logs
 
# Or use monitoring dashboard

Track Errors

// apps/web/src/lib/sentry.ts
import * as Sentry from '@sentry/nextjs'
 
Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 1.0,
})
 
// Capture custom error
try {
  await riskyOperation()
} catch (error) {
  Sentry.captureException(error, {
    tags: { section: 'billing' },
    extra: { organizationId: org.id },
  })
}

Next Steps