Claude Code Plugins

Community-maintained marketplace

Feedback

Cria Server Actions com autenticação, validação Zod e operações de banco de dados usando Drizzle ORM. Use quando precisar criar ou modificar Server Actions seguindo padrões de segurança Bewear.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name create-server-action
description Cria Server Actions com autenticação, validação Zod e operações de banco de dados usando Drizzle ORM. Use quando precisar criar ou modificar Server Actions seguindo padrões de segurança Bewear.
tools Read, Write, Edit, Glob, Grep, Bash

Create Server Action Skill

Esta skill cria Server Actions seguindo os padrões de segurança e arquitetura do projeto Bewear.

Quando Usar

  • Criar novas Server Actions
  • Adicionar operações de banco de dados
  • Implementar lógica de negócio no servidor
  • Modificar Server Actions existentes

Regras Obrigatórias

1. Estrutura de Arquivos

SEMPRE crie dois arquivos na pasta src/actions/nome-action/:

src/actions/add-cart-product/
├── index.ts     (Server Action)
└── schema.ts    (Validação Zod)

Arquivo de referência obrigatório: src/actions/add-cart-product/

2. schema.ts - Validação

import { z } from "zod";

// 1. Definir schema Zod
export const addCartProductSchema = z.object({
  productVariantId: z.uuid(),
  quantity: z.number().min(1),
});

// 2. Exportar tipo inferido
export type AddCartProductSchema = z.infer<typeof addCartProductSchema>;

3. index.ts - Server Action

Template obrigatório com verificação de segurança:

"use server";

import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { revalidatePath } from "next/cache";
import { nomeSchema, NomeSchema } from "./schema";
import { db } from "@/db";
import { nomeTable } from "@/db/schema";
import { eq } from "drizzle-orm";

export async function nomeAction(data: NomeSchema) {
  // 1. VALIDAR DADOS (obrigatório)
  nomeSchema.parse(data);

  // 2. VERIFICAR AUTENTICAÇÃO (obrigatório)
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session?.user) {
    throw new Error("Unauthorized");
  }

  // 3. OPERAÇÕES DE BANCO DE DADOS
  const result = await db.query.nomeTable.findFirst({
    where: eq(nomeTable.userId, session.user.id),
  });

  if (!result) {
    throw new Error("Resource not found");
  }

  // 4. VALIDAR OWNERSHIP (quando necessário)
  if (result.userId !== session.user.id) {
    throw new Error("Unauthorized");
  }

  // 5. EXECUTAR LÓGICA
  await db.insert(nomeTable).values({
    userId: session.user.id,
  });

  // 6. REVALIDAR CACHE (se necessário)
  revalidatePath("/route");

  // 7. RETORNAR RESULTADO
  return result;
}

4. Verificação de Segurança (CRÍTICO)

SEMPRE inclua esta verificação em TODAS as Server Actions:

const session = await auth.api.getSession({
  headers: await headers(),
});

if (!session?.user) {
  throw new Error("Unauthorized");
}

Arquivo de referência: src/actions/add-cart-product/index.ts (linhas 14-20)

5. Validação de Ownership

Quando o recurso pertence a um usuário específico:

const order = await db.query.orderTable.findFirst({
  where: eq(orderTable.id, orderId),
});

if (!order) {
  throw new Error("Order not found");
}

if (order.userId !== session.user.id) {
  throw new Error("Unauthorized");
}

Arquivo de referência: src/actions/create-checkout-session-stripe/index.ts (linhas 33-45)

6. Operações Drizzle ORM

Query (SELECT):

// Find First
const item = await db.query.tableName.findFirst({
  where: eq(tableName.id, itemId),
  with: {
    relatedTable: true,
  },
});

// Find Many
const items = await db.query.tableName.findMany({
  where: eq(tableName.userId, session.user.id),
  with: {
    relatedTable: true,
  },
});

Insert:

// Insert single
await db.insert(tableName).values({
  userId: session.user.id,
  field: data.field,
});

// Insert with returning
const [newItem] = await db.insert(tableName).values({
  userId: session.user.id,
}).returning();

const itemId = newItem.id;

Update:

await db.update(tableName)
  .set({
    field: newValue,
  })
  .where(eq(tableName.id, itemId));

Delete:

await db.delete(tableName)
  .where(eq(tableName.id, itemId));

7. Transactions

Para operações múltiplas que precisam ser atômicas:

await db.transaction(async (tx) => {
  const [order] = await tx.insert(orderTable).values({
    userId: session.user.id,
    totalPriceInCents: total,
  }).returning();

  await tx.insert(orderItemTable).values({
    orderId: order.id,
    productVariantId: productId,
  });

  await tx.delete(cartTable).where(eq(cartTable.userId, session.user.id));
});

Arquivo de referência: src/actions/finish-purchase/index.ts (linhas 43-82)

8. Cache Revalidation

SEMPRE revalide o cache após mutações:

import { revalidatePath } from "next/cache";

// Revalidar rota específica
revalidatePath("/cart/identification");

// Revalidar layout completo
revalidatePath("/", "layout");

// Revalidar múltiplas rotas
revalidatePath("/cart/identification");
revalidatePath("/cart/confirmation");

9. Error Handling

// Erros de validação
if (!data.field) {
  throw new Error("Field is required");
}

// Erros de autorização
if (!session?.user) {
  throw new Error("Unauthorized");
}

// Erros de recurso não encontrado
if (!resource) {
  throw new Error("Resource not found");
}

// Erros de ownership
if (resource.userId !== session.user.id) {
  throw new Error("Unauthorized");
}

10. Imports Necessários

"use server";

// Autenticação
import { auth } from "@/lib/auth";
import { headers } from "next/headers";

// Cache
import { revalidatePath } from "next/cache";

// Validação
import { nomeSchema, NomeSchema } from "./schema";

// Database
import { db } from "@/db";
import { tableName } from "@/db/schema";
import { eq, and, or } from "drizzle-orm";

Schemas Drizzle

Ver schema completo: src/db/schema.ts

Principais tabelas:

  • userTable - Usuários
  • categoryTable - Categorias
  • productTable - Produtos
  • productVariantTable - Variantes de produtos
  • cartTable - Carrinhos
  • cartItemTable - Itens do carrinho
  • shippingAddressTable - Endereços de entrega
  • orderTable - Pedidos
  • orderItemTable - Itens do pedido

Operadores Drizzle ORM

import { eq, and, or, gt, gte, lt, lte, like, inArray } from "drizzle-orm";

// Igual
where: eq(table.id, value)

// E lógico
where: and(
  eq(table.userId, userId),
  eq(table.status, "active")
)

// Ou lógico
where: or(
  eq(table.status, "pending"),
  eq(table.status, "processing")
)

// Maior que
where: gt(table.price, 1000)

// In array
where: inArray(table.status, ["pending", "paid"])

Padrões Comuns

Buscar ou Criar

let cart = await db.query.cartTable.findFirst({
  where: eq(cartTable.userId, session.user.id),
});

if (!cart) {
  const [newCart] = await db.insert(cartTable).values({
    userId: session.user.id,
  }).returning();

  cart = newCart;
}

Incrementar Quantidade

const cartItem = await db.query.cartItemTable.findFirst({
  where: and(
    eq(cartItemTable.cartId, cartId),
    eq(cartItemTable.productVariantId, productVariantId)
  ),
});

if (cartItem) {
  await db.update(cartItemTable).set({
    quantity: cartItem.quantity + quantity,
  }).where(eq(cartItemTable.id, cartItem.id));
} else {
  await db.insert(cartItemTable).values({
    cartId,
    productVariantId,
    quantity,
  });
}

Validações Zod Comuns para Server Actions

// UUID
productVariantId: z.uuid()

// String
name: z.string().min(1, "Required")

// Number
quantity: z.number().min(1)

// Enum
status: z.enum(["pending", "paid", "cancelled"])

// Object nested
address: z.object({
  street: z.string(),
  number: z.string(),
})

Arquivos de Referência

  • Server Action simples: src/actions/add-cart-product/index.ts
  • Server Action com ownership: src/actions/create-checkout-session-stripe/index.ts
  • Server Action com transaction: src/actions/finish-purchase/index.ts
  • Schema reference: src/db/schema.ts

Processo de Criação

  1. Criar pasta: src/actions/nome-action/
  2. Criar schema.ts: Definir validação Zod
  3. Criar index.ts: Implementar Server Action
  4. Adicionar segurança: Session check obrigatório
  5. Implementar lógica: Operações de banco de dados
  6. Validar ownership: Se necessário
  7. Revalidar cache: Se houver mutação
  8. Testar: Verificar autorização e validação

Checklist de Segurança

  • "use server" no topo do arquivo
  • Schema Zod definido em schema.ts
  • Validação com schema.parse(data)
  • Session check com auth.api.getSession()
  • Verificação if (!session?.user)
  • Validação de ownership quando necessário
  • Error handling apropriado
  • revalidatePath após mutações