スキル一覧に戻る
RadikeCosa

supabase-queries-muma

by RadikeCosa

E-store de Fira Estudio

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

SKILL.md


title: "Supabase Queries - Fira Estudio" description: "Query patterns for fetching products with relations and caching strategies" version: "1.0" lastUpdated: "2026-01-19" activationTriggers:

  • "supabase"
  • "query"
  • "getProductos"
  • "database"
  • "filtrar"
  • "relaciones"

Supabase Queries Skill

🎯 Quick Reference

Use cached queries for public pages, fresh queries for admin interfaces.


📚 Query Patterns

Get Products with All Relations

import { getProductos, getProductosFresh } from "@/lib/supabase/queries";

// ✅ Cached (1 hour for general, 2 hours for category-filtered)
const productos = await getProductos();
const { items, pagination } = await getProductos({
  page: 2,
  pageSize: 12,
});
const manteles = await getProductos({
  categoriaSlug: "manteles",
  page: 1,
  pageSize: 20,
});

// ✅ Fresh data (admin/dashboard)
const productosFresh = await getProductosFresh();
const { items } = await getProductosFresh({ categoriaSlug: "manteles" });

Returns: PaginatedResult<ProductoCompleto>

{
  items: ProductoCompleto[], // productos con categoria, variaciones, imagenes
  pagination: {
    total: number,
    page: number,
    pageSize: number,
    totalPages: number,
    hasNextPage: boolean,
    hasPreviousPage: boolean
  }
}

Get Single Product by Slug

import {
  getProductoBySlug,
  getProductoBySlugFresh,
} from "@/lib/supabase/queries";

// ✅ Cached (1 hour)
const producto = await getProductoBySlug("mantel-floral");

// ✅ Fresh data
const productoFresh = await getProductoBySlugFresh("mantel-floral");

// Handle not found
if (!producto) {
  notFound(); // Triggers not-found.tsx
}

Returns: ProductoCompleto | null


import {
  getProductosRelacionados,
  getProductosRelacionadosFresh,
} from "@/lib/supabase/queries";

// ✅ Cached (1 hour) - default 4 products
const relacionados = await getProductosRelacionados(
  producto.id,
  producto.categoria_id,
);

// ✅ Custom limit
const relacionados = await getProductosRelacionados(
  producto.id,
  producto.categoria_id,
  6, // limite: 6 productos
);

// ✅ Fresh data
const relacionadosFresh = await getProductosRelacionadosFresh(
  producto.id,
  producto.categoria_id,
  4,
);

Returns: ProductoCompleto[] (excludes current product, filters by category)


Get Categories

import { getCategorias, getCategoriasFresh } from "@/lib/supabase/queries";

// ✅ Cached (24 hours)
const categorias = await getCategorias();

// ✅ Fresh data
const categoriasFresh = await getCategoriasFresh();

Returns: Categoria[] (ordered by orden field)


🔄 Cache Strategy

Cache Durations

QueryCache DurationUse Case
getProductos()1 hourGeneral product listing
getProductos({ categoriaSlug })2 hoursCategory-filtered (more stable)
getProductoBySlug()1 hourProduct detail page
getProductosRelacionados()1 hourRelated products
getCategorias()24 hoursCategories (rarely change)

When to Use Fresh Queries

Use *Fresh() variants when:

  • Building admin interfaces
  • Showing real-time inventory
  • After data mutations (create/update/delete)
  • Development/debugging

Cache Invalidation

import {
  revalidateProductos,
  revalidateProducto,
  revalidateCategorias,
} from "@/lib/cache/revalidate";

// After updating multiple products
await updateMultipleProductos(data);
revalidateProductos(); // Clears all product caches

// After updating a single product
await updateProducto(id, data);
revalidateProducto(producto.slug); // Clears specific product cache

// After updating categories
await updateCategoria(id, data);
revalidateCategorias(); // Clears category cache

⚠️ Important Notes

Cannot Order Nested Relations

Supabase does NOT support ordering nested relations in queries.

// ❌ WRONG - This doesn't work
const { data } = await supabase
  .from("productos")
  .select("*, variaciones(*)")
  .order("variaciones(precio)"); // ❌ Not supported!

// ✅ CORRECT - Sort in JavaScript after fetch
const { data } = await supabase.from("productos").select("*, variaciones(*)");

data?.forEach((producto) => {
  producto.variaciones.sort((a, b) => a.precio - b.precio);
});

Repository Layer: The ProductoRepository handles this automatically.


Use activo Column (Not disponible)

// ❌ WRONG - Column doesn't exist
.eq("disponible", true)

// ✅ CORRECT
.eq("activo", true)

Always Handle Errors

const { data, error } = await supabase
  .from("productos")
  .select("*")
  .eq("slug", slug)
  .single();

if (error) {
  // Handle not found
  if (error.code === "PGRST116") {
    return notFound();
  }

  // Log and throw other errors
  console.error("Database error:", error);
  throw error;
}

🏗️ Repository Pattern

The project uses a repository layer for productos:

// lib/repositories/producto.repository.ts
class ProductoRepository {
  async findAll(filters?: {
    categoria?: string;
    limit?: number;
    offset?: number;
  }): Promise<{ items: ProductoCompleto[]; total: number }> {
    // Handles ordering relations in JavaScript
  }
}

Benefits:

  • Encapsulates Supabase complexity
  • Handles relation sorting automatically
  • Consistent error handling
  • Type-safe filtering

  • Complete schema: .github/reference/database-schema.md
  • Business rules: .github/reference/business-logic.md
  • Cache configuration: lib/cache/index.ts
  • TypeScript types: lib/types.ts

✅ Best Practices Checklist

  • Use cached queries for public pages
  • Use fresh queries for admin interfaces
  • Always handle errors before using data
  • Sort relations in JavaScript (not in query)
  • Use .eq("activo", true) for active records
  • Import correct client (server.ts vs client.ts)
  • Use TypeScript types from lib/types.ts
  • Handle not found cases with notFound()

スコア

総合スコア

55/100

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

SKILL.md

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

+20
LICENSE

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

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

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

0/5
Issue管理

オープンIssueが50未満

+5
言語

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

+5
タグ

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

+5

レビュー

💬

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