Skip to content
Back to skills

600 Reference 5c30fc8b

ASecurity

```typescript 'use server';

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
developmenttypescriptapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 600-reference_5c30fc8b --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 600 Reference 5c30fc8b?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 600 Reference 5c30fc8b
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-600-reference-5c30fc8b/badge)](https://www.skillsdirectory.com/skills/tools-only-600-reference-5c30fc8b)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Server Action Reference

## Server Action Pattern

```typescript
'use server';

import { z } from 'zod';
import { revalidatePath } from 'next/cache';

import { createClient } from '@/lib/supabase/server';
import { getSession } from '@/lib/auth';
import { logger } from '@/lib/logger';

import { MySchema } from '../schema/my.schema';
import { createMyService } from './my.service';

export async function myAction(input: z.infer<typeof MySchema>) {
  // 1. Authenticate
  const session = await getSession();
  if (!session) throw new Error('Unauthorized');

  // 2. Validate
  const data = MySchema.parse(input);

  // 3. Execute
  const client = await createClient();
  const service = createMyService(client);
  const result = await service.doSomething(data);

  // 4. Revalidate
  revalidatePath('/home/[account]/my-feature');

  return { success: true, data: result };
}
```

### Action Structure

| Step | Purpose | Required |
|------|---------|----------|
| `getSession()` | Verify authentication | Yes (for protected actions) |
| `Schema.parse(input)` | Validate input with Zod | Yes |
| `createClient()` | Get Supabase client with RLS | Yes |
| `createService(client)` | Instantiate service with client | Yes |
| `revalidatePath()` | Refresh cached data | Yes (after mutations) |

### Handler Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `input` | `z.infer<Schema>` | Raw input data from the client |
| `session.user` | `User` | Authenticated user (from `getSession()`) |

## Route Handler Pattern

```typescript
// app/api/my-feature/route.ts
import { NextRequest, NextResponse } from 'next/server';

import { createClient } from '@/lib/supabase/server';
import { getSession } from '@/lib/auth';

import { MySchema } from '../_lib/schema/my.schema';
import { createMyService } from '../_lib/server/my.service';

export async function POST(request: NextRequest) {
  const session = await getSession();
  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const body = await request.json();
  const data = MySchema.parse(body);

  const client = await createClient();
  const service = createMyService(client);
  const result = await service.doSomething(data);

  return NextResponse.json(result);
}

export async function GET(request: NextRequest) {
  const session = await getSession();
  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const url = new URL(request.url);
  const param = url.searchParams.get('param');

  const client = await createClient();
  const service = createMyService(client);
  const result = await service.get(param);

  return NextResponse.json({ data: result });
}
```

## Common Zod Patterns

```typescript
import { z } from 'zod';

// Basic schema
export const CreateItemSchema = z.object({
  name: z.string().min(1, 'Name is required'),
  description: z.string().optional(),
  accountId: z.string().uuid('Invalid account ID'),
});

// With transforms
export const SearchSchema = z.object({
  query: z.string().trim().min(1),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().min(1).max(100).default(10),
});

// With refinements
export const DateRangeSchema = z.object({
  startDate: z.coerce.date(),
  endDate: z.coerce.date(),
}).refine(
  (data) => data.endDate > data.startDate,
  { message: 'End date must be after start date' }
);

// Enum values
export const StatusSchema = z.object({
  status: z.enum(['active', 'inactive', 'pending']),
});
```

## Revalidation

```typescript
import { revalidatePath } from 'next/cache';
import { revalidateTag } from 'next/cache';

// Revalidate specific path
revalidatePath('/home/[account]/items');

// Revalidate with dynamic segment
revalidatePath(`/home/${accountSlug}/items`);

// Revalidate by tag
revalidateTag('items');
```

## Redirect

```typescript
import { redirect } from 'next/navigation';

// Redirect after action
redirect('/success');

// Redirect with dynamic path
redirect(`/home/${accountSlug}/items/${itemId}`);
```

## Logging

```typescript
import { logger } from '@/lib/logger';

// Context object for all logs
const ctx = {
  name: 'action-name',
  userId: session.user.id,
  accountId: data.accountId,
};

// Log levels
logger.info(ctx, 'Starting operation');
logger.warn({ ...ctx, warning: 'details' }, 'Warning message');
logger.error({ ...ctx, error }, 'Operation failed');
```

## Supabase Clients

```typescript
// Standard client (RLS enforced) - pass to services
import { createClient } from '@/lib/supabase/server';
const client = await createClient();
const service = createFeatureService(client);

// Admin client (bypasses RLS - use sparingly)
import { createAdminClient } from '@/lib/supabase/admin';
const adminClient = await createAdminClient();
```

## Error Handling

```typescript
import { isRedirectError } from 'next/dist/client/components/redirect-error';

try {
  await operation();
  redirect('/success');
} catch (error) {
  if (!isRedirectError(error)) {
    // Handle actual error
    logger.error({ error }, 'Operation failed');
    throw error;
  }
  throw error; // Re-throw redirect
}
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.