API Versioning for Web Application

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

API Versioning for Web Application

API versioning is a way to introduce breaking changes without breaking existing clients. Without versioning, any schema change, field removal, or endpoint renaming will break mobile apps, partner integrations, and scripts you don't control.

Versioning Strategies

URL versioning — the most explicit approach:

GET /api/v1/articles
GET /api/v2/articles

Pros: obvious from the URL, easy to cache on CDN, simple for developers. Cons: URL becomes "cluttered" with version, resource /articles is duplicated.

Header versioning:

GET /api/articles
Accept: application/vnd.myapp.v2+json

Cleaner from a REST perspective, but harder to test (curl requires explicit header), caches poorly without Vary: Accept.

Query parameter:

GET /api/articles?version=2

Only for edge cases — mixes version with business request parameters.

Recommendation: URL versioning for most projects. Header versioning if API is already in production and URLs cannot be changed.

Implementation in Laravel

// routes/api.php
Route::prefix('v1')->group(base_path('routes/api_v1.php'));
Route::prefix('v2')->group(base_path('routes/api_v2.php'));

// routes/api_v2.php
Route::apiResource('articles', App\Http\Controllers\V2\ArticleController::class);

V2 controllers inherit from V1, overriding only changed methods:

namespace App\Http\Controllers\V2;

use App\Http\Controllers\V1\ArticleController as V1Controller;

class ArticleController extends V1Controller
{
    public function index(Request $request)
    {
        // V2: added excerpt field, removed body from list
        return ArticleV2Resource::collection(
            Article::paginate($request->per_page ?? 20)
        );
    }
}

Implementation in NestJS

// main.ts
app.setGlobalPrefix('api');

// modules/v1/v1.module.ts and v2/v2.module.ts
@Controller('v1/articles')
export class ArticleV1Controller { ... }

@Controller('v2/articles')
export class ArticleV2Controller { ... }

Or via NestJS versioning API:

app.enableVersioning({ type: VersioningType.URI });

@Controller({ path: 'articles', version: '2' })
export class ArticleV2Controller {
  @Get()
  findAll() { ... }
}

Version Lifecycle

Typical process:

  1. New version is announced in Changelog with list of breaking changes
  2. Old version gets deprecated status — Sunset header in responses
  3. After 6–12 months, old version is turned off
// Middleware adds Deprecation header to V1 responses
class AddDeprecationHeader
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        if (str_starts_with($request->path(), 'api/v1/')) {
            $response->headers->set('Deprecation', 'true');
            $response->headers->set('Sunset', 'Sat, 01 Jan 2026 00:00:00 GMT');
            $response->headers->set('Link', '<https://api.example.com/v2/>; rel="successor-version"');
        }
        return $response;
    }
}

What Constitutes a Breaking Change

Not all changes require a new version. Safe changes (backward-compatible):

  • Adding a new field to response
  • Adding a new optional request parameter
  • Adding a new endpoint
  • Adding a new enum value (if client ignores unknown values)

Breaking changes requiring a new version:

  • Removing a field from response
  • Renaming a field
  • Changing field type (stringinteger)
  • Changing format (2024-01-151705276800)
  • Removing an endpoint
  • Changing method semantics (e.g., PATCH behaves like PUT)

API Changelog

Versioning without documenting changes is useless. CHANGELOG.md format for API:

## v2.0.0 (2025-03-01)

### Breaking Changes
- `GET /articles` — removed `body` field, added `excerpt` (first 200 characters)
- `POST /articles` — `tags` field now array of IDs, not strings

### New Features
- `GET /articles/{id}/related` — related articles
- Cursor-based pagination: `after` parameter instead of `page`

## v1.x — Deprecated
Supported until 2026-01-01. Use v2.

Versioning OpenAPI Specifications

Separate file per version:

docs/
  openapi.v1.yaml
  openapi.v2.yaml

Or via $ref between files — reuse common schemas (ErrorResponse, Pagination) without duplication.

Timeline

Setting up URL versioning with route separation, controller inheritance, Deprecation headers: 2–3 days. With automatic changelog, Sunset monitoring (alert on deadline exceeded), and separate OpenAPI files: 1 week.