Skip to content

Universal routes

Universal routes refer to routes that are usable by both the central app and the tenant app.

In other words: tenancy is initialized when a tenant is specified, and it’s not initialized when a tenant is not specified.

Since Tenancy v4, making a route universal is as easy as adding the universal middleware on top of identification middleware:

Route::middleware([
InitializeTenancyByDomain::class,
'universal',
])->group(function () {
Route::get('/posts', [PostController::class, 'index']);
});

If your central domain were your-saas.com and a tenant’s domain were your-saas.theirdomain.com:

  • Tenancy would not be initialized on your-saas.com/posts
  • Tenancy would be initialized on your-saas.theirdomain.com/posts

Above we said that tenancy is initialized if the tenant is specified. What exactly does this mean?

It refers to providing the tenant in some format that the used identification middleware expects.

With the domain example above it’s obvious, requests made on a domain that’s included in the central_domains config will be central requests, and requests made on other domains will be tenant requests.

But how does this work with other identification middleware?

With request data identification, tenancy will be initialized if the tenant is provided in any of the supported formats:

InitializeTenancyByRequestData.php
protected function getPayload(Request $request): string|null
{
if (static::$header && $request->hasHeader(static::$header)) {
$payload = $request->header(static::$header);
} elseif (static::$queryParameter && $request->has(static::$queryParameter)) {
$payload = $request->get(static::$queryParameter);
} elseif (static::$cookie && $request->hasCookie(static::$cookie)) {
$payload = $request->cookie(static::$cookie);
} else {
$payload = null;
}
if (is_string($payload) || is_null($payload)) {
return $payload;
}
throw new TenantCouldNotBeIdentifiedByRequestDataException($payload);
}

With path identification, it’s a bit more complex.

With path identification, you need to include the {tenant} parameter in your routes.

However, the parameter is part of the route path, and the path is how Laravel distinguishes between different routes.

Therefore, we cannot have a route that works as both /posts and /{tenant}/posts.

Instead, we will need two different routes.

So since we’ll be using two different routes, we’ll need to register each route with path tenant identification that we want to use universally in two ways:

  • /posts (central route, no identification middleware)
  • /{tenant}/posts (tenant route, with identification middleware)

To make this easier for you, we have a dedicated feature for this: cloning routes as tenant routes.

If you use kernel identification, you can set the default route mode to be universal. In such a case, all your routes would be universal by default, unless explicitly marked central or tenant to be exclusive to one context.