E-commerce API Example
A complete working e-commerce backend with product catalog, shopping cart, payment processing, order management, and inventory tracking. Copy and customize.
Complete Working Example
1import { createApp } from '@morojs/moro';
2import { z } from 'zod';
3
4const app = createApp({
5 cors: true,
6 compression: true,
7 helmet: true
8});
9
10// Product catalog endpoints
11app.get('/products', {
12 query: z.object({
13 category: z.string().optional(),
14 search: z.string().optional(),
15 minPrice: z.coerce.number().optional(),
16 maxPrice: z.coerce.number().optional(),
17 inStock: z.boolean().optional(),
18 page: z.coerce.number().default(1),
19 limit: z.coerce.number().max(50).default(20)
20 }),
21 handler: async ({ query, db }) => {
22 const products = await db.query(`
23 SELECT p.*, c.name as category_name, i.quantity as stock_quantity
24 FROM products p
25 LEFT JOIN categories c ON p.category_id = c.id
26 LEFT JOIN inventory i ON p.id = i.product_id
27 WHERE ($1::text IS NULL OR c.name ILIKE $1)
28 AND ($2::text IS NULL OR p.name ILIKE $2 OR p.description ILIKE $2)
29 AND ($3::numeric IS NULL OR p.price >= $3)
30 AND ($4::numeric IS NULL OR p.price <= $4)
31 AND ($5::boolean IS NULL OR ($5 = true AND i.quantity > 0))
32 ORDER BY p.created_at DESC
33 LIMIT $6 OFFSET $7
34 `, [
35 query.category ? `%${query.category}%` : null,
36 query.search ? `%${query.search}%` : null,
37 query.minPrice,
38 query.maxPrice,
39 query.inStock,
40 query.limit,
41 (query.page - 1) * query.limit
42 ]);
43
44 return { success: true, data: products };
45 }
46});
47
48// Shopping cart endpoints
49app.get('/cart', {
50 middleware: [requireAuth],
51 handler: async ({ context, db }) => {
52 const cartItems = await db.query(`
53 SELECT ci.*, p.name, p.price, p.image_url
54 FROM cart_items ci
55 JOIN products p ON ci.product_id = p.id
56 WHERE ci.user_id = $1
57 `, [context.user.id]);
58
59 const total = cartItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
60
61 return {
62 success: true,
63 data: {
64 items: cartItems,
65 total,
66 itemCount: cartItems.length
67 }
68 };
69 }
70});
71
72app.post('/cart/items', {
73 middleware: [requireAuth],
74 body: z.object({
75 productId: z.string().uuid(),
76 quantity: z.number().positive().max(10)
77 }),
78 handler: async ({ body, context, db }) => {
79 // Check product availability
80 const product = await db.query(
81 'SELECT id, price FROM products WHERE id = $1',
82 [body.productId]
83 );
84
85 if (!product[0]) {
86 return { success: false, error: 'Product not found' };
87 }
88
89 // Check inventory
90 const inventory = await db.query(
91 'SELECT quantity FROM inventory WHERE product_id = $1',
92 [body.productId]
93 );
94
95 if (!inventory[0] || inventory[0].quantity < body.quantity) {
96 return { success: false, error: 'Insufficient inventory' };
97 }
98
99 // Add to cart (or update existing)
100 const cartItem = await db.query(`
101 INSERT INTO cart_items (user_id, product_id, quantity)
102 VALUES ($1, $2, $3)
103 ON CONFLICT (user_id, product_id)
104 DO UPDATE SET quantity = cart_items.quantity + $3
105 RETURNING *
106 `, [context.user.id, body.productId, body.quantity]);
107
108 return { success: true, data: cartItem[0] };
109 }
110});
111
112// Checkout process
113app.post('/checkout', {
114 middleware: [requireAuth],
115 body: z.object({
116 shippingAddress: z.object({
117 street: z.string(),
118 city: z.string(),
119 state: z.string(),
120 zipCode: z.string(),
121 country: z.string()
122 }),
123 paymentMethod: z.string(), // Stripe payment method ID
124 couponCode: z.string().optional()
125 }),
126 handler: async ({ body, context, db }) => {
127 return await db.transaction(async (tx) => {
128 // Get cart items
129 const cartItems = await tx.query(
130 'SELECT ci.*, p.price FROM cart_items ci JOIN products p ON ci.product_id = p.id WHERE ci.user_id = $1',
131 [context.user.id]
132 );
133
134 if (cartItems.length === 0) {
135 return { success: false, error: 'Cart is empty' };
136 }
137
138 // Calculate totals
139 let subtotal = cartItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
140 let discount = 0;
141
142 // Apply coupon if provided
143 if (body.couponCode) {
144 const coupon = await tx.query(
145 'SELECT * FROM coupons WHERE code = $1 AND active = true AND expires_at > NOW()',
146 [body.couponCode]
147 );
148
149 if (coupon[0]) {
150 discount = coupon[0].type === 'percentage'
151 ? subtotal * (coupon[0].value / 100)
152 : coupon[0].value;
153 }
154 }
155
156 const total = subtotal - discount;
157
158 // Create order
159 const order = await tx.query(`
160 INSERT INTO orders (user_id, subtotal, discount, total, status, shipping_address)
161 VALUES ($1, $2, $3, $4, 'pending', $5)
162 RETURNING *
163 `, [context.user.id, subtotal, discount, total, JSON.stringify(body.shippingAddress)]);
164
165 // Create order items
166 for (const item of cartItems) {
167 await tx.query(`
168 INSERT INTO order_items (order_id, product_id, quantity, price)
169 VALUES ($1, $2, $3, $4)
170 `, [order[0].id, item.product_id, item.quantity, item.price]);
171
172 // Update inventory
173 await tx.query(
174 'UPDATE inventory SET quantity = quantity - $1 WHERE product_id = $2',
175 [item.quantity, item.product_id]
176 );
177 }
178
179 // Process payment
180 const paymentResult = await processPayment({
181 amount: total,
182 currency: 'usd',
183 paymentMethod: body.paymentMethod,
184 orderId: order[0].id
185 });
186
187 if (!paymentResult.success) {
188 return { success: false, error: 'Payment failed' };
189 }
190
191 // Update order status
192 await tx.query(
193 'UPDATE orders SET status = $1, payment_id = $2 WHERE id = $3',
194 ['paid', paymentResult.paymentId, order[0].id]
195 );
196
197 // Clear cart
198 await tx.query('DELETE FROM cart_items WHERE user_id = $1', [context.user.id]);
199
200 // Emit order created event
201 events.emit('order.created', { order: order[0], items: cartItems });
202
203 return {
204 success: true,
205 data: {
206 order: order[0],
207 payment: paymentResult
208 }
209 };
210 });
211 }
212});
213
214app.listen(3000, () => {
215 console.log('E-commerce API running on http://localhost:3000');
216});What This Does
Shopping Cart
- • Add/remove items
- • Quantity management
- • Price calculations
- • Cart persistence
Payment Processing
- • Stripe integration
- • Secure transactions
- • Payment webhooks
- • Refund handling
Order Management
- • Order tracking
- • Inventory management
- • Shipping integration
- • Order history
Key API Endpoints
Product Catalog
typescript
1// Get products with filtering
2GET /products?category=electronics&minPrice=100&maxPrice=1000&inStock=true
3
4// Response
5{
6 "success": true,
7 "data": [
8 {
9 "id": "uuid",
10 "name": "Product Name",
11 "price": 99.99,
12 "category_name": "Electronics",
13 "stock_quantity": 50
14 }
15 ]
16}Shopping Cart
typescript
1// Get cart
2GET /cart
3Authorization: Bearer <token>
4
5// Add item to cart
6POST /cart/items
7Authorization: Bearer <token>
8{
9 "productId": "uuid",
10 "quantity": 2
11}
12
13// Response
14{
15 "success": true,
16 "data": {
17 "items": [...],
18 "total": 199.98,
19 "itemCount": 1
20 }
21}Checkout
typescript
1// Process checkout
2POST /checkout
3Authorization: Bearer <token>
4{
5 "shippingAddress": {
6 "street": "123 Main St",
7 "city": "New York",
8 "state": "NY",
9 "zipCode": "10001",
10 "country": "USA"
11 },
12 "paymentMethod": "pm_xxx",
13 "couponCode": "SAVE10"
14}
15
16// Response
17{
18 "success": true,
19 "data": {
20 "order": {
21 "id": "uuid",
22 "total": 179.98,
23 "status": "paid"
24 },
25 "payment": {
26 "paymentId": "pi_xxx",
27 "status": "succeeded"
28 }
29 }
30}Run the Example
Getting Started
typescript
1# Clone and setup
2git clone https://github.com/Moro-JS/examples.git
3cd examples/ecommerce-api
4
5# Install dependencies
6npm install
7
8# Setup environment
9cp .env.example .env
10# Add your Stripe keys and database URL
11
12# Setup database
13npm run db:setup
14npm run db:migrate
15npm run db:seed # Load sample products
16
17# Start development
18npm run dev
19
20# Test the API:
21curl http://localhost:3000/products
22curl http://localhost:3000/categories
23
24# Available scripts:
25npm run dev # Development server
26npm run build # Build for production
27npm run start # Production server
28npm run test # Run tests
29npm run db:migrate # Database migrations
30npm run db:seed # Seed sample dataWhat You'll Learn
- • E-commerce data modeling
- • Payment processing with Stripe
- • Inventory management
- • Order workflow design
- • Transaction handling
- • Webhook processing