Examples
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
typescript
1import { createApp } from '@morojs/moro';
2import { z } from 'zod';
3
4const app = await 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// Shopping cart endpoints
48app.get('/cart')
49 .use(requireAuth)
50 .handler(async ({ context, db }) => {
51 const cartItems = await db.query(`
52 SELECT ci.*, p.name, p.price, p.image_url
53 FROM cart_items ci
54 JOIN products p ON ci.product_id = p.id
55 WHERE ci.user_id = $1
56 `, [context.user.id]);
57
58 const total = cartItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
59
60 return {
61 success: true,
62 data: {
63 items: cartItems,
64 total,
65 itemCount: cartItems.length
66 }
67 };
68 }));
69
70app.post('/cart/items')
71 .body(z.object({
72 productId: z.string().uuid(),
73 quantity: z.number().positive().max(10)
74 }))
75 .use(requireAuth)
76 .handler(async ({ body, context, db }) => {
77 // Check product availability
78 const product = await db.query(
79 'SELECT id, price FROM products WHERE id = $1',
80 [body.productId]
81 );
82
83 if (!product[0]) {
84 return { success: false, error: 'Product not found' };
85 }
86
87 // Check inventory
88 const inventory = await db.query(
89 'SELECT quantity FROM inventory WHERE product_id = $1',
90 [body.productId]
91 );
92
93 if (!inventory[0] || inventory[0].quantity < body.quantity) {
94 return { success: false, error: 'Insufficient inventory' };
95 }
96
97 // Add to cart (or update existing)
98 const cartItem = await db.query(`
99 INSERT INTO cart_items (user_id, product_id, quantity)
100 VALUES ($1, $2, $3)
101 ON CONFLICT (user_id, product_id)
102 DO UPDATE SET quantity = cart_items.quantity + $3
103 RETURNING *
104 `, [context.user.id, body.productId, body.quantity]);
105
106 return { success: true, data: cartItem[0] };
107 }));
108
109// Checkout process
110app.post('/checkout')
111 .body(z.object({
112 shippingAddress: z.object({
113 street: z.string(),
114 city: z.string(),
115 state: z.string(),
116 zipCode: z.string(),
117 country: z.string()
118 }),
119 paymentMethod: z.string(), // Stripe payment method ID
120 couponCode: z.string().optional()
121 }))
122 .use(requireAuth)
123 .handler(async ({ body, context, db }) => {
124 return await db.transaction(async (tx) => {
125 // Get cart items
126 const cartItems = await tx.query(
127 'SELECT ci.*, p.price FROM cart_items ci JOIN products p ON ci.product_id = p.id WHERE ci.user_id = $1',
128 [context.user.id]
129 );
130
131 if (cartItems.length === 0) {
132 return { success: false, error: 'Cart is empty' };
133 }
134
135 // Calculate totals
136 let subtotal = cartItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
137 let discount = 0;
138
139 // Apply coupon if provided
140 if (body.couponCode) {
141 const coupon = await tx.query(
142 'SELECT * FROM coupons WHERE code = $1 AND active = true AND expires_at > NOW()',
143 [body.couponCode]
144 );
145
146 if (coupon[0]) {
147 discount = coupon[0].type === 'percentage'
148 ? subtotal * (coupon[0].value / 100)
149 : coupon[0].value;
150 }
151 }
152
153 const total = subtotal - discount;
154
155 // Create order
156 const order = await tx.query(`
157 INSERT INTO orders (user_id, subtotal, discount, total, status, shipping_address)
158 VALUES ($1, $2, $3, $4, 'pending', $5)
159 RETURNING *
160 `, [context.user.id, subtotal, discount, total, JSON.stringify(body.shippingAddress)]);
161
162 // Create order items
163 for (const item of cartItems) {
164 await tx.query(`
165 INSERT INTO order_items (order_id, product_id, quantity, price)
166 VALUES ($1, $2, $3, $4)
167 `, [order[0].id, item.product_id, item.quantity, item.price]);
168
169 // Update inventory
170 await tx.query(
171 'UPDATE inventory SET quantity = quantity - $1 WHERE product_id = $2',
172 [item.quantity, item.product_id]
173 );
174 }
175
176 // Process payment
177 const paymentResult = await processPayment({
178 amount: total,
179 currency: 'usd',
180 paymentMethod: body.paymentMethod,
181 orderId: order[0].id
182 });
183
184 if (!paymentResult.success) {
185 return { success: false, error: 'Payment failed' };
186 }
187
188 // Update order status
189 await tx.query(
190 'UPDATE orders SET status = $1, payment_id = $2 WHERE id = $3',
191 ['paid', paymentResult.paymentId, order[0].id]
192 );
193
194 // Clear cart
195 await tx.query('DELETE FROM cart_items WHERE user_id = $1', [context.user.id]);
196
197 // Emit order created event
198 events.emit('order.created', { order: order[0], items: cartItems });
199
200 return {
201 success: true,
202 data: {
203 order: order[0],
204 payment: paymentResult
205 }
206 };
207 });
208 }));
209
210app.listen(3000, () => {
211 console.log('E-commerce API running on http://localhost:3000');
212});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 Catalogtypescript
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 Carttypescript
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}Checkouttypescript
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 Startedtypescript
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