Skip to content

REST API Implementation Plan - Laravel-DDD CRM System

Overview

This document outlines the comprehensive plan for implementing REST APIs that serve both Single Page Application (SPA) and third-party consumers. The API design follows RESTful principles with Laravel best practices while maintaining clean domain boundaries.

API Strategy & Architecture

Dual-Purpose API Design

Our API will serve both SPA and third-party applications with the following architecture:

┌─────────────────────────────────────────────────────────────┐
│                    API Gateway Layer                       │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────────┐    ┌─────────────────────────────────┐ │
│  │   SPA Routes    │    │    Public API Routes           │ │
│  │   /spa/api/v1   │    │    /api/v1                     │ │
│  │                 │    │                                 │ │
│  │ - Session Auth  │    │ - Token Auth                    │ │
│  │ - CSRF Protected│    │ - Rate Limited                  │ │
│  │ - Rich Responses│    │ - Standard Responses            │ │
│  │ - Eager Loading │    │ - Minimal Payloads              │ │
│  └─────────────────┘    └─────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│                  Shared Business Logic                     │
│              Controllers → Services → Domain               │
└─────────────────────────────────────────────────────────────┘

Authentication Strategy

SPA Authentication (Session-based):

  • Laravel Sanctum stateful authentication
  • CSRF protection enabled
  • Same-origin policy enforced
  • Session cookies for state management

Third-Party API Authentication (Token-based):

  • Laravel Sanctum API tokens
  • Bearer token authentication
  • Scoped permissions per token
  • Rate limiting and throttling

API Versioning Strategy

URL Structure

# SPA API (Internal)
https://domain.com/spa/api/v1/{module}/{resource}

# Public API (Third-party)
https://domain.com/api/v1/{module}/{resource}

Version Management

  • Current Version: v1 (stable)
  • Future Versions: v2, v3 (backward compatible when possible)
  • Deprecation Policy: 12 months notice for breaking changes
  • Header Versioning: Accept version in headers as alternative

Multi-Tenancy API Design

Tenant Resolution

php
// Middleware will resolve tenant from:
1. Subdomain: tenant1.crm.test
2. Header: X-Tenant-ID: tenant1
3. Token scope: tenant:tenant1

Tenant Context

  • All API calls automatically scoped to tenant
  • No tenant ID in URL paths (security)
  • Automatic tenant validation on every request
  • Cross-tenant access prevention

API Response Standards

Standard Response Format

Success Response:

json
{
  "success": true,
  "data": {
    // Resource data or collection
  },
  "meta": {
    "timestamp": "2024-01-15T10:30:00Z",
    "version": "v1",
    "tenant": "tenant1"
  }
}

Collection Response:

json
{
  "success": true,
  "data": [
    // Array of resources
  ],
  "pagination": {
    "current_page": 1,
    "last_page": 10,
    "per_page": 25,
    "total": 250,
    "from": 1,
    "to": 25
  },
  "meta": {
    "timestamp": "2024-01-15T10:30:00Z",
    "version": "v1",
    "tenant": "tenant1"
  }
}

Error Response:

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The given data was invalid.",
    "details": {
      "email": ["The email field is required."],
      "name": ["The name field must be at least 3 characters."]
    }
  },
  "meta": {
    "timestamp": "2024-01-15T10:30:00Z",
    "version": "v1",
    "tenant": "tenant1"
  }
}

HTTP Status Codes

  • 200 OK: Successful GET, PUT, PATCH
  • 201 Created: Successful POST
  • 204 No Content: Successful DELETE
  • 400 Bad Request: Invalid request format
  • 401 Unauthorized: Authentication required
  • 403 Forbidden: Insufficient permissions
  • 404 Not Found: Resource not found
  • 422 Unprocessable Entity: Validation errors
  • 429 Too Many Requests: Rate limit exceeded
  • 500 Internal Server Error: Server error

Module-by-Module API Implementation

1. CRM Module API

Lead Management API

Base Path: /api/v1/crm/leads

Endpoints

GET /api/v1/crm/leads

php
// Query Parameters
?status=new,qualified
?source=website,referral
?assigned_to=user_id
?score_min=50
?score_max=100
?search=email,name,company
?created_after=2024-01-01
?created_before=2024-12-31
?page=1
?per_page=25
?sort=created_at
?order=desc
?include=assigned_user,activities,customer

Response Example:

json
{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "lead_number": "LEAD000001",
      "email": "[email protected]",
      "status": "qualified",
      "source": "website",
      "contact_info": {
        "first_name": "John",
        "last_name": "Doe",
        "company": "Tech Corp",
        "job_title": "CTO",
        "phone": "+1234567890"
      },
      "score": {
        "total": 85,
        "behavioral": 50,
        "demographic": 35,
        "rating": "hot"
      },
      "estimated_value": 50000.00,
      "currency": "USD",
      "assigned_to": {
        "id": "user123",
        "name": "Jane Smith",
        "email": "[email protected]"
      },
      "tags": ["enterprise", "priority"],
      "custom_fields": {
        "budget": 100000,
        "timeline": "Q2 2024"
      },
      "qualified_at": "2024-01-10T15:30:00Z",
      "last_activity_at": "2024-01-15T09:15:00Z",
      "created_at": "2024-01-01T12:00:00Z",
      "updated_at": "2024-01-15T09:15:00Z"
    }
  ],
  "pagination": {
    "current_page": 1,
    "last_page": 5,
    "per_page": 25,
    "total": 125
  }
}

POST /api/v1/crm/leads

json
{
  "email": "[email protected]",
  "source": "website",
  "contact_info": {
    "first_name": "New",
    "last_name": "Lead",
    "company": "Example Corp",
    "job_title": "Manager",
    "phone": "+1234567890"
  },
  "estimated_value": 25000,
  "currency": "USD",
  "notes": "Interested in enterprise solution",
  "tags": ["website", "demo-requested"],
  "custom_fields": {
    "industry": "technology",
    "company_size": 50
  }
}

PUT /api/v1/crm/leads/{id}GET /api/v1/crm/leads/{id}DELETE /api/v1/crm/leads/{id}

Lead Actions

POST /api/v1/crm/leads/{id}/qualify

json
{
  "notes": "Met qualification criteria",
  "assigned_to": "user123"
}

POST /api/v1/crm/leads/{id}/convert

json
{
  "customer_data": {
    "name": "Tech Corp",
    "type": "business",
    "industry": "technology"
  },
  "create_opportunity": true,
  "opportunity_data": {
    "name": "Tech Corp - Enterprise License",
    "amount": 50000,
    "stage": "prospecting"
  }
}

POST /api/v1/crm/leads/{id}/assign

json
{
  "assigned_to": "user123",
  "notify": true
}

GET /api/v1/crm/leads/{id}/activitiesPOST /api/v1/crm/leads/{id}/activitiesGET /api/v1/crm/leads/{id}/score-history

Customer Management API

Base Path: /api/v1/crm/customers

Customer Endpoints

GET /api/v1/crm/customersPOST /api/v1/crm/customersGET /api/v1/crm/customers/{id}PUT /api/v1/crm/customers/{id}DELETE /api/v1/crm/customers/{id}

Customer Response Example:

json
{
  "id": "customer123",
  "customer_number": "CUST000001",
  "name": "Tech Corp",
  "email": "[email protected]",
  "phone": "+1234567890",
  "type": "business",
  "status": "active",
  "industry": "technology",
  "company_size": "large",
  "annual_revenue": 5000000,
  "website": "https://techcorp.com",
  "billing_address": {
    "line_1": "123 Tech Street",
    "line_2": "Suite 100",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94105",
    "country": "US"
  },
  "primary_contact": {
    "first_name": "John",
    "last_name": "Doe",
    "email": "[email protected]",
    "phone": "+1234567890",
    "job_title": "CTO"
  },
  "assigned_account_manager": {
    "id": "user123",
    "name": "Jane Smith"
  },
  "lifecycle_stage": "customer",
  "lifetime_value": 150000.00,
  "acquisition_date": "2024-01-01",
  "tags": ["enterprise", "technology"],
  "custom_fields": {
    "crm_system": "salesforce",
    "contract_type": "annual"
  }
}

Customer Actions

GET /api/v1/crm/customers/{id}/ordersGET /api/v1/crm/customers/{id}/invoicesGET /api/v1/crm/customers/{id}/paymentsGET /api/v1/crm/customers/{id}/activitiesGET /api/v1/crm/customers/{id}/opportunitiesGET /api/v1/crm/customers/{id}/metrics

Opportunity Management API

Base Path: /api/v1/crm/opportunities

Similar structure to leads and customers with opportunity-specific fields and actions.

2. Sales Module API

Order Management API

Base Path: /api/v1/sales/orders

Order Endpoints

GET /api/v1/sales/orders

php
// Query Parameters
?status=confirmed,processing,shipped
?customer_id=customer123
?date_from=2024-01-01
?date_to=2024-12-31
?amount_min=1000
?amount_max=50000
?include=customer,items,payments

Order Response Example:

json
{
  "id": "order123",
  "order_number": "ORD000001",
  "customer": {
    "id": "customer123",
    "name": "Tech Corp",
    "email": "[email protected]"
  },
  "status": "confirmed",
  "type": "sale",
  "order_date": "2024-01-15",
  "required_date": "2024-01-30",
  "subtotal": 45000.00,
  "tax_amount": 4500.00,
  "shipping_amount": 500.00,
  "discount_amount": 2000.00,
  "total_amount": 48000.00,
  "currency": "USD",
  "payment_terms": "net_30",
  "billing_address": {
    "line_1": "123 Tech Street",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94105",
    "country": "US"
  },
  "items": [
    {
      "id": "item1",
      "product": {
        "id": "product123",
        "name": "Enterprise License",
        "sku": "ENT-LIC-001"
      },
      "quantity": 10,
      "unit_price": 5000.00,
      "discount_percentage": 10,
      "discount_amount": 5000.00,
      "tax_rate": 10,
      "tax_amount": 4500.00,
      "line_total": 45000.00
    }
  ],
  "assigned_to": {
    "id": "user123",
    "name": "Sales Rep"
  }
}

Order Actions

POST /api/v1/sales/orders/{id}/confirmPOST /api/v1/sales/orders/{id}/shipPOST /api/v1/sales/orders/{id}/deliverPOST /api/v1/sales/orders/{id}/cancelPOST /api/v1/sales/orders/{id}/return

Invoice Management API

Base Path: /api/v1/sales/invoices

Payment Management API

Base Path: /api/v1/sales/payments

3. Operations Module API

Product Management API

Base Path: /api/v1/operations/products

Inventory Management API

Base Path: /api/v1/operations/inventory

Purchase Order API

Base Path: /api/v1/operations/purchase-orders

4. Finance Module API

Chart of Accounts API

Base Path: /api/v1/finance/chart-of-accounts

Journal Entries API

Base Path: /api/v1/finance/journal-entries

5. HRM Module API

Employee Management API

Base Path: /api/v1/hrm/employees

Time Tracking API

Base Path: /api/v1/hrm/time-entries

Leave Management API

Base Path: /api/v1/hrm/leaves

6. Analytics Module API

Metrics API

Base Path: /api/v1/analytics/metrics

Dashboard API

Base Path: /api/v1/analytics/dashboards

Reports API

Base Path: /api/v1/analytics/reports

API Middleware Stack

Authentication Middleware

php
// For SPA routes
Route::middleware(['web', 'auth:sanctum'])->group(function () {
    // SPA routes
});

// For API routes
Route::middleware(['api', 'auth:sanctum', 'throttle:api'])->group(function () {
    // Public API routes
});

Tenant Resolution Middleware

php
class ResolveTenantMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $tenant = $this->resolveTenant($request);
        
        if (!$tenant) {
            return response()->json(['error' => 'Invalid tenant'], 403);
        }
        
        // Set tenant context
        app()->instance('tenant', $tenant);
        
        return $next($request);
    }
    
    private function resolveTenant(Request $request): ?Tenant
    {
        // Try subdomain first
        if ($tenant = $this->getTenantFromSubdomain($request)) {
            return $tenant;
        }
        
        // Try header
        if ($tenant = $this->getTenantFromHeader($request)) {
            return $tenant;
        }
        
        // Try token scope
        if ($tenant = $this->getTenantFromToken($request)) {
            return $tenant;
        }
        
        return null;
    }
}

API Response Middleware

php
class ApiResponseMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);
        
        if ($response instanceof JsonResponse) {
            $data = $response->getData(true);
            
            $wrappedData = [
                'success' => $response->status() < 400,
                'data' => $data,
                'meta' => [
                    'timestamp' => now()->toISOString(),
                    'version' => 'v1',
                    'tenant' => app('tenant')->slug ?? null,
                ]
            ];
            
            $response->setData($wrappedData);
        }
        
        return $response;
    }
}

Rate Limiting

php
// config/sanctum.php
'api_throttle' => [
    'attempts' => 60,  // requests per minute
    'decay_minutes' => 1,
],

'spa_throttle' => [
    'attempts' => 300,  // higher limit for SPA
    'decay_minutes' => 1,
],

Error Handling Strategy

Global Exception Handler

php
class ApiExceptionHandler extends Handler
{
    public function render($request, Exception $exception)
    {
        if ($request->expectsJson()) {
            return $this->handleApiException($request, $exception);
        }
        
        return parent::render($request, $exception);
    }
    
    protected function handleApiException($request, Exception $exception)
    {
        $statusCode = 500;
        $errorCode = 'INTERNAL_ERROR';
        $message = 'An error occurred';
        $details = null;
        
        switch (true) {
            case $exception instanceof ValidationException:
                $statusCode = 422;
                $errorCode = 'VALIDATION_ERROR';
                $message = 'The given data was invalid.';
                $details = $exception->errors();
                break;
                
            case $exception instanceof ModelNotFoundException:
                $statusCode = 404;
                $errorCode = 'RESOURCE_NOT_FOUND';
                $message = 'The requested resource was not found.';
                break;
                
            case $exception instanceof AuthenticationException:
                $statusCode = 401;
                $errorCode = 'AUTHENTICATION_REQUIRED';
                $message = 'Authentication is required.';
                break;
                
            case $exception instanceof AuthorizationException:
                $statusCode = 403;
                $errorCode = 'INSUFFICIENT_PERMISSIONS';
                $message = 'You do not have permission to perform this action.';
                break;
                
            case $exception instanceof ThrottleRequestsException:
                $statusCode = 429;
                $errorCode = 'RATE_LIMIT_EXCEEDED';
                $message = 'Too many requests. Please try again later.';
                break;
        }
        
        return response()->json([
            'success' => false,
            'error' => [
                'code' => $errorCode,
                'message' => $message,
                'details' => $details,
            ],
            'meta' => [
                'timestamp' => now()->toISOString(),
                'version' => 'v1',
                'tenant' => app('tenant')->slug ?? null,
            ]
        ], $statusCode);
    }
}

API Documentation Strategy

OpenAPI/Swagger Specification

  • Generate comprehensive API documentation
  • Interactive API explorer
  • Code examples for multiple languages
  • Authentication flow documentation

Documentation Structure

docs/api/
├── openapi.yaml              # Master OpenAPI spec
├── modules/
│   ├── crm.yaml             # CRM endpoints
│   ├── sales.yaml           # Sales endpoints
│   ├── operations.yaml      # Operations endpoints
│   ├── finance.yaml         # Finance endpoints
│   ├── hrm.yaml             # HRM endpoints
│   └── analytics.yaml       # Analytics endpoints
├── authentication.md        # Auth documentation
├── rate-limiting.md         # Rate limiting info
├── errors.md               # Error codes and handling
├── pagination.md           # Pagination guidelines
├── filtering.md            # Filtering and search
└── webhooks.md             # Webhook documentation

Performance Optimization

Caching Strategy

php
// API Response Caching
Route::middleware(['cache:5'])->group(function () {
    // Cacheable endpoints
});

// Query Result Caching
$leads = Cache::remember("leads:tenant:{$tenantId}:filters:{$filtersHash}", 300, function () {
    return Lead::with(['assignedUser'])->filtered($filters)->paginate();
});

Database Optimization

  • Eager loading for relationships
  • Database indexes for common queries
  • Query optimization for large datasets
  • Connection pooling for high concurrency

API Response Optimization

php
// Conditional field loading
public function toArray($request)
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'activities' => $this->whenLoaded('activities'),
        'detailed_stats' => $this->when($request->include_stats, function () {
            return $this->calculateDetailedStats();
        }),
    ];
}

Security Considerations

API Security Measures

  1. Authentication: Multi-layer auth with tokens and sessions
  2. Authorization: Role-based access control
  3. Rate Limiting: Prevent abuse and DoS attacks
  4. Input Validation: Comprehensive request validation
  5. SQL Injection Prevention: Eloquent ORM protection
  6. XSS Prevention: Output sanitization
  7. CSRF Protection: For SPA endpoints
  8. HTTPS Enforcement: SSL/TLS for all endpoints

Data Privacy

  • Personal data encryption
  • Audit logging for sensitive operations
  • GDPR compliance features
  • Data retention policies

Testing Strategy

API Testing Levels

  1. Unit Tests: Individual endpoint logic
  2. Integration Tests: Database and service integration
  3. Feature Tests: Complete API workflows
  4. Performance Tests: Load and stress testing
  5. Security Tests: Penetration testing

Test Examples

php
// Feature test example
public function test_can_create_lead_via_api()
{
    $user = User::factory()->create();
    $this->actingAs($user, 'sanctum');
    
    $response = $this->postJson('/api/v1/crm/leads', [
        'email' => '[email protected]',
        'source' => 'website',
        'contact_info' => [
            'first_name' => 'John',
            'last_name' => 'Doe',
        ],
    ]);
    
    $response->assertStatus(201)
            ->assertJsonStructure([
                'success',
                'data' => ['id', 'email', 'status'],
                'meta' => ['timestamp', 'version', 'tenant']
            ]);
}

Implementation Phases

Phase 1: Foundation (Weeks 1-2)

  • Base API infrastructure
  • Authentication and authorization
  • Tenant resolution middleware
  • Response formatting
  • Error handling

Phase 2: Core Modules (Weeks 3-6)

  • CRM API endpoints
  • Sales API endpoints
  • Basic CRUD operations
  • Relationship loading
  • Validation rules

Phase 3: Advanced Features (Weeks 7-10)

  • Operations API endpoints
  • Finance API endpoints
  • HRM API endpoints
  • Complex business operations
  • Bulk operations

Phase 4: Analytics & Optimization (Weeks 11-12)

  • Analytics API endpoints
  • Performance optimization
  • Caching implementation
  • Load testing

Phase 5: Documentation & Polish (Weeks 13-14)

  • API documentation
  • Code examples
  • Security audit
  • Final testing

This comprehensive API implementation plan provides a solid foundation for building both SPA and third-party compatible REST APIs while maintaining clean architecture principles and Laravel best practices.

Documentation for SynthesQ CRM/ERP Platform