Nitrocloud LogoNitroStack
/templates
/ecommerce

E-commerce Server Template

Time to read: 20 minutes

The official NitroStack template - A production-ready e-commerce MCP server showcasing v3.0 decorator-based architecture with authentication, database, and comprehensive UI widgets.

What You'll Learn

  • v3.0 Architecture - Decorator-based development with @Tool, @Module, @McpApp
  • JWT Authentication - Secure user authentication with guards
  • SQLite Database - Embedded database with DI
  • UI Widgets - Beautiful Next.js components
  • Production Patterns - Middleware, interceptors, caching, rate limiting

Quick Start

# Install NitroStack CLI
npm install -g nitrostack

# Create project
nitrostack init my-store --template typescript-auth
cd my-store

# Setup database
npm run setup-db

# Start development
npm run dev

The server starts with Studio on http://localhost:3000.

Features Overview

šŸ” Authentication System

  • JWT Tokens - Secure authentication with JWTModule
  • Login/Logout - User management tools
  • Guards - @UseGuards(JWTGuard) for protected tools
  • Token Storage - Automatic browser storage

šŸ’¾ Database Layer

  • SQLite - Embedded database with better-sqlite3
  • Seed Data - Pre-populated sample data
  • DI Integration - DatabaseService injected everywhere
  • Tables: Users, Products, Cart, Orders, Addresses

šŸ› ļø Tools (20+)

All tools use decorator-based patterns with proper input validation and examples.

Public Tools:

  • browse_products - Browse product catalog with caching
  • get_categories - View product categories
  • get_product - Get product details
  • search_products - Search products by query
  • login - User authentication

Authenticated Tools (require @UseGuards(JWTGuard)):

  • whoami - Get current user profile
  • add_to_cart - Add products to cart
  • get_cart - View shopping cart
  • update_cart_item - Update cart item quantity
  • remove_cart_item - Remove item from cart
  • clear_cart - Empty entire cart
  • get_addresses - View saved addresses
  • add_address - Add delivery address
  • update_address - Update existing address
  • set_default_address - Set default address
  • delete_address - Remove address
  • create_order - Place order from cart
  • get_orders - View order history
  • get_order_details - View specific order

šŸŽØ UI Widgets

Every tool has a beautifully designed Next.js widget with:

  • Product grids with images and prices
  • Shopping cart with item management
  • Order confirmations and history
  • Address management forms
  • Login/logout UI
  • User profile cards
  • Inline styles from ecommerce.ts

Project Structure (v3.0)

src/
ā”œā”€ā”€ app.module.ts              # Root @McpApp module
ā”œā”€ā”€ index.ts                   # Application bootstrap
ā”œā”€ā”€ modules/                   # Feature modules
│   ā”œā”€ā”€ auth/
│   │   ā”œā”€ā”€ auth.module.ts    # @Module definition
│   │   ā”œā”€ā”€ auth.tools.ts     # @Tool decorators
│   │   ā”œā”€ā”€ auth.resources.ts # @Resource decorators
│   │   └── auth.prompts.ts   # @Prompt decorators
│   ā”œā”€ā”€ products/
│   │   ā”œā”€ā”€ products.module.ts
│   │   ā”œā”€ā”€ products.tools.ts  # With @Cache
│   │   ā”œā”€ā”€ products.resources.ts
│   │   └── products.prompts.ts
│   ā”œā”€ā”€ cart/
│   │   ā”œā”€ā”€ cart.module.ts
│   │   ā”œā”€ā”€ cart.tools.ts      # With @UseGuards
│   │   ā”œā”€ā”€ cart.resources.ts
│   │   └── cart.prompts.ts
│   ā”œā”€ā”€ orders/
│   │   └── ... (similar structure)
│   └── addresses/
│       └── ... (similar structure)
ā”œā”€ā”€ services/
│   └── database.service.ts    # @Injectable service
ā”œā”€ā”€ guards/
│   └── jwt.guard.ts          # JWT authentication guard
ā”œā”€ā”€ middleware/
│   └── logging.middleware.ts  # @Middleware decorator
ā”œā”€ā”€ interceptors/
│   └── transform.interceptor.ts # @Interceptor decorator
ā”œā”€ā”€ pipes/
│   └── validation.pipe.ts     # @Pipe for validation
ā”œā”€ā”€ filters/
│   └── global-exception.filter.ts # @ExceptionFilter
ā”œā”€ā”€ health/
│   └── database.health.ts     # @HealthCheck
ā”œā”€ā”€ events/
│   ā”œā”€ā”€ analytics.service.ts   # @OnEvent listeners
│   └── notification.service.ts
ā”œā”€ā”€ db/
│   ā”œā”€ā”€ database.ts           # Database setup
│   ā”œā”€ā”€ seed.ts               # Sample data
│   └── setup.ts              # DB initialization
└── widgets/                   # Next.js UI widgets
    ā”œā”€ā”€ app/
    │   ā”œā”€ā”€ whoami/
    │   ā”œā”€ā”€ products-grid/
    │   ā”œā”€ā”€ product-card/
    │   ā”œā”€ā”€ cart/
    │   ā”œā”€ā”€ order-confirmation/
    │   └── ... (20+ widgets)
    ā”œā”€ā”€ styles/
    │   └── ecommerce.ts       # Shared inline styles
    └── types/
        └── tool-data.ts       # Generated types

v3.0 Architecture Examples

Tool with Decorators

// src/modules/products/products.tools.ts
import { Tool, Widget, Cache, ExecutionContext } from 'nitrostack';
import { z } from 'zod';

export class ProductsTools {
  constructor(
    private productService: ProductService  // DI
  ) {}
  
  @Tool({
    name: 'browse_products',
    description: 'Browse products with optional filters',
    inputSchema: z.object({
      category: z.string().optional(),
      page: z.number().default(1),
      limit: z.number().default(10)
    }),
    examples: {
      request: { category: 'Electronics', page: 1, limit: 10 },
      response: {
        products: [
          { id: 'p1', name: 'Laptop', price: 999, image_url: '...' }
        ],
        total: 50,
        page: 1
      }
    }
  })
  @Widget('products-grid')
  @Cache({ ttl: 300 })  // Cache for 5 minutes
  async browseProducts(input: any, ctx: ExecutionContext) {
    ctx.logger.info('Browsing products', { category: input.category });
    
    const products = await this.productService.browse({
      category: input.category,
      limit: input.limit,
      offset: (input.page - 1) * input.limit
    });
    
    return {
      products,
      total: await this.productService.count(input.category),
      page: input.page
    };
  }
}

Module with DI

// src/modules/products/products.module.ts
import { Module } from 'nitrostack';
import { ProductsTools } from './products.tools.js';
import { ProductsResources } from './products.resources.js';
import { ProductsPrompts } from './products.prompts.js';
import { ProductService } from './product.service.js';

@Module({
  name: 'products',
  description: 'Product catalog management',
  controllers: [ProductsTools, ProductsResources, ProductsPrompts],
  providers: [ProductService],
  exports: [ProductService]
})
export class ProductsModule {}

Guard for Authentication

// src/guards/jwt.guard.ts
import { Guard, ExecutionContext, Injectable } from 'nitrostack';
import jwt from 'jsonwebtoken';

@Injectable()
export class JWTGuard implements Guard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const token = this.extractToken(context);
    if (!token) return false;
    
    try {
      const payload = jwt.verify(token, process.env.JWT_SECRET);
      context.auth = { subject: payload.sub, email: payload.email };
      return true;
    } catch {
      return false;
    }
  }
  
  private extractToken(context: ExecutionContext): string | null {
    return context.metadata?.authorization?.substring(7) || null;
  }
}

Protected Tool

@Tool({
  name: 'add_to_cart',
  description: 'Add a product to shopping cart',
  inputSchema: z.object({
    product_id: z.string(),
    quantity: z.number().min(1).default(1)
  })
})
@UseGuards(JWTGuard)  // Requires authentication
@Widget('add-to-cart')
async addToCart(input: any, ctx: ExecutionContext) {
  const userId = ctx.auth?.subject;  // From guard
  
  await this.cartService.addItem({
    userId,
    productId: input.product_id,
    quantity: input.quantity
  });
  
  return await this.cartService.getCart(userId);
}

Application Bootstrap

// src/app.module.ts
import { McpApp, Module } from 'nitrostack';
import { ConfigModule } from 'nitrostack';
import { JWTModule } from 'nitrostack';
import { ProductsModule } from './modules/products/products.module.js';
import { CartModule } from './modules/cart/cart.module.js';
// ... other modules

@McpApp({
  server: {
    name: 'ecommerce-server',
    version: '3.0.0'
  },
  logging: {
    level: 'info'
  }
})
@Module({
  imports: [
    ConfigModule.forRoot(),
    JWTModule.forRoot({
      secret: process.env.JWT_SECRET,
      expiresIn: '24h'
    }),
    ProductsModule,
    CartModule,
    OrdersModule,
    AddressesModule,
    AuthModule
  ],
  providers: [DatabaseService],
  globalMiddleware: [LoggingMiddleware],
  globalInterceptors: [TransformInterceptor],
  globalFilters: [GlobalExceptionFilter]
})
export class AppModule {}

Authentication Flow

1. Login

User: "Login as emily.johnson@x.dummyjson.com with password123"
LLM: [Calls login tool]
Result: JWT token saved in browser, user profile displayed

2. Protected Operations

User: "Add the laptop to my cart"
LLM: [Calls add_to_cart with JWT token]
Guard: Validates token, extracts user ID
Result: Cart updated, widget displayed

3. Order Placement

User: "Place an order"
LLM: [Calls create_order with JWT token]
Result: Order created, cart cleared, confirmation displayed

Database Schema

Users

CREATE TABLE users (
  id TEXT PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  password_hash TEXT NOT NULL,
  name TEXT NOT NULL,
  avatar_url TEXT,
  created_at INTEGER NOT NULL
);

Products

CREATE TABLE products (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  description TEXT,
  price REAL NOT NULL,
  category TEXT NOT NULL,
  image_url TEXT,
  in_stock INTEGER DEFAULT 1
);

Cart Items

CREATE TABLE cart_items (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  product_id TEXT NOT NULL,
  quantity INTEGER NOT NULL,
  added_at INTEGER NOT NULL,
  FOREIGN KEY (user_id) REFERENCES users(id),
  FOREIGN KEY (product_id) REFERENCES products(id)
);

Full schema includes Orders, Order Items, and Addresses tables.

Example Workflows

Browse & Purchase

User: "Show me laptops"
LLM: [browse_products category=Electronics] 
→ Products grid widget with 10 laptops

User: "Add the Dell XPS to my cart"
LLM: [add_to_cart product_id=p1, quantity=1]
→ Cart widget showing 1 item

User: "Checkout"
LLM: [create_order]
→ Order confirmation widget

Manage Cart

User: "What's in my cart?"
LLM: [get_cart]
→ Cart widget with items, quantities, total

User: "Change laptop quantity to 2"
LLM: [update_cart_item item_id=c1, quantity=2]
→ Updated cart widget

User: "Remove the mouse from cart"
LLM: [remove_cart_item item_id=c2]
→ Updated cart widget

Advanced Features Demonstrated

1. Caching

@Tool({ name: 'browse_products' })
@Cache({ ttl: 300, key: (input) => `products:${input.category}` })
async browseProducts(input: any) {
  // Results cached for 5 minutes per category
}

2. Rate Limiting

@Tool({ name: 'create_order' })
@RateLimit({ requests: 10, window: '1h', key: (ctx) => ctx.auth?.subject })
async createOrder(input: any, ctx: ExecutionContext) {
  // Max 10 orders per hour per user
}

3. Event System

@Tool({ name: 'create_order' })
async createOrder(input: any, ctx: ExecutionContext) {
  const order = await this.orderService.create(input);
  
  // Emit event
  ctx.emit('order.created', { orderId: order.id, userId: ctx.auth?.subject });
  
  return order;
}

// In analytics service
@OnEvent('order.created')
async handleOrderCreated(data: any) {
  await this.analyticsService.track('order_created', data);
}

4. Health Checks

@HealthCheck('database')
async checkDatabase() {
  try {
    await this.db.queryOne('SELECT 1');
    return { status: 'healthy' };
  } catch (error) {
    return { status: 'unhealthy', error: error.message };
  }
}

Testing Accounts

Pre-seeded users for testing:

emily.johnson@x.dummyjson.com / password123
michael.chen@x.dummyjson.com / password123
sarah.williams@x.dummyjson.com / password123

All accounts have sample orders and addresses.

Customization

Add New Module

nitrostack generate module reviews

Creates:

  • src/modules/reviews/reviews.module.ts
  • src/modules/reviews/reviews.tools.ts
  • src/modules/reviews/reviews.resources.ts
  • src/modules/reviews/reviews.prompts.ts
  • src/modules/reviews/reviews.service.ts

Add Payment Processing

import Stripe from 'stripe';

@Injectable()
export class PaymentService {
  private stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
  
  async processPayment(amount: number, token: string) {
    return await this.stripe.charges.create({
      amount: Math.round(amount * 100),
      currency: 'usd',
      source: token
    });
  }
}

Generate Types for Widgets

nitrostack generate types

Creates src/widgets/types/tool-data.ts with TypeScript interfaces from Zod schemas.

Best Practices Demonstrated

  1. Decorator-Based Architecture - Modern, declarative code
  2. Dependency Injection - Loose coupling, easy testing
  3. Module Organization - Clear feature boundaries
  4. Guard System - Declarative authentication
  5. Input Validation - Zod schemas for all inputs
  6. Error Handling - Global exception filter
  7. Type Safety - Full TypeScript with generated types
  8. UI Widgets - Rich user experience
  9. Caching - Performance optimization
  10. Events - Decoupled architecture

Production Deployment

Environment Variables

NODE_ENV=production
JWT_SECRET=your-super-secret-key-change-this
DATABASE_PATH=./data/ecommerce.db
PORT=3000

Build & Deploy

npm run build
npm start

Docker Deployment

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY dist ./dist
COPY data ./data
CMD ["node", "dist/index.js"]

Next Steps


Pro Tip: This template is a complete starting point. Customize it for your use case - replace products with articles for a CMS, tasks for a project manager, or services for a booking system!