E-commerce Store Example
Build a complete online store with products, cart, and checkout.
Overview
This example creates a fully-functional e-commerce store with:
- Product catalog
- Shopping cart
- Stripe checkout
- Order management
- Inventory tracking
- Admin dashboard
Create the Project
npx autodeploybase init my-store \
--framework next \
--archetype ecommerce \
--database postgresql \
--plugins payments-stripe,email,file-storage
cd my-store
Project Structure
my-store/
├── prisma/
│ └── schema.prisma
├── src/
│ ├── app/
│ │ ├── (shop)/
│ │ │ ├── products/
│ │ │ ├── cart/
│ │ │ └── checkout/
│ │ ├── (account)/
│ │ │ ├── orders/
│ │ │ └── settings/
│ │ ├── admin/
│ │ │ ├── products/
│ │ │ ├── orders/
│ │ │ └── inventory/
│ │ └── api/
│ ├── components/
│ │ ├── shop/
│ │ └── admin/
│ └── lib/
└── autodeploy.config.json
Setup
# Install dependencies
npm install
# Set up database
npx prisma generate
npx prisma migrate dev --name init
# Seed sample products
npx prisma db seed
# Start development
npm run dev
Environment Variables
.env
DATABASE_URL="postgresql://localhost:5432/my_store"
JWT_SECRET="your-secret"
# Stripe
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_PUBLISHABLE_KEY="pk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."
# Storage
STORAGE_PROVIDER="s3"
S3_BUCKET="my-store-images"
S3_REGION="us-east-1"
S3_ACCESS_KEY="..."
S3_SECRET_KEY="..."
# Email
EMAIL_PROVIDER="resend"
RESEND_API_KEY="re_..."
Features
Product Catalog
app/(shop)/products/page.tsx
import { ProductGrid } from '@/components/shop/ProductGrid';
import { CategoryFilter } from '@/components/shop/CategoryFilter';
import { prisma } from '@/lib/db';
export default async function ProductsPage({
searchParams
}: {
searchParams: { category?: string }
}) {
const products = await prisma.product.findMany({
where: searchParams.category
? { category: { slug: searchParams.category } }
: undefined,
include: { images: true }
});
return (
<div className="container">
<CategoryFilter />
<ProductGrid products={products} />
</div>
);
}
Shopping Cart
components/shop/Cart.tsx
'use client';
import { useCart } from '@/hooks/useCart';
export function Cart() {
const { items, total, updateQuantity, removeItem } = useCart();
return (
<div className="cart">
{items.map(item => (
<div key={item.id} className="cart-item">
<img src={item.image} alt={item.name} />
<div>
<h3>{item.name}</h3>
<p>${item.price}</p>
</div>
<input
type="number"
value={item.quantity}
onChange={(e) => updateQuantity(item.id, parseInt(e.target.value))}
/>
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
))}
<div className="cart-total">
<strong>Total: ${total}</strong>
</div>
<a href="/checkout" className="btn btn-primary">
Checkout
</a>
</div>
);
}
Checkout
app/(shop)/checkout/page.tsx
import { CheckoutForm } from '@/components/shop/CheckoutForm';
import { OrderSummary } from '@/components/shop/OrderSummary';
import { getCart } from '@/lib/cart';
export default async function CheckoutPage() {
const cart = await getCart();
return (
<div className="checkout-page">
<div className="checkout-form">
<h1>Checkout</h1>
<CheckoutForm />
</div>
<div className="order-summary">
<OrderSummary cart={cart} />
</div>
</div>
);
}
Order Management
app/(account)/orders/page.tsx
import { OrderList } from '@/components/account/OrderList';
import { getUser } from '@/lib/auth';
import { prisma } from '@/lib/db';
export default async function OrdersPage() {
const user = await getUser();
const orders = await prisma.order.findMany({
where: { userId: user.id },
include: { items: { include: { product: true } } },
orderBy: { createdAt: 'desc' }
});
return (
<div>
<h1>My Orders</h1>
<OrderList orders={orders} />
</div>
);
}
Database Schema
prisma/schema.prisma
model Product {
id String @id @default(cuid())
name String
slug String @unique
description String?
price Decimal
comparePrice Decimal?
images ProductImage[]
category Category @relation(fields: [categoryId], references: [id])
categoryId String
inventory Inventory?
variants ProductVariant[]
orderItems OrderItem[]
createdAt DateTime @default(now())
}
model Category {
id String @id @default(cuid())
name String
slug String @unique
products Product[]
}
model Order {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
items OrderItem[]
status OrderStatus @default(PENDING)
total Decimal
shippingAddress Json
stripePaymentId String?
createdAt DateTime @default(now())
}
model Inventory {
id String @id @default(cuid())
productId String @unique
product Product @relation(fields: [productId], references: [id])
quantity Int @default(0)
reserved Int @default(0)
}
Admin Features
Product Management
app/admin/products/page.tsx
import { ProductsTable } from '@/components/admin/ProductsTable';
import { prisma } from '@/lib/db';
export default async function AdminProductsPage() {
const products = await prisma.product.findMany({
include: { category: true, inventory: true }
});
return (
<div>
<div className="flex justify-between">
<h1>Products</h1>
<a href="/admin/products/new" className="btn">Add Product</a>
</div>
<ProductsTable products={products} />
</div>
);
}
Order Fulfillment
app/admin/orders/page.tsx
import { OrdersTable } from '@/components/admin/OrdersTable';
export default async function AdminOrdersPage() {
const orders = await prisma.order.findMany({
include: { user: true, items: true },
orderBy: { createdAt: 'desc' }
});
return (
<div>
<h1>Orders</h1>
<OrdersTable orders={orders} />
</div>
);
}
Webhooks
Stripe Webhook
app/api/webhooks/stripe/route.ts
import { stripe } from '@/lib/stripe';
import { prisma } from '@/lib/db';
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('stripe-signature')!;
const event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
switch (event.type) {
case 'checkout.session.completed':
await handleCheckoutComplete(event.data.object);
break;
case 'payment_intent.succeeded':
await handlePaymentSuccess(event.data.object);
break;
}
return Response.json({ received: true });
}
Deployment
Vercel + Stripe
- Deploy to Vercel
- Add environment variables
- Set up Stripe webhook endpoint
- Configure product webhooks
vercel