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 Catalog
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
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
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
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 data

What You'll Learn

  • E-commerce data modeling
  • Payment processing with Stripe
  • Inventory management
  • Order workflow design
  • Transaction handling
  • Webhook processing

Next Steps