> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auction-rise.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding Features

> Directory conventions, migration naming, and a step-by-step example for extending the template.

# Adding Features

This guide shows how to add new features following the conventions used throughout the codebase.

## Directory Conventions

| Layer          | Location                       | Contains                    |
| -------------- | ------------------------------ | --------------------------- |
| Pages          | `app/(platform)/your-feature/` | Next.js App Router pages    |
| API routes     | `app/api/your-feature/`        | Route handlers              |
| Server actions | `lib/your-feature/actions.ts`  | `"use server"` functions    |
| Types          | `lib/your-feature/types.ts`    | TypeScript types            |
| Context/hooks  | `lib/your-feature/context.tsx` | React context + hooks       |
| UI components  | `components/your-feature/`     | Feature-specific components |
| Migrations     | `supabase/migrations/`         | Timestamped SQL files       |

## Migration Naming

Use a timestamp prefix that puts your migrations after the template's own migrations:

```
supabase/migrations/20260401000000_create_projects.sql
```

The template's migrations use dates in the `2026XXXX` range. Use a date after the latest existing migration to ensure correct ordering.

## Example: Adding a "Projects" Feature

<Steps>
  <Step title="Create the migration">
    ```sql theme={null}
    -- supabase/migrations/20260401000000_create_projects.sql
    CREATE TABLE projects (
      id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
      workspace_id uuid NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
      name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 100),
      description text,
      created_by uuid REFERENCES auth.users(id),
      created_at timestamptz DEFAULT now(),
      updated_at timestamptz DEFAULT now()
    );

    ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

    CREATE POLICY "workspace members can access projects"
      ON projects FOR ALL
      USING (is_workspace_member(workspace_id));
    ```
  </Step>

  <Step title="Generate updated TypeScript types">
    ```bash theme={null}
    pnpm run types:generate
    ```

    This regenerates `types/database.types.ts` from your Supabase schema.
  </Step>

  <Step title="Create server actions">
    ```typescript theme={null}
    // lib/projects/actions.ts
    "use server";

    import { createServerClient } from "@/lib/supabase/server";

    export async function createProject(workspaceId: string, name: string) {
      const supabase = await createServerClient();
      const { data: { user } } = await supabase.auth.getUser();
      if (!user) return { error: "Not authenticated" };

      const { data, error } = await supabase
        .from("projects")
        .insert({ workspace_id: workspaceId, name, created_by: user.id })
        .select()
        .single();

      if (error) return { error: error.message };
      return { project: data };
    }
    ```
  </Step>

  <Step title="Add a page">
    ```typescript theme={null}
    // app/(platform)/projects/page.tsx
    import { createServerClient } from "@/lib/supabase/server";
    import { PageContainer } from "@/components/ui/page-container";

    export default async function ProjectsPage() {
      const supabase = await createServerClient();
      const { data: projects } = await supabase
        .from("projects")
        .select("*")
        .order("created_at", { ascending: false });

      return (
        <PageContainer size="default">
          <h1>Projects</h1>
          {/* render projects */}
        </PageContainer>
      );
    }
    ```
  </Step>

  <Step title="Add to sidebar navigation">
    ```typescript theme={null}
    // saas.config.ts
    import { FolderOpen } from "lucide-react";

    sidebar: {
      navItems: [
        { href: "/projects", label: "Projects", icon: <FolderOpen /> },
      ],
    },
    ```
  </Step>
</Steps>

## Caching Considerations

For workspace-scoped data protected by RLS, **do not** use `unstable_cache()`. It caches at the process level and bypasses RLS on cache hits, which can leak data across users.

Instead, use React `cache()` for request-scoped deduplication:

```typescript theme={null}
import { cache } from "react";

export const cachedGetProject = cache(async (id: string) => {
  const supabase = await createServerClient();
  const { data } = await supabase.from("projects").select("*").eq("id", id).single();
  return data;
});
```

For cache invalidation after mutations, use `revalidateTag()` with tag constants in `lib/cache/tags.ts`.

## Pulling Template Updates

If the template receives upstream fixes, use `sync-upstream.sh` to pull changes into your fork while preserving your domain code:

```bash theme={null}
./sync-upstream.sh
```

The script merges template changes onto a separate branch so you can review diffs before applying them to your main branch.
