Welcome to the Jose Madrid Salsa developer docs — explore features, APIs, and deployment guides.
Jose Madrid SalsaJMS Docs

Common Prisma Query Patterns

Frequently used database query patterns in the Jose Madrid Salsa platform

Common Prisma Query Patterns

This guide covers the most common database query patterns used throughout the Jose Madrid Salsa platform, with practical examples you can adapt.

Basic CRUD Operations

Finding Records

import { prisma } from '@/lib/prisma'

// Find by unique field
const user = await prisma.user.findUnique({
  where: { email: 'user@example.com' },
})

// Find first matching record
const activeProduct = await prisma.product.findFirst({
  where: { isActive: true, heatLevel: 'HOT' },
})

// Find many with filtering
const products = await prisma.product.findMany({
  where: {
    isActive: true,
    heatLevel: 'MILD',
    inventory: { gt: 0 },
  },
  orderBy: [
    { isFeatured: 'desc' },
    { sortOrder: 'asc' },
    { name: 'asc' },
  ],
})

Creating Records

const product = await prisma.product.create({
  data: {
    name: 'Ghost of Clovis',
    slug: 'ghost-of-clovis',
    heatLevel: 'HOT',
    price: 9.49,
    sku: 'JMS-HOT-001',
    inventory: 80,
    categoryId: 'category_id_here',
    ingredients: ['Ghost Peppers', 'Tomatoes'],
  },
})

Updating Records

const updated = await prisma.order.update({
  where: { id: orderId },
  data: {
    status: 'SHIPPED',
    trackingNumber: '9400111899223456789012',
  },
})

Deleting Records

// Soft delete (prefer for user-facing data)
await prisma.product.update({
  where: { id: productId },
  data: { isActive: false },
})

// Hard delete
await prisma.cartItem.deleteMany({
  where: { userId: user.id },
})

Filtering Patterns

The products API uses mode: 'insensitive' for search:

const where = {
  OR: [
    { name: { contains: search, mode: 'insensitive' } },
    { description: { contains: search, mode: 'insensitive' } },
  ],
}

Tag and Category Filtering

// Filter by category slug
where.category = { slug: { in: ['salsas', 'hot-sauces'] } }

// Filter by tag slug (through relation)
where.productTags = {
  some: {
    tag: { slug: { in: ['spicy', 'organic'] } },
  },
}

Date Range Filtering

const recentOrders = await prisma.order.findMany({
  where: {
    createdAt: {
      gte: new Date('2024-01-01'),
      lte: new Date('2024-12-31'),
    },
  },
})

Stock Filtering

// In-stock only
where.inventory = { gt: 0 }

// By stock status enum
where.stockStatus = 'IN_STOCK'

Relation Queries

Eager Loading (Include)

const order = await prisma.order.findUnique({
  where: { id: orderId },
  include: {
    items: {
      include: {
        product: true,
      },
    },
    user: {
      select: { id: true, email: true, name: true },
    },
    shippingAddress: true,
  },
})

Nested Includes

The product query demonstrates deep nesting:

const products = await prisma.product.findMany({
  include: {
    productTags: {
      include: { tag: true },
    },
    nutritionalInfo: true,
    productIngredients: {
      include: { ingredient: true },
      orderBy: { sortOrder: 'asc' },
    },
  },
})

Selective Fields

const users = await prisma.user.findMany({
  select: {
    id: true,
    email: true,
    role: true,
    // Exclude sensitive fields like password
  },
})

Pagination

const { searchParams } = new URL(request.url)
const rawTake = Number(searchParams.get('take'))
const take = Number.isFinite(rawTake) && rawTake > 0 ? rawTake : undefined
const rawSkip = Number(searchParams.get('skip'))
const skip = Number.isFinite(rawSkip) && rawSkip >= 0 ? rawSkip : 0

const products = await prisma.product.findMany({
  skip,
  take,
  orderBy: { createdAt: 'desc' },
})

Decimal Handling

Prisma returns Decimal types for monetary fields. Always convert for JSON responses:

const parsedProducts = products.map(product => ({
  ...product,
  price: parseFloat(String(product.price)),
  compareAtPrice: product.compareAtPrice
    ? parseFloat(String(product.compareAtPrice))
    : undefined,
}))

For creating records with decimals:

import { Prisma } from '@prisma/client'

const toDecimal = (value: number) =>
  new Prisma.Decimal(value.toFixed(2))

await prisma.order.create({
  data: {
    subtotal: toDecimal(25.99),
    tax: toDecimal(2.34),
    total: toDecimal(28.33),
  },
})

Transactions

Simple Transaction

const [order, updatedInventory] = await prisma.$transaction([
  prisma.order.create({ data: orderData }),
  prisma.product.update({
    where: { id: productId },
    data: { inventory: { decrement: quantity } },
  }),
])

Interactive Transaction

The inventory manager uses interactive transactions for complex operations:

await prisma.$transaction(async (tx) => {
  const product = await tx.product.findUnique({
    where: { id: productId },
  })

  if (product.inventory < quantity) {
    throw new Error('Insufficient inventory')
  }

  await tx.product.update({
    where: { id: productId },
    data: { inventory: { decrement: quantity } },
  })

  await tx.inventoryTransaction.create({
    data: {
      productId,
      quantity: -quantity,
      type: 'SALE',
    },
  })
})

Serializable Retry Pattern

For high-contention operations, use the retry pattern from lib/inventory-manager.ts:

async function withSerializableRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  let lastError: unknown
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn()
    } catch (error: any) {
      if (error?.code === 'P2034' && attempt < maxRetries - 1) {
        lastError = error
        continue
      }
      throw error
    }
  }
  throw lastError
}

Aggregations

// Count orders by status
const orderCounts = await prisma.order.groupBy({
  by: ['status'],
  _count: { id: true },
})

// Sum revenue
const revenue = await prisma.order.aggregate({
  _sum: { total: true },
  where: { paymentStatus: 'PAID' },
})

Upsert Pattern

// Create if not exists, update if exists
const settings = await prisma.shippingSettings.upsert({
  where: { singleton: 'singleton' },
  update: { freeShippingThreshold: 75.00 },
  create: {
    singleton: 'singleton',
    freeShippingThreshold: 75.00,
  },
})

Query Performance

Enable query logging in development to see generated SQL. Set logLevels to ['query'] in lib/prisma.ts. This helps identify slow queries and missing indexes.

How is this guide?

Edit on GitHub

Last updated on

On this page