Claude
Skills
Sign in
Back

backend-agent

Included with Lifetime
$97 forever

Handles backend/API/database work for Unite-Hub. Implements Next.js API routes, Supabase database operations, RLS policies, authentication, and third-party integrations (Gmail, Stripe).

Web Dev

What this skill does


# Backend Agent Skill

## Overview

The Backend Agent is responsible for all server-side work in Unite-Hub:
1. **Next.js API route development** (serverless functions)
2. **Supabase database operations** (queries, mutations, RLS)
3. **Authentication and authorization** (NextAuth.js, Supabase Auth)
4. **Third-party integrations** (Gmail API, Stripe, Claude AI)
5. **Database schema management** (migrations, indexes)
6. **API security and performance** (rate limiting, caching)

## How to Use This Agent

### Trigger

User says: "Create new API endpoint", "Fix database query", "Update RLS policies", "Implement Gmail integration"

### What the Agent Does

#### 1. Understand the Request

**Questions to Ask**:
- What's the API endpoint purpose?
- What database tables are involved?
- What's the expected input/output format?
- What authentication is required?
- What's the priority (P0/P1/P2)?

#### 2. Analyze Current Implementation

**Step A: Locate Files**
```bash
# Find API routes
find src/app/api -name "route.ts" | grep -i "contacts"

# Find database utilities
find src/lib -name "*.ts" | grep -i "db"
```

**Step B: Read Current Code**
```typescript
// Use text_editor tool
text_editor.view("src/app/api/contacts/route.ts")
text_editor.view("src/lib/db.ts")
```

**Step C: Identify Dependencies**
- What database tables are queried?
- What authentication is required?
- What external APIs are called?
- What error handling exists?

#### 3. Implement Changes

**Step A: Create API Route**

All API routes in Unite-Hub follow this pattern:

```typescript
// src/app/api/example/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase";

export async function POST(request: NextRequest) {
  try {
    // 1. Parse request body
    const body = await request.json();
    const { workspaceId, action, ...params } = body;

    // 2. Validate input
    if (!workspaceId) {
      return NextResponse.json(
        { error: "workspaceId is required" },
        { status: 400 }
      );
    }

    // 3. Get Supabase client
    const supabase = createClient();

    // 4. Check authentication (if needed)
    const { data: { user }, error: authError } = await supabase.auth.getUser();
    if (authError || !user) {
      return NextResponse.json(
        { error: "Unauthorized" },
        { status: 401 }
      );
    }

    // 5. Perform database operation
    const { data, error } = await supabase
      .from("contacts")
      .select("*")
      .eq("workspace_id", workspaceId)  // CRITICAL: Workspace filter
      .eq("organization_id", user.organization_id);  // CRITICAL: Org filter

    if (error) {
      console.error("Database error:", error);
      return NextResponse.json(
        { error: "Database query failed" },
        { status: 500 }
      );
    }

    // 6. Return success response
    return NextResponse.json({
      success: true,
      data,
      count: data.length
    });

  } catch (error) {
    console.error("API error:", error);
    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 }
    );
  }
}

// Support OPTIONS for CORS
export async function OPTIONS(request: NextRequest) {
  return NextResponse.json({}, { status: 200 });
}
```

**Step B: Database Operations**

All database operations MUST use workspace filtering:

```typescript
// ❌ BAD - No workspace filter
const { data } = await supabase
  .from("contacts")
  .select("*");

// ✅ GOOD - Workspace filtered
const { data } = await supabase
  .from("contacts")
  .select("*")
  .eq("workspace_id", workspaceId)
  .eq("organization_id", orgId);
```

**Required filters for data isolation**:
- `.eq("workspace_id", workspaceId)` - Workspace scope
- `.eq("organization_id", orgId)` - Organization scope (top-level)

**Step C: Update `src/lib/db.ts` Wrapper**

The `db.ts` wrapper provides consistent database access:

```typescript
// src/lib/db.ts
import { createClient } from "@/lib/supabase";

export const db = {
  contacts: {
    async listByWorkspace(workspaceId: string) {
      const supabase = createClient();
      const { data, error } = await supabase
        .from("contacts")
        .select("*")
        .eq("workspace_id", workspaceId)
        .order("created_at", { ascending: false });

      if (error) throw error;
      return data || [];
    },

    async getById(contactId: string, workspaceId: string) {
      const supabase = createClient();
      const { data, error } = await supabase
        .from("contacts")
        .select("*")
        .eq("id", contactId)
        .eq("workspace_id", workspaceId)
        .single();

      if (error) throw error;
      return data;
    },

    async create(contact: ContactInput, workspaceId: string) {
      const supabase = createClient();
      const { data, error } = await supabase
        .from("contacts")
        .insert([{ ...contact, workspace_id: workspaceId }])
        .select()
        .single();

      if (error) throw error;
      return data;
    },

    async update(contactId: string, updates: Partial<ContactInput>, workspaceId: string) {
      const supabase = createClient();
      const { data, error } = await supabase
        .from("contacts")
        .update(updates)
        .eq("id", contactId)
        .eq("workspace_id", workspaceId)
        .select()
        .single();

      if (error) throw error;
      return data;
    }
  },

  // Similar patterns for campaigns, emails, etc.
};
```

**CRITICAL FIX for V1**: Add missing import in `src/lib/db.ts:58`

```typescript
// Line 1 - Add import
import { createClient, getSupabaseServer } from "./supabase";

// Line 58 - Fix usage
const supabaseServer = getSupabaseServer();
const { data: workspace, error } = await supabaseServer
  .from("workspaces")
  .select("*")
  .eq("id", workspaceId)
  .single();
```

#### 4. Implement Authentication

**Pattern 1: Client-Side Auth (Browser)**

```typescript
import { createClient } from "@/lib/supabase";

export async function GET(request: NextRequest) {
  const supabase = createClient();

  const { data: { user }, error } = await supabase.auth.getUser();

  if (error || !user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  // User is authenticated, proceed
}
```

**Pattern 2: Server-Side Auth (API Routes)**

```typescript
import { getSupabaseServer } from "@/lib/supabase";

export async function POST(request: NextRequest) {
  const supabase = getSupabaseServer();

  const { data: { session }, error } = await supabase.auth.getSession();

  if (error || !session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  // Session is valid, proceed
}
```

**CRITICAL for V1**: Re-enable authentication on all API routes

Many routes currently have:
```typescript
// TODO: Re-enable authentication in production
// const { auth } = await import("@/lib/auth");
// const session = await auth();
```

**Action Required**: Remove TODO comments and re-enable auth checks.

#### 5. Row Level Security (RLS) Policies

All Supabase tables MUST have RLS policies:

```sql
-- Enable RLS on table
ALTER TABLE contacts ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only see contacts in their workspace
CREATE POLICY "Users can view workspace contacts"
ON contacts
FOR SELECT
USING (
  workspace_id IN (
    SELECT w.id
    FROM workspaces w
    JOIN user_organizations uo ON uo.organization_id = w.organization_id
    WHERE uo.user_id = auth.uid()
  )
);

-- Policy: Users can insert contacts in their workspace
CREATE POLICY "Users can create workspace contacts"
ON contacts
FOR INSERT
WITH CHECK (
  workspace_id IN (
    SELECT w.id
    FROM workspaces w
    JOIN user_organizations uo ON uo.organization_id = w.organization_id
    WHERE uo.user_id = auth.uid()
  )
);

-- Policy: Users can update contacts in their workspace
CREATE POLICY "Users can update workspace contacts"
ON contacts
FOR UPDATE
USING (
  workspace_id IN (
    SELECT w.id
    FROM wo

Related in Web Dev