Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here

Versioning your API: from V1 to V2 and Beyond

Premium
4:28

If you are working on a project long enough, you will need versioning at some point. If you have used any APIs like Stripe or Facebook, they have version numbers. Knowing which version you use is essential because it may be a part of the URL endpoint or some parameter. So, how do you implement versioning in your project?


Until now, we had endpoint /api/categories without any version. Let's create version two, a copy of version one.

To do that, first, let's move the existing API to version one. Create an app\Http\Controllers\Api\V1 directory and move CategoryController and ProductController inside it. Change the namespace in both Controllers.

namespace App\Http\Controllers\Api;
namespace App\Http\Controllers\Api\V1;
 
// ...

Next, change the Controller path in the routes/api.php.

routes/api.php:

Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
 
Route::apiResource('categories', \App\Http\Controllers\Api\CategoryController::class)
Route::apiResource('categories', \App\Http\Controllers\Api\V1\CategoryController::class)
->middleware('auth:sanctum');
 
Route::get('products', [\App\Http\Controllers\Api\ProductController::class, 'index']);
Route::get('products', [\App\Http\Controllers\Api\V1\ProductController::class, 'index']);

Last, we must change the prefix from /api to /api/v1. Routes are configured in the...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (31 h 16 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

A
angel ✓ Link copied!

Should I also versionning all files related to the api like FormRequest, Ressource, migration, Policies, Observers and so on...?

M
Modestas ✓ Link copied!

It depends.

FormRequest, resources -> Yes. I would version them myself.

Policies -> If there are big changes between them - yeah. You should be already versioning contrllers and other things around, so why not.

Migrations -> No, as that would mean a separate database. Just make sure nothing crashes for older users

Observers -> Maybe. This one is tricky to answer exactly.

But all in all - you have to keep in mind that you have to support older versions of the code. So whatever you do, you have to check if it makes sense. For example, removing a function from anywhere - can be pretty bad. So a version with all the things - is needed. But for an additional field - it might not be so important.

M
mewtonium ✓ Link copied!

There's a few different ways to organise your routes for versioning. I like to pass an array to the api argument of withRouting() and leave the base API prefix as /api:

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: [
            __DIR__.'/../routes/api/base.php',
            __DIR__.'/../routes/api/v1.php',
        ],
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        //
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

Then inside your version-specific route file define the prefix:

routes/api/v1.php

<?php

use App\Http\Controllers\Api\V1\ProductController;
use Illuminate\Support\Facades\Route;

Route::prefix('/v1')
    ->middleware('auth:sanctum')
    ->group(function () {
        Route::apiResource('/products', ProductController::class);
    });
M
M ✓ Link copied!

Is splitting it off the preferred method? versus having it contained in a single file: routes/api.php

I realize this could potentially get bloated/big.

use App\Http\Controllers\Api\V1\ProductController AS v1ProductController;
use App\Http\Controllers\Api\V1\CategoryController AS v1CategoryController;
use App\Http\Controllers\Api\V2\ProductController AS v2ProductController;
use App\Http\Controllers\Api\V2\CategoryController AS v2CategoryController;

Route::prefix('v1')->group(function () {
    Route::apiResource('categories', v1CategoryController::class);
    Route::get('/products', [v1ProductController::class, 'index']);
});

Route::prefix('v2')->group(function () {
    Route::apiResource('categories', v2CategoryController::class);
    Route::get('/products', [v2ProductController::class, 'index']);
});
N
Nerijus ✓ Link copied!

in one file this would be bad. you can require those route files in the routes api instead