Skip to main content

API Versioning Strategy

API versioning is crucial for maintaining backward compatibility while allowing your API to evolve. This guide covers versioning strategies and best practices for ServITech Backend API.

Current Implementation

ServITech Backend currently implements a single version API without explicit version prefixes. All endpoints are accessible at:
${APP_URL}/api/{endpoint}
Example:
http://localhost:8000/api/auth/login
http://localhost:8000/api/articles
http://localhost:8000/api/support-request
The current implementation assumes all clients are using the latest version. This works well for early-stage projects but should evolve as the API matures.

Why Version Your API?

API versioning allows you to:
  • Maintain backward compatibility while introducing breaking changes
  • Support multiple client versions (mobile apps that users haven’t updated)
  • Deprecate features gradually without breaking existing integrations
  • Test new features in parallel with stable versions
Breaking changes without versioning can break mobile apps already installed on user devices.

Versioning Strategies

Include the version number in the URL path:
/api/v1/articles
/api/v2/articles
Advantages:
  • Clear and explicit
  • Easy to test with tools like Postman
  • Cache-friendly
  • Easy to route in Laravel
Implementation in Laravel:
routes/api.php
Route::prefix('v1')->group(function () {
    Route::localizedGroup(function () {
        Route::prefix('auth')->group(function () {
            Route::post('login', [AuthController::class, 'login']);
            Route::post('register', [AuthController::class, 'register']);
        });

        Route::prefix('articles')->group(function () {
            Route::get('', [ArticleController::class, 'index']);
            Route::post('', [ArticleController::class, 'store']);
        });
    });
});

Route::prefix('v2')->group(function () {
    Route::localizedGroup(function () {
        // V2 endpoints with breaking changes
    });
});

2. Header Versioning

Specify version in custom header:
GET /api/articles
Accept-Version: v1
Advantages:
  • Clean URLs
  • More “RESTful”
Disadvantages:
  • Less visible
  • Harder to test
  • Requires custom middleware

3. Accept Header Versioning

Use the Accept header with custom media types:
GET /api/articles
Accept: application/vnd.servitech.v1+json
Advantages:
  • Follows REST principles
  • Fine-grained content negotiation
Disadvantages:
  • More complex to implement
  • Less intuitive for developers

4. Query Parameter Versioning

Include version as a query parameter:
/api/articles?version=1
Advantages:
  • Simple to implement
Disadvantages:
  • Mixes versioning with query parameters
  • Can interfere with caching
  • Not recommended for production APIs
Recommendation: URL Path Versioning is the most common and developer-friendly approach for REST APIs.

Migration Plan for ServITech

1

Introduce v1 Prefix

Move all current endpoints under /api/v1:
routes/api.php
Route::prefix('v1')->group(function () {
    Route::localizedGroup(function () {
        // All existing routes here
    });
});
2

Maintain Backward Compatibility

Keep non-versioned endpoints working temporarily:
// V1 routes
Route::prefix('v1')->group(function () {
    Route::localizedGroup(function () {
        // Include routes
    });
});

// Deprecated: Redirect to v1
Route::localizedGroup(function () {
    // Same routes without prefix
    // Add deprecation warnings in responses
});
3

Update API Documentation

Update Scramble documentation to reflect versioned endpoints:
${APP_URL}/docs/api/v1
4

Update Mobile Client

Update Android app to use /api/v1 endpoints.
5

Deprecation Notice

Add deprecation warnings to non-versioned endpoints:
return response()->json([
    'data' => $data,
    'meta' => [
        'deprecated' => true,
        'message' => 'This endpoint is deprecated. Use /api/v1/* instead.',
        'sunset_date' => '2026-06-01'
    ]
]);
6

Remove Legacy Endpoints

After a grace period, remove non-versioned endpoints.

Version Management Best Practices

Semantic Versioning

Follow semantic versioning principles:
  • v1.x.x → Major version (breaking changes)
  • v1.2.x → Minor version (new features, backward compatible)
  • v1.2.3 → Patch version (bug fixes)
For API versioning, typically only major versions are exposed:
/api/v1/*  (version 1.x.x)
/api/v2/*  (version 2.x.x)

When to Create a New Version

Create a new major version when making breaking changes:
  • Removing or renaming endpoints
  • Changing request/response structure
  • Removing required fields
  • Changing authentication mechanism
  • Modifying error response format
Breaking change example:
// v1 response
{
  "user": {
    "name": "Carlos Orellana",
    "email": "[email protected]"
  }
}

// v2 response (breaking)
{
  "data": {
    "first_name": "Carlos",
    "last_name": "Orellana",
    "email": "[email protected]"
  }
}

Non-Breaking Changes

These can be added without a new version:
  • Adding new endpoints
  • Adding optional parameters
  • Adding new fields to responses
  • Adding new error codes
  • Improving performance
Example of backward-compatible change:
// Before
{
  "user": {
    "id": 1,
    "name": "Carlos Orellana",
    "email": "[email protected]"
  }
}

// After (still v1)
{
  "user": {
    "id": 1,
    "name": "Carlos Orellana",
    "email": "[email protected]",
    "avatar_url": "https://example.com/avatar.jpg"  // New field
  }
}

Version-Specific Controllers

For significantly different logic between versions:
app/Http/Controllers/
├── V1/
   ├── ArticleController.php
   ├── AuthController.php
   └── UserController.php
└── V2/
    ├── ArticleController.php
    └── AuthController.php
routes/api.php
use App\Http\Controllers\V1\ArticleController as V1ArticleController;
use App\Http\Controllers\V2\ArticleController as V2ArticleController;

Route::prefix('v1')->group(function () {
    Route::get('articles', [V1ArticleController::class, 'index']);
});

Route::prefix('v2')->group(function () {
    Route::get('articles', [V2ArticleController::class, 'index']);
});

Deprecation Strategy

1

Announce Deprecation

Communicate deprecation plans well in advance:
  • Add deprecation warnings to API responses
  • Update documentation
  • Notify integration partners
  • Send in-app notifications
2

Set Sunset Date

Give clients adequate time to migrate (3-6 months minimum):
return response()->json($data)
    ->header('Deprecation', 'true')
    ->header('Sunset', 'Sat, 1 Jun 2026 00:00:00 GMT');
3

Monitor Usage

Track which clients are still using deprecated endpoints:
Log::info('Deprecated endpoint used', [
    'endpoint' => $request->path(),
    'user_id' => auth()->id(),
    'app_version' => $request->header('App-Version')
]);
4

Graceful Shutdown

After the sunset date, return 410 Gone status:
return response()->json([
    'error' => 'This endpoint has been removed. Use /api/v2 instead.'
], 410);

Current Endpoints Overview

ServITech currently has the following endpoint groups:

Public Endpoints

  • POST /api/auth/login — User login
  • POST /api/auth/register — User registration
  • POST /api/auth/reset-password — Password reset request
  • PUT /api/auth/reset-password — Password reset confirmation
  • GET /api/articles — List articles
  • GET /api/articles/{category} — Get article by category
  • GET /api/subcategories — List subcategories

Protected Endpoints (JWT Required)

  • POST /api/auth/logout — User logout
  • GET /api/user/profile — Get user profile
  • PUT /api/user/profile — Update user profile
  • PUT /api/user/password — Change password
  • GET/POST/PUT/DELETE /api/support-request/* — Support requests

Admin-Only Endpoints (Role Required)

  • GET/POST/PUT/DELETE /api/category/* — Category management
  • POST/PUT/DELETE /api/articles/* — Article management
  • POST/PUT/DELETE /api/subcategories/* — Subcategory management
  • GET/POST/PUT/DELETE /api/repair-request/* — Repair request management
All endpoints support localization through the Accept-Language header (es/en).

Next Steps

Build docs developers (and LLMs) love