スキル一覧に戻る
keidsondesigner

create-server-action

by keidsondesigner

0🍴 0📅 2026年1月16日
GitHubで見るManusで実行

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

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です