This document defines the database schema for the GuepardStore Demo App. This project uses Prisma as its ORM (Object-Relational Mapper) and demonstrates Guepard's git-like database branching capabilities.
The following schema defines all models, fields, and relations. It is used by Prisma to generate database migrations and the type-safe client.
// file: components/api/prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
model Category {
id String @id @default(uuid()) @db.Uuid
name String @unique @db.VarChar(100)
slug String @unique @db.VarChar(100)
description String?
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
products Product[]
}
model Product {
id String @id @default(uuid()) @db.Uuid
name String @db.VarChar(255)
description String
price Decimal @db.Decimal(10, 2)
stock Int @default(0)
imageUrl String? @map("image_url")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
category Category @relation(fields: [categoryId], references: [id])
categoryId String @map("category_id") @db.Uuid
orderItems OrderItem[]
}
model Order {
id String @id @default(uuid()) @db.Uuid
// userId String? @map("user_id") @db.Uuid // We'll keep this commented for now
clientName String @map("client_name")
clientPhone String @map("client_phone")
clientAddress String @map("client_address")
totalAmount Decimal @map("total_amount") @db.Decimal(10, 2)
status String @default("pending") @db.VarChar(50)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
orderItems OrderItem[]
}
model OrderItem {
id String @id @default(uuid()) @db.Uuid
order Order @relation(fields: [orderId], references: [id])
orderId String @map("order_id") @db.Uuid
product Product @relation(fields: [productId], references: [id])
productId String @map("product_id") @db.Uuid
quantity Int
priceAtTime Decimal @map("price_at_time") @db.Decimal(10, 2)
createdAt DateTime @default(now()) @map("created_at")
@@map("order_items")
}The schema includes shadowDatabaseUrl configuration for Prisma migrations, which is essential for Guepard's database branching:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}- Main Branch: Contains the base schema without discount features
- Feature Branches: Guepard creates isolated database branches (e.g.,
discout-feature) with schema modifications - Migration Management: Prisma migrations are applied to specific branches without affecting others
In this project, complex business rules are handled within the application layer (the Node.js backend) rather than with database triggers. This keeps the logic centralized and easier to manage.
- The
created_attimestamp is set automatically by the database using Prisma's@default(now())attribute. - The
updated_attimestamp is automatically updated on every record change by Prisma's@updatedAtattribute.
- To ensure data consistency, stock validation and updates are performed within a Prisma transaction (
prisma.$transaction) in the/api/ordersendpoint. - This guarantees that an order is only created if all items are in stock, and the stock is only decreased if the order is successfully created. This atomic operation prevents race conditions and data corruption.
Sample data is managed via a seed script at components/api/prisma/seed.ts. This script populates the database with initial categories and products.
You can run the seed script using:
cd components/api
bunx prisma db seedNote: The seed script only runs if the database is empty, preventing accidental data duplication.
GET /api/products: Lists products (supportsqfor search andcategoryfor filtering).POST /api/products: Creates a new product (admin).PUT /api/products/:id: Updates a product (admin).DELETE /api/products/:id: Deletes a product (admin).
GET /api/categories: Lists all categories.
POST /api/orders: Creates a new order, validates stock, and updates product quantities.
GET /api/demo-control/status: Returns current database connection statusGET /api/demo-control/feature-status/:featureName: Checks if a feature patch is appliedPOST /api/demo-control/manage-feature: Applies or reverts feature patchesPOST /api/demo-control/switch-db: Updates database connection stringsPOST /api/demo-control/run-seed: Runs database seeding
These examples show how to perform common queries using the type-safe Prisma Client in the backend.
const productsWithCategories = await prisma.product.findMany({
include: {
category: true, // Joins the category table
},
orderBy: {
createdAt: 'desc',
},
});const orderDetails = await prisma.order.findUnique({
where: { id: orderId },
include: {
orderItems: { // Nested join on order items
include: {
product: true, // Further nest to get product details for each item
},
},
},
});const lowStockProducts = await prisma.product.findMany({
where: {
stock: {
lt: 10, // 'lt' means 'less than'
},
},
orderBy: {
stock: 'asc',
},
});When the discount feature is applied (via scripts or git checkout), the schema includes additional fields:
model Product {
// ... existing fields
discountPrice Decimal? @map("discount_price") @db.Decimal(10, 2)
discountPercentage Int? @map("discount_percentage")
isOnSale Boolean @default(false) @map("is_on_sale")
}This demonstrates how Guepard's database branching allows for:
- Schema Evolution: Different branches can have different schemas
- Safe Testing: Test new features without affecting production data
- Easy Rollback: Revert schema changes by switching branches
- Prisma Client: Provides type-safe database access and prevents SQL injection
- Transaction Support: Ensures data consistency for complex operations
- Connection Pooling: Managed by Prisma for optimal performance
- Data Validation: Handled at the API layer before database operations
# Create and apply migrations
bunx prisma migrate dev --name "migration-name"
# Generate Prisma client
bunx prisma generate
# Reset database (development only)
bunx prisma migrate reset
# View database in Prisma Studio
bunx prisma studio