Node.js & Express Integration Guide
This guide covers integrating SpecJet with Node.js backend applications, including Express servers, API validation, and contract-first development.
Quick Setup
1. Install SpecJet
# In your Node.js project
npm install --save-dev specjet
# Or globally
npm install -g specjet
2. Initialize SpecJet
# Initialize in your existing Node.js project
specjet init .
# This creates:
# ├── api-contract.yaml      # Your API contract
# ├── specjet.config.js      # Configuration  
# └── src/
#     ├── types/             # Generated TypeScript types
#     ├── client/            # Generated API client (for testing)
#     └── mocks/             # Generated mock server
3. Configure for Node.js
Update specjet.config.js for backend development:
export default {
  contract: './api-contract.yaml',
  
  output: {
    types: './src/types',
    client: './src/client',     // For internal API calls & testing
    mocks: './test/mocks'       // Test fixtures
  },
  
  typescript: {
    strictMode: true,
    exportType: 'named',
    clientName: 'InternalClient'
  },
  
  mock: {
    port: 3001,                 // Different from your app port
    cors: false,                // Backend typically doesn't need CORS
    scenario: 'realistic',
    logging: true               // Useful for backend debugging
  }
};
4. Add Package.json Scripts
{
  "scripts": {
    "dev": "nodemon src/server.js",
    "start": "node src/server.js",
    "api:generate": "specjet generate",
    "api:watch": "specjet generate --watch",
    "api:mock": "specjet mock",
    "api:validate": "specjet validate http://localhost:3000",
    "test": "jest",
    "test:api": "npm run api:mock & sleep 2 && npm test && kill %1"
  }
}
Express.js Integration
Using Generated Types in Express Routes
// src/routes/users.ts
import express from 'express';
import type { User, CreateUserRequest, UpdateUserRequest } from '../types/api';
const router = express.Router();
// In-memory database simulation (replace with real database)
let users: User[] = [
  { id: 1, name: 'John Doe', email: 'john@example.com', isActive: true },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com', isActive: true }
];
let nextId = 3;
// GET /users - List all users
router.get('/', (req, res) => {
  try {
    // Type-safe response
    const response: User[] = users;
    res.json(response);
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
// GET /users/:id - Get user by ID
router.get('/:id', (req, res) => {
  try {
    const id = parseInt(req.params.id);
    const user = users.find(u => u.id === id);
    
    if (!user) {
      return res.status(404).json({ 
        code: 'USER_NOT_FOUND',
        message: `User with ID ${id} not found` 
      });
    }
    
    // Type-safe response
    const response: User = user;
    res.json(response);
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
// POST /users - Create new user
router.post('/', (req, res) => {
  try {
    // Type-safe request body
    const userData: CreateUserRequest = req.body;
    
    // Validation (could use middleware)
    if (!userData.name || !userData.email) {
      return res.status(400).json({
        code: 'VALIDATION_ERROR',
        message: 'Name and email are required'
      });
    }
    
    // Check for duplicate email
    if (users.some(u => u.email === userData.email)) {
      return res.status(400).json({
        code: 'EMAIL_EXISTS',
        message: 'User with this email already exists'
      });
    }
    
    // Create new user
    const newUser: User = {
      id: nextId++,
      name: userData.name,
      email: userData.email,
      isActive: userData.isActive ?? true
    };
    
    users.push(newUser);
    
    // Type-safe response
    res.status(201).json(newUser);
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
// PUT /users/:id - Update user
router.put('/:id', (req, res) => {
  try {
    const id = parseInt(req.params.id);
    const userData: UpdateUserRequest = req.body;
    
    const userIndex = users.findIndex(u => u.id === id);
    if (userIndex === -1) {
      return res.status(404).json({
        code: 'USER_NOT_FOUND',
        message: `User with ID ${id} not found`
      });
    }
    
    // Update user (merge with existing data)
    const updatedUser: User = {
      ...users[userIndex],
      ...userData,
      id // Ensure ID doesn't change
    };
    
    users[userIndex] = updatedUser;
    
    res.json(updatedUser);
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
// DELETE /users/:id - Delete user
router.delete('/:id', (req, res) => {
  try {
    const id = parseInt(req.params.id);
    const userIndex = users.findIndex(u => u.id === id);
    
    if (userIndex === -1) {
      return res.status(404).json({
        code: 'USER_NOT_FOUND',
        message: `User with ID ${id} not found`
      });
    }
    
    users.splice(userIndex, 1);
    res.status(204).send();
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
export default router;
Express Server Setup
// src/server.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
import userRoutes from './routes/users';
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(helmet());
app.use(morgan('combined'));
app.use(cors({
  origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
  credentials: true
}));
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Routes
app.use('/api/users', userRoutes);
// Error handling middleware
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  console.error('Error:', err);
  
  res.status(500).json({
    code: 'INTERNAL_ERROR',
    message: 'Internal server error',
    ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
  });
});
// 404 handler
app.use('*', (req, res) => {
  res.status(404).json({
    code: 'NOT_FOUND',
    message: `Route ${req.originalUrl} not found`
  });
});
// Start server
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
  console.log(`API available at http://localhost:${PORT}/api`);
});
export default app;
Request/Response Validation Middleware
Schema Validation Middleware
// src/middleware/validation.ts
import type { Request, Response, NextFunction } from 'express';
import type { CreateUserRequest, UpdateUserRequest } from '../types/api';
// Generic validation function
function validateSchema<T>(data: unknown, validator: (data: unknown) => data is T): T {
  if (!validator(data)) {
    throw new Error('Invalid data format');
  }
  return data;
}
// Type guards for request validation
function isCreateUserRequest(data: unknown): data is CreateUserRequest {
  if (!data || typeof data !== 'object') return false;
  const obj = data as Record<string, unknown>;
  
  return (
    typeof obj.name === 'string' &&
    typeof obj.email === 'string' &&
    obj.email.includes('@') &&
    (obj.isActive === undefined || typeof obj.isActive === 'boolean')
  );
}
function isUpdateUserRequest(data: unknown): data is UpdateUserRequest {
  if (!data || typeof data !== 'object') return false;
  const obj = data as Record<string, unknown>;
  
  return (
    (obj.name === undefined || typeof obj.name === 'string') &&
    (obj.email === undefined || (typeof obj.email === 'string' && obj.email.includes('@'))) &&
    (obj.isActive === undefined || typeof obj.isActive === 'boolean')
  );
}
// Validation middleware factories
export const validateCreateUser = (req: Request, res: Response, next: NextFunction) => {
  try {
    req.body = validateSchema(req.body, isCreateUserRequest);
    next();
  } catch (error) {
    res.status(400).json({
      code: 'VALIDATION_ERROR',
      message: 'Invalid user data format',
      details: error.message
    });
  }
};
export const validateUpdateUser = (req: Request, res: Response, next: NextFunction) => {
  try {
    req.body = validateSchema(req.body, isUpdateUserRequest);
    next();
  } catch (error) {
    res.status(400).json({
      code: 'VALIDATION_ERROR',
      message: 'Invalid user data format',
      details: error.message
    });
  }
};
// Parameter validation
export const validateUserId = (req: Request, res: Response, next: NextFunction) => {
  const id = parseInt(req.params.id);
  
  if (isNaN(id) || id <= 0) {
    return res.status(400).json({
      code: 'INVALID_USER_ID',
      message: 'User ID must be a positive integer'
    });
  }
  
  // Store parsed ID for use in route handlers
  req.params.id = id.toString();
  next();
};
Using Validation Middleware
// src/routes/users.ts (updated with validation)
import express from 'express';
import { validateCreateUser, validateUpdateUser, validateUserId } from '../middleware/validation';
const router = express.Router();
// Apply validation middleware to routes
router.post('/', validateCreateUser, (req, res) => {
  // req.body is now guaranteed to be a valid CreateUserRequest
  const userData = req.body; // TypeScript knows this is CreateUserRequest
  // ... rest of the handler
});
router.put('/:id', validateUserId, validateUpdateUser, (req, res) => {
  // req.params.id is validated and req.body is valid UpdateUserRequest
  const id = parseInt(req.params.id);
  const userData = req.body;
  // ... rest of the handler
});
router.get('/:id', validateUserId, (req, res) => {
  // req.params.id is validated
  const id = parseInt(req.params.id);
  // ... rest of the handler
});
router.delete('/:id', validateUserId, (req, res) => {
  // req.params.id is validated
  const id = parseInt(req.params.id);
  // ... rest of the handler
});
export default router;
Database Integration
Mongoose Integration
// src/models/User.ts
import mongoose, { Schema, Document } from 'mongoose';
import type { User as UserType } from '../types/api';
// Extend the generated type with MongoDB Document
export interface UserDocument extends UserType, Document {}
const userSchema = new Schema<UserDocument>({
  name: {
    type: String,
    required: true,
    trim: true,
    maxlength: 100
  },
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
    validate: {
      validator: (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email),
      message: 'Invalid email format'
    }
  },
  isActive: {
    type: Boolean,
    default: true
  }
}, {
  timestamps: true, // Adds createdAt and updatedAt
  toJSON: {
    transform: (doc, ret) => {
      ret.id = ret._id;
      delete ret._id;
      delete ret.__v;
      return ret;
    }
  }
});
export const User = mongoose.model<UserDocument>('User', userSchema);
Database Service Layer
// src/services/userService.ts
import { User, UserDocument } from '../models/User';
import type { User as UserType, CreateUserRequest, UpdateUserRequest } from '../types/api';
export class UserService {
  async getAllUsers(): Promise<UserType[]> {
    const users = await User.find().lean();
    return users.map(user => ({
      id: user._id.toString(),
      name: user.name,
      email: user.email,
      isActive: user.isActive
    }));
  }
  async getUserById(id: string): Promise<UserType | null> {
    const user = await User.findById(id).lean();
    if (!user) return null;
    
    return {
      id: user._id.toString(),
      name: user.name,
      email: user.email,
      isActive: user.isActive
    };
  }
  async createUser(userData: CreateUserRequest): Promise<UserType> {
    // Check for existing email
    const existingUser = await User.findOne({ email: userData.email });
    if (existingUser) {
      throw new Error('User with this email already exists');
    }
    const user = new User(userData);
    await user.save();
    
    return {
      id: user._id.toString(),
      name: user.name,
      email: user.email,
      isActive: user.isActive
    };
  }
  async updateUser(id: string, userData: UpdateUserRequest): Promise<UserType | null> {
    // Check for email conflicts if email is being updated
    if (userData.email) {
      const existingUser = await User.findOne({ 
        email: userData.email, 
        _id: { $ne: id } 
      });
      if (existingUser) {
        throw new Error('User with this email already exists');
      }
    }
    const user = await User.findByIdAndUpdate(
      id,
      userData,
      { new: true, runValidators: true }
    ).lean();
    if (!user) return null;
    
    return {
      id: user._id.toString(),
      name: user.name,
      email: user.email,
      isActive: user.isActive
    };
  }
  async deleteUser(id: string): Promise<boolean> {
    const result = await User.findByIdAndDelete(id);
    return result !== null;
  }
  async getUsersByEmail(email: string): Promise<UserType[]> {
    const users = await User.find({ email: new RegExp(email, 'i') }).lean();
    return users.map(user => ({
      id: user._id.toString(),
      name: user.name,
      email: user.email,
      isActive: user.isActive
    }));
  }
}
Updated Routes with Database Service
// src/routes/users.ts (with database)
import express from 'express';
import { UserService } from '../services/userService';
import { validateCreateUser, validateUpdateUser, validateUserId } from '../middleware/validation';
const router = express.Router();
const userService = new UserService();
router.get('/', async (req, res) => {
  try {
    const users = await userService.getAllUsers();
    res.json(users);
  } catch (error) {
    console.error('Error fetching users:', error);
    res.status(500).json({
      code: 'INTERNAL_ERROR',
      message: 'Failed to fetch users'
    });
  }
});
router.get('/:id', validateUserId, async (req, res) => {
  try {
    const user = await userService.getUserById(req.params.id);
    
    if (!user) {
      return res.status(404).json({
        code: 'USER_NOT_FOUND',
        message: `User with ID ${req.params.id} not found`
      });
    }
    
    res.json(user);
  } catch (error) {
    console.error('Error fetching user:', error);
    res.status(500).json({
      code: 'INTERNAL_ERROR',
      message: 'Failed to fetch user'
    });
  }
});
router.post('/', validateCreateUser, async (req, res) => {
  try {
    const newUser = await userService.createUser(req.body);
    res.status(201).json(newUser);
  } catch (error) {
    console.error('Error creating user:', error);
    
    if (error.message.includes('already exists')) {
      return res.status(400).json({
        code: 'EMAIL_EXISTS',
        message: error.message
      });
    }
    
    res.status(500).json({
      code: 'INTERNAL_ERROR',
      message: 'Failed to create user'
    });
  }
});
router.put('/:id', validateUserId, validateUpdateUser, async (req, res) => {
  try {
    const updatedUser = await userService.updateUser(req.params.id, req.body);
    
    if (!updatedUser) {
      return res.status(404).json({
        code: 'USER_NOT_FOUND',
        message: `User with ID ${req.params.id} not found`
      });
    }
    
    res.json(updatedUser);
  } catch (error) {
    console.error('Error updating user:', error);
    
    if (error.message.includes('already exists')) {
      return res.status(400).json({
        code: 'EMAIL_EXISTS',
        message: error.message
      });
    }
    
    res.status(500).json({
      code: 'INTERNAL_ERROR',
      message: 'Failed to update user'
    });
  }
});
router.delete('/:id', validateUserId, async (req, res) => {
  try {
    const deleted = await userService.deleteUser(req.params.id);
    
    if (!deleted) {
      return res.status(404).json({
        code: 'USER_NOT_FOUND',
        message: `User with ID ${req.params.id} not found`
      });
    }
    
    res.status(204).send();
  } catch (error) {
    console.error('Error deleting user:', error);
    res.status(500).json({
      code: 'INTERNAL_ERROR',
      message: 'Failed to delete user'
    });
  }
});
export default router;
Testing with SpecJet
API Testing with Generated Client
// test/api.test.ts
import request from 'supertest';
import { ApiClient } from '../src/client/client';
import type { User, CreateUserRequest } from '../src/types/api';
describe('User API', () => {
  const apiClient = new ApiClient('http://localhost:3000/api');
  
  beforeAll(async () => {
    // Start your Express server here
    // Or ensure it's running on port 3000
  });
  describe('GET /users', () => {
    test('should return array of users', async () => {
      const users = await apiClient.getUsers();
      
      expect(Array.isArray(users)).toBe(true);
      
      if (users.length > 0) {
        const user = users[0];
        expect(user).toHaveProperty('id');
        expect(user).toHaveProperty('name');
        expect(user).toHaveProperty('email');
        expect(user).toHaveProperty('isActive');
        
        // Type checking at compile time
        expect(typeof user.id).toBe('number');
        expect(typeof user.name).toBe('string');
        expect(typeof user.email).toBe('string');
        expect(typeof user.isActive).toBe('boolean');
      }
    });
  });
  describe('POST /users', () => {
    test('should create new user', async () => {
      const newUserData: CreateUserRequest = {
        name: 'Test User',
        email: 'test@example.com',
        isActive: true
      };
      const createdUser = await apiClient.createUser(newUserData);
      
      expect(createdUser).toHaveProperty('id');
      expect(createdUser.name).toBe(newUserData.name);
      expect(createdUser.email).toBe(newUserData.email);
      expect(createdUser.isActive).toBe(newUserData.isActive);
    });
    test('should reject invalid user data', async () => {
      const invalidData = {
        name: 'Test User'
        // Missing required email
      };
      await expect(
        apiClient.createUser(invalidData as CreateUserRequest)
      ).rejects.toThrow();
    });
  });
  describe('GET /users/:id', () => {
    test('should return specific user', async () => {
      // First create a user
      const newUser = await apiClient.createUser({
        name: 'Specific User',
        email: 'specific@example.com',
        isActive: true
      });
      // Then fetch it by ID
      const fetchedUser = await apiClient.getUserById(newUser.id);
      
      expect(fetchedUser.id).toBe(newUser.id);
      expect(fetchedUser.name).toBe(newUser.name);
      expect(fetchedUser.email).toBe(newUser.email);
    });
    test('should return 404 for non-existent user', async () => {
      await expect(
        apiClient.getUserById(999999)
      ).rejects.toThrow('404');
    });
  });
});
Integration Testing with Mock Server
// test/integration.test.ts
import { spawn, ChildProcess } from 'child_process';
import { ApiClient } from '../src/client/client';
describe('API Integration Tests', () => {
  let mockServer: ChildProcess;
  let apiClient: ApiClient;
  beforeAll(async () => {
    // Start mock server for testing
    mockServer = spawn('specjet', ['mock', '--port', '3001', '--scenario', 'demo'], {
      stdio: 'pipe'
    });
    // Wait for server to start
    await new Promise(resolve => setTimeout(resolve, 2000));
    
    apiClient = new ApiClient('http://localhost:3001');
  });
  afterAll(() => {
    if (mockServer) {
      mockServer.kill();
    }
  });
  test('mock server provides consistent demo data', async () => {
    const users = await apiClient.getUsers();
    
    // Demo scenario should provide consistent data
    expect(users).toHaveLength(3);
    expect(users[0].name).toBe('Alice Johnson');
    expect(users[1].name).toBe('Bob Smith');
    expect(users[2].name).toBe('Carol Williams');
  });
  test('can create user through mock server', async () => {
    const newUser = await apiClient.createUser({
      name: 'Test User',
      email: 'test@example.com',
      isActive: true
    });
    expect(newUser).toHaveProperty('id');
    expect(newUser.name).toBe('Test User');
  });
});
Contract Validation Testing
// test/contract-validation.test.ts
import { spawn } from 'child_process';
describe('Contract Validation', () => {
  test('API implementation matches contract', (done) => {
    // Assume your API server is running on port 3000
    const validationProcess = spawn('specjet', [
      'validate', 
      'http://localhost:3000/api',
      '--format', 'json'
    ]);
    let output = '';
    
    validationProcess.stdout.on('data', (data) => {
      output += data.toString();
    });
    validationProcess.on('close', (code) => {
      try {
        const results = JSON.parse(output);
        
        // Check that validation passed
        expect(code).toBe(0);
        expect(results.summary.coverage).toBeGreaterThan(0.8); // 80% coverage
        expect(results.summary.failed).toBe(0);
        
        done();
      } catch (error) {
        done(error);
      }
    });
  }, 30000); // 30 second timeout
});
Development Workflow
Contract-First Development Process
- Design API Contract: Start with 
api-contract.yaml - Generate Types: Run 
specjet generateto create TypeScript types - Implement Routes: Use generated types in Express routes
 - Test with Mock Server: Develop against consistent mock data
 - Validate Implementation: Use 
specjet validateto check compliance - Deploy with Confidence: Know your API matches the contract
 
Scripts for Development Flow
{
  "scripts": {
    "dev": "concurrently \"npm run api:watch\" \"nodemon src/server.ts\"",
    "api:watch": "specjet generate --watch",
    "api:mock": "specjet mock --scenario realistic --port 3001",
    "api:validate:dev": "specjet validate http://localhost:3000/api",
    "test:unit": "jest src/",
    "test:integration": "npm run api:mock & sleep 2 && jest test/integration && kill %1",
    "test:contract": "npm run api:validate:dev && echo 'Contract validation passed'",
    "precommit": "npm run api:generate && npm run test:contract"
  }
}
Environment Configuration
// src/config/index.ts
import type { User } from '../types/api';
interface DatabaseConfig {
  url: string;
  options: Record<string, unknown>;
}
interface ApiConfig {
  port: number;
  corsOrigin: string;
  database: DatabaseConfig;
  mockServerUrl?: string;
}
const config: ApiConfig = {
  port: parseInt(process.env.PORT || '3000'),
  corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:3000',
  
  database: {
    url: process.env.DATABASE_URL || 'mongodb://localhost:27017/myapp',
    options: {
      useNewUrlParser: true,
      useUnifiedTopology: true
    }
  },
  
  // For development/testing
  mockServerUrl: process.env.MOCK_SERVER_URL || 'http://localhost:3001'
};
export default config;
Microservices Integration
Service-to-Service Communication
// src/services/external/userService.ts
import { ApiClient } from '../../client/client';
import type { User, CreateUserRequest } from '../../types/api';
export class ExternalUserService {
  private client: ApiClient;
  constructor(baseUrl: string) {
    this.client = new ApiClient(baseUrl);
    
    // Set authentication if needed
    const apiKey = process.env.USER_SERVICE_API_KEY;
    if (apiKey) {
      this.client.setApiKey(apiKey);
    }
  }
  async getUser(id: number): Promise<User | null> {
    try {
      return await this.client.getUserById(id);
    } catch (error) {
      if (error.message.includes('404')) {
        return null;
      }
      throw error;
    }
  }
  async createUser(userData: CreateUserRequest): Promise<User> {
    return await this.client.createUser(userData);
  }
  async validateUserExists(id: number): Promise<boolean> {
    const user = await this.getUser(id);
    return user !== null && user.isActive;
  }
}
API Gateway Pattern
// src/gateway/userGateway.ts
import { ExternalUserService } from '../services/external/userService';
import type { User } from '../types/api';
export class UserGateway {
  private userService: ExternalUserService;
  private cache = new Map<number, { user: User; timestamp: number }>();
  private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes
  constructor() {
    const userServiceUrl = process.env.USER_SERVICE_URL || 'http://localhost:3001';
    this.userService = new ExternalUserService(userServiceUrl);
  }
  async getUser(id: number): Promise<User | null> {
    // Check cache first
    const cached = this.cache.get(id);
    if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
      return cached.user;
    }
    // Fetch from service
    const user = await this.userService.getUser(id);
    
    // Cache result
    if (user) {
      this.cache.set(id, { user, timestamp: Date.now() });
    }
    
    return user;
  }
  async validateUser(id: number): Promise<boolean> {
    const user = await this.getUser(id);
    return user !== null && user.isActive;
  }
  clearCache(): void {
    this.cache.clear();
  }
}
Best Practices
- Use Generated Types Everywhere: Leverage SpecJet types in all layers
 - Implement Validation Middleware: Validate requests against contract
 - Test Against Contract: Use 
specjet validatein CI/CD - Version Your APIs: Plan for contract evolution
 - Monitor Contract Compliance: Regular validation checks
 - Document with Examples: Use realistic examples in contract
 - Cache Service Calls: Implement caching for external API calls
 
Deployment Considerations
Docker Integration
# Dockerfile
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY src/ ./src/
COPY api-contract.yaml ./
COPY specjet.config.js ./
# Generate types at build time
RUN npx specjet generate
# Build TypeScript
RUN npm run build
# Expose port
EXPOSE 3000
# Start server
CMD ["npm", "start"]
CI/CD Pipeline
# .github/workflows/api-validation.yml
name: API Validation
on: [push, pull_request]
jobs:
  validate-contract:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Generate API types
        run: npm run api:generate
        
      - name: Build application
        run: npm run build
        
      - name: Start test server
        run: npm start &
        
      - name: Wait for server
        run: sleep 10
        
      - name: Validate API contract
        run: npm run api:validate:dev
        
      - name: Run integration tests
        run: npm run test:integration
Troubleshooting
Common Issues
1. Type Generation Fails
# Check contract syntax
specjet generate --verbose
# Verify contract is valid OpenAPI
npm run api:generate
2. Validation Fails
# Test with mock server first
npm run api:mock
specjet validate http://localhost:3001
# Then test your implementation
specjet validate http://localhost:3000/api
3. CORS Issues
// Configure CORS properly
app.use(cors({
  origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
  credentials: true
}));
4. Database Type Mismatches
// Transform database objects to match contract types
const transformUser = (dbUser: UserDocument): User => ({
  id: dbUser._id.toString(), // Convert ObjectId to string
  name: dbUser.name,
  email: dbUser.email,
  isActive: dbUser.isActive
});
Next Steps
- React Integration: Frontend integration patterns
 - Vue Integration: Vue.js integration patterns
 - Best Practices: OpenAPI design recommendations
 - Configuration: Advanced configuration options