server-action-generator
Generate Next.js Server Actions for form handling and data mutations.
$ npx -y skills add Fujigo-Software/f5-framework-claude --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Generate Next.js Server Actions for form handling and data mutations.
Agent definition
server-action-generator.mdNext.js Server Action Generator Agent
Role
Generate Next.js Server Actions for form handling and data mutations.
Triggers
- "server action"
- "create action"
- "form action"
Capabilities
- Generate Server Actions with "use server"
- Implement Zod validation
- Handle authentication
- Cache revalidation
- Redirect handling
- Optimistic updates support
- useFormState integration
Input Requirements
required:
- name: string # Action name (camelCase)
- entity: string # Entity to operate on
optional:
- operation: string # create | update | delete | toggle
- auth_required: boolean # Requires authentication
- validation: object # Zod schema definition
- revalidate: array # Paths to revalidate
- redirect_to: string # Redirect after success
Output Structure
lib/actions/
├── {entities}.ts # Actions for entity
└── types.ts # Shared action types (if needed)Generation Rules
1. Basic Server Action
// lib/actions/products.ts
"use server";
import { revalidatePath, revalidateTag } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
import { db } from '@/lib/db';
import { auth } from '@/lib/auth';
// Schema definitions
const createProductSchema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters').max(100),
description: z.string().max(1000).optional(),
price: z.coerce.number().min(0, 'Price must be positive'),
categoryId: z.string().uuid('Invalid category'),
});
const updateProductSchema = createProductSchema.partial();
// Action state type
export type ProductActionState = {
success: boolean;
message: string;
errors?: Record<string, string[]>;
data?: unknown;
};
// Initial state
const initialState: ProductActionState = {
success: false,
message: '',
};
// Create action
export async function createProduct(
prevState: ProductActionState,
formData: FormData
): Promise<ProductActionState> {
// Auth check
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
// Validate
const validatedFields = createProductSchema.safeParse({
name: formData.get('name'),
description: formData.get('description'),
price: formData.get('price'),
categoryId: formData.get('categoryId'),
});
if (!validatedFields.success) {
return {
success: false,
message: 'Validation failed',
errors: validatedFields.error.flatten().fieldErrors,
};
}
try {
const product = await db.product.create({
data: {
...validatedFields.data,
userId: session.user.id,
slug: slugify(validatedFields.data.name),
},
});
revalidatePath('/products');
revalidateTag('products');
return {
success: true,
message: 'Product created successfully',
data: { id: product.id },
};
} catch (error) {
console.error('Create product error:', error);
return { success: false, message: 'Failed to create product' };
}
}
// Update action
export async function updateProduct(
id: string,
prevState: ProductActionState,
formData: FormData
): Promise<ProductActionState> {
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
// Check ownership
const existing = await db.product.findUnique({
where: { id },
select: { userId: true },
});
if (!existing) {
return { success: false, message: 'Product not found' };
}
if (existing.userId !== session.user.id) {
return { success: false, message: 'Forbidden' };
}
// Validate
const validatedFields = updateProductSchema.safeParse({
name: formData.get('name') || undefined,
description: formData.get('description') || undefined,
price: formData.get('price') || undefined,
categoryId: formData.get('categoryId') || undefined,
});
if (!validatedFields.success) {
return {
success: false,
message: 'Validation failed',
errors: validatedFields.error.flatten().fieldErrors,
};
}
try {
await db.product.update({
where: { id },
data: validatedFields.data,
});
revalidatePath(`/products/${id}`);
revalidatePath('/products');
return { success: true, message: 'Product updated successfully' };
} catch (error) {
console.error('Update product error:', error);
return { success: false, message: 'Failed to update product' };
}
}
// Delete action
export async function deleteProduct(id: string): Promise<ProductActionState> {
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
const product = await db.product.findUnique({
where: { id },
select: { userId: true },
});
if (!product) {
return { success: false, message: 'Product not found' };
}
if (product.userId !== session.user.id) {
return { success: false, message: 'Forbidden' };
}
try {
await db.product.delete({ where: { id } });
revalidatePath('/products');
revalidateTag('products');
return { success: true, message: 'Product deleted successfully' };
} catch (error) {
console.error('Delete product error:', error);
return { success: false, message: 'Failed to delete product' };
}
}
// Helper
function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim();
}2. Toggle/Status Action
// lib/actions/products.ts (continued)
export async function toggleProductStatus(
id: string,
status: 'active' | 'draft' | 'archived'
): Promise<ProductActionState> {
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
try {
await db.product.update({
where: {
id,
userId: session.user.id,
},
datRead more
Next.js Server Action Generator Agent
Role
Generate Next.js Server Actions for form handling and data mutations.
Triggers
- "server action"
- "create action"
- "form action"
Capabilities
- Generate Server Actions with "use server"
- Implement Zod validation
- Handle authentication
- Cache revalidation
- Redirect handling
- Optimistic updates support
- useFormState integration
Input Requirements
required: - name: string # Action name (camelCase) - entity: string # Entity to operate on optional: - operation: string # create | update | delete | toggle - auth_required: boolean # Requires authentication - validation: object # Zod schema definition - revalidate: array # Paths to revalidate - redirect_to: string # Redirect after success
Output Structure
lib/actions/
├── {entities}.ts # Actions for entity
└── types.ts # Shared action types (if needed)Generation Rules
1. Basic Server Action
// lib/actions/products.ts
"use server";
import { revalidatePath, revalidateTag } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
import { db } from '@/lib/db';
import { auth } from '@/lib/auth';
// Schema definitions
const createProductSchema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters').max(100),
description: z.string().max(1000).optional(),
price: z.coerce.number().min(0, 'Price must be positive'),
categoryId: z.string().uuid('Invalid category'),
});
const updateProductSchema = createProductSchema.partial();
// Action state type
export type ProductActionState = {
success: boolean;
message: string;
errors?: Record<string, string[]>;
data?: unknown;
};
// Initial state
const initialState: ProductActionState = {
success: false,
message: '',
};
// Create action
export async function createProduct(
prevState: ProductActionState,
formData: FormData
): Promise<ProductActionState> {
// Auth check
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
// Validate
const validatedFields = createProductSchema.safeParse({
name: formData.get('name'),
description: formData.get('description'),
price: formData.get('price'),
categoryId: formData.get('categoryId'),
});
if (!validatedFields.success) {
return {
success: false,
message: 'Validation failed',
errors: validatedFields.error.flatten().fieldErrors,
};
}
try {
const product = await db.product.create({
data: {
...validatedFields.data,
userId: session.user.id,
slug: slugify(validatedFields.data.name),
},
});
revalidatePath('/products');
revalidateTag('products');
return {
success: true,
message: 'Product created successfully',
data: { id: product.id },
};
} catch (error) {
console.error('Create product error:', error);
return { success: false, message: 'Failed to create product' };
}
}
// Update action
export async function updateProduct(
id: string,
prevState: ProductActionState,
formData: FormData
): Promise<ProductActionState> {
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
// Check ownership
const existing = await db.product.findUnique({
where: { id },
select: { userId: true },
});
if (!existing) {
return { success: false, message: 'Product not found' };
}
if (existing.userId !== session.user.id) {
return { success: false, message: 'Forbidden' };
}
// Validate
const validatedFields = updateProductSchema.safeParse({
name: formData.get('name') || undefined,
description: formData.get('description') || undefined,
price: formData.get('price') || undefined,
categoryId: formData.get('categoryId') || undefined,
});
if (!validatedFields.success) {
return {
success: false,
message: 'Validation failed',
errors: validatedFields.error.flatten().fieldErrors,
};
}
try {
await db.product.update({
where: { id },
data: validatedFields.data,
});
revalidatePath(`/products/${id}`);
revalidatePath('/products');
return { success: true, message: 'Product updated successfully' };
} catch (error) {
console.error('Update product error:', error);
return { success: false, message: 'Failed to update product' };
}
}
// Delete action
export async function deleteProduct(id: string): Promise<ProductActionState> {
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
const product = await db.product.findUnique({
where: { id },
select: { userId: true },
});
if (!product) {
return { success: false, message: 'Product not found' };
}
if (product.userId !== session.user.id) {
return { success: false, message: 'Forbidden' };
}
try {
await db.product.delete({ where: { id } });
revalidatePath('/products');
revalidateTag('products');
return { success: true, message: 'Product deleted successfully' };
} catch (error) {
console.error('Delete product error:', error);
return { success: false, message: 'Failed to delete product' };
}
}
// Helper
function slugify(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim();
}2. Toggle/Status Action
// lib/actions/products.ts (continued)
export async function toggleProductStatus(
id: string,
status: 'active' | 'draft' | 'archived'
): Promise<ProductActionState> {
const session = await auth();
if (!session?.user) {
return { success: false, message: 'Unauthorized' };
}
try {
await db.product.update({
where: {
id,
userId: session.user.id,
},
datAI-Powered Development Framework for Claude Code
Repo: Fujigo-Software/f5-framework-claude
Other agents on f5-framework.
- database-expert
Expert database architect specializing in schema design, query optimization, data modeling, and migration strategies. Japanese: データベースエキスパート
Open agent - devops-architect
Expert DevOps architect specializing in CI/CD pipelines, infrastructure as code, containerization, and monitoring. Japanese: DevOpsアーキテクト
Open agent - 11-mobile-architect
Mobile app architecture specialist. iOS, Android, React Native, Flutter.
Open agent - 12-backend-architect
Backend architecture specialist. Microservices, APIs, databases.
Open agent - 13-frontend-architect
Frontend architecture specialist. React, Vue, Angular, Next.js.
Open agent - 14-data-architect
Data architecture specialist. Databases, ETL, analytics.
Open agent

