API: Tenancy
Resolve a tenant once per request with API::tenant() or ApiInterface::tenant(), then scope criteria with endpoint callbacks.
The API can resolve one tenant per request and hand it to your code. It does not decide what a tenant is or filter data for you: you supply a resolver, then use the tenant to scope queries.
For site-level multi-tenancy (different config, data paths, or streams per domain), see Core Applications and the Tenancy guide.
Resolve a tenant
Register a global resolver in a service provider:
use Streams\Api\Support\Facades\API;
API::tenant(fn () => request()->user()?->organization_id);
Or set one per interface. An interface resolver takes precedence over the global one for routes on that interface:
use Streams\Api\ApiInterface;
API::interface(
ApiInterface::make('partners')
->path('api/partners')
->middleware(['auth:sanctum'])
->tenant(fn () => request()->header('X-Partner'))
);
Read it anywhere during the request:
$tenant = API::getTenant();
API::getTenant() calls the resolver once and binds the result in the container as streams.api.tenant, so later calls in the same request return the cached value. If no resolver is registered it returns null.
Scope queries
ListEntries and ShowEntry fire an apply callback with the criteria before they run the query (ListEntries also fires applied after filters). Add a listener to scope every request:
use Streams\Api\Endpoints\Entries\ListEntries;
use Streams\Api\Endpoints\Entries\ShowEntry;
use Streams\Api\Support\Facades\API;
use Streams\Core\Criteria\Criteria;
$scope = function (Criteria $criteria) {
if ($tenant = API::getTenant()) {
$criteria->where('organization_id', $tenant);
}
};
ListEntries::addCallbackListener('apply', $scope);
ShowEntry::addCallbackListener('apply', $scope);
The callback receives the criteria as $criteria, because listeners are called through the container with named parameters.
What is not scoped
Only the list and show endpoints fire apply. Create, update, patch, delete, and query, plus all stream endpoints, do not. To enforce tenancy on writes:
- Put tenant checks in your gate middleware or interface middleware, or
- Replace the endpoints with your own subclasses (see Custom endpoints), or
- Keep tenants in separate data sources with Core Applications.