API versioning in Moonlight

Who this is for

Northwind Studio is shipping DeskFlow v2 with breaking payload changes. You need two API versions running in parallel/api/v1/ for existing clients and /api/v2/ for the new contract.

What you will learn

  • Creating a V2Switch class and registering it in settings.ini
  • Which DeskFlow changes warrant a new version vs a new action on v1
  • How each version gets its own ping and service catalog

Before you start

Before you start

How it works

Each switch is bound to one API version path (/api/v1/, /api/v2/, …). A switch lists which services are exposed on that version.

flowchart LR INI["[app_switches]"] --> V1["/api/v1/ MainSwitch"] INI --> V2["/api/v2/ V2Switch"] V1 --> TaskV1[task / member / project] V2 --> TaskV2[task v2 contract]

Define a switch

namespace Application\Switches;

use Application\Services\MemberService;
use Application\Services\V2\TaskService as TaskServiceV2;
use Pionia\Collections\Arrayable;
use Pionia\Http\Switches\ApiSwitch;

class V2Switch extends ApiSwitch
{
    public static function registerServices(): Arrayable
    {
        return arr([
            'task' => TaskServiceV2::class,
            'member' => MemberService::class,
        ]);
    }
}

Scaffold with:

php pionia make:switch V2Switch

Register versions

Declare switches in environment/settings.ini:

[app_switches]
v1=Application\Switches\MainSwitch
v2=Application\Switches\V2Switch

The framework registers routes at boot — no bootstrap/routes.php required. See HTTP routing for provider-based registration and advanced cases.

Clients POST to the version prefix:

POST http://127.0.0.1:8000/api/v2/
{ "service": "task", "action": "list" }

Each version has its own ping endpoint:

curl -s http://127.0.0.1:8000/api/v1/ping
curl -s http://127.0.0.1:8000/api/v2/ping

When to add a version

  • Breaking action contracts or payload shapes (e.g. renaming assignee_id to member_id)
  • Removing services from the public surface
  • Running old and new implementations in parallel during migration

Non-breaking additions (new actions on existing services) usually stay on the current version — Alex’s mobile app keeps calling /api/v1/ until Northwind sunsets it.

Common mistakes

  • Creating v2 for every new actiontask.archive can live on v1 if existing clients ignore unknown actions.
  • Forgetting to register the switch in settings.ini — the class alone does not create a route.
  • Different service aliases per version without documentation — if v2 removes project, update /docs and notify frontend teams.
  • Hardcoding /api/v1/ in the SPA — use apiVersionPath() or env config so DeskFlow can migrate gradually.

What’s next

Moonlight overview

One URL per version explained.

Documenting your API

Tag actions per version.

HTTP routing

Provider routes and route cache.