how to register {tenant} routes for API (api.php routes file)

I cannot figure out how i can manage the api.php routes to register the {tenant} parameter so that i can use this for my API routes. filament/filament/routes/web.php uses something like this:
Route::name('filament.')
->group(function () {
foreach (Filament::getPanels() as $panel) {
/** @var \Filament\Panel $panel */
$panelId = $panel->getId();
$hasTenancy = $panel->hasTenancy();
$tenantRoutePrefix = $panel->getTenantRoutePrefix();
$tenantSlugAttribute = $panel->getTenantSlugAttribute();
$domains = $panel->getDomains();
Route::name('filament.')
->group(function () {
foreach (Filament::getPanels() as $panel) {
/** @var \Filament\Panel $panel */
$panelId = $panel->getId();
$hasTenancy = $panel->hasTenancy();
$tenantRoutePrefix = $panel->getTenantRoutePrefix();
$tenantSlugAttribute = $panel->getTenantSlugAttribute();
$domains = $panel->getDomains();
but when i add these to my routes/api.php only $panelId = 1 and $hasTenancy = true. the rest of the variables are NULL... Also in the Http/Middleware/IdentifyTenant.php this get mentioned like this:
if (! $request->route()->hasParameter('tenant')) {}
if (! $request->route()->hasParameter('tenant')) {}
and
$tenant = $panel->getTenant($request->route()->parameter('tenant'));
$tenant = $panel->getTenant($request->route()->parameter('tenant'));
how can i make sure that also for my API enpoints i can use the tenant slug in the URI. php artisan route:list only shows it for the web.php endpoints like so:
GET|HEAD admin/{tenant}/jobs .......................................................... filament.admin.resources.jobs.index › App\Filament\Resources\JobResource\Pages\ListJobs
GET|HEAD admin/{tenant}/jobs .......................................................... filament.admin.resources.jobs.index › App\Filament\Resources\JobResource\Pages\ListJobs
but for my API endpoints: no {tenant}
POST api/admin/jobs ........................................................ api.admin.jobs.create › App\Filament\Resources\JobResource\Api\Handlers\CreateHandler@handler
POST api/admin/jobs ........................................................ api.admin.jobs.create › App\Filament\Resources\JobResource\Api\Handlers\CreateHandler@handler
11 Replies
Lara Zeus
Lara Zeus5mo ago
why you need filament in your api routes? dont use Filament::panels on an early point like api.php. also filament wont be initialized for apis I think! the tenant is just a record in your DB in your api you will receive a parameter for the {tenant} and then check if this slug exist for a tenant sorry just confused why you need filament in your api routes :)!?
shenntek
shenntek5mo ago
Filament
API Service by Rupadana - Filament
A simple API service for supporting Filament resources.
shenntek
shenntek5mo ago
And extend this to allow tenant Id to scope api resource results for specific tenant I can use global scopes in my models but if I use a belongtomany relationship for my tenants and a user belongs to multiple tenants the api resource returns all tenant records
Lara Zeus
Lara Zeus5mo ago
I didnt use the package with tenant, better to ask in #rupadana-api-service I am sure there is a better way to support tenant or adding a config for a middleware in the package you maybe can apply global api middleware in your kernal! sorry not much help 🙂
shenntek
shenntek5mo ago
I know I give this as an example this package. But I just can’t figure out at what point this {tenant} gets registered in filament package. It gets added to the routes when you add $panel->tenant(Team::class) for example. But is it in a filament serviceprovider /middleware / kernel. But not sure at what point in the dispatcher. So I can build it myself in api.php I want to code it as close as possible like the filament way So actually how does it work under the hood.
Lara Zeus
Lara Zeus5mo ago
ya the package idea is to scan all your panels and get the resources and register them on the fly if you're not using the package you dont need all this!
shenntek
shenntek5mo ago
Okay then I can just register the tenant parameter in routes file like the laravel way. And then use it in a middleware to apply global modal scope. I will just use the “laravel way” of adding {tenant} parameter in my api URL’s.
Lara Zeus
Lara Zeus5mo ago
yes that the best in my opinion for apis
shenntek
shenntek5mo ago
Still strange that in web.php $tenantRoutePrefix = $panel->getTenantRoutePrefix(); $tenantSlugAttribute = $panel->getTenantSlugAttribute(); are not NULL, and in api.php both are NULL. is the dispatch of the API in laravel different? as i'm using sanctum. and web.php goes via the web guard....? so something in web.php gets initialized by Filament earlier and this does not get dispatched in api.php routes...
Lara Zeus
Lara Zeus5mo ago
I think filament middleware for web guard not api? nit sure tho
shenntek
shenntek5mo ago
i'm sorry, that's incorrect what i was saying, in both files these variables are null. I will look further how Filament creates these {tenant} parameter into the url's once $panel->tenant() is added. otherwise i will use something like this:
Route::group(['prefix' => '{tenant}'], function () {
Route::get('jobs', ['as' => 'jobs', 'uses' => '....']);
});
Route::group(['prefix' => '{tenant}'], function () {
Route::get('jobs', ['as' => 'jobs', 'uses' => '....']);
});
etc.. i think this is the prefix what i need to use as mentioned in vendor/filament/filament/routes/web.php:127:
->prefix((($tenantRoutePrefix) ? "{$tenantRoutePrefix}/" : '') . '{tenant' . (($tenantSlugAttribute) ? ":{$tenantSlugAttribute}" : '') . '}')
->prefix((($tenantRoutePrefix) ? "{$tenantRoutePrefix}/" : '') . '{tenant' . (($tenantSlugAttribute) ? ":{$tenantSlugAttribute}" : '') . '}')
and because $tenantSlugAttribute and $tenantSlugAttribute are NULL this would result in '{tenant}' okay i got it working now still little bit dirty but i can tidy up everything: So this is what i did: for my model(s) i used this method:
protected static function booted(): void
{
static::addGlobalScope('team', function (Builder $query) {
if (auth()->check()) {
$query->where('team_id', request()->tenant);
// or with a `team` relationship defined:
// $query->whereBelongsTo(request()->user()->teams);
}
});
}
protected static function booted(): void
{
static::addGlobalScope('team', function (Builder $query) {
if (auth()->check()) {
$query->where('team_id', request()->tenant);
// or with a `team` relationship defined:
// $query->whereBelongsTo(request()->user()->teams);
}
});
}
and in my routes/api.php as per example like the Api Service package:
Route::prefix('api')
->name('api.')
->group(function () {
$panels = Filament::getPanels();
foreach ($panels as $key => $panel) {
try {
$panelId = $panel->getId();
$hasTenancy = $panel->hasTenancy();
$tenantRoutePrefix = $panel->getTenantRoutePrefix();
$tenantSlugAttribute = $panel->getTenantSlugAttribute();
$domains = $panel->getDomains();

Route::prefix($panel->getId() . '/' . (($tenantRoutePrefix) ? "{$tenantRoutePrefix}/" : '') . '{tenant' . (($tenantSlugAttribute) ? ":{$tenantSlugAttribute}" : '') . '}')
// prefix($panel->getId())
->name($panel->getId() . '.')
->group(function () use ($panel) {
$apiServicePlugin = $panel->getPlugin('api-service');
$apiServicePlugin->route($panel);
});
} catch (Exception $e) {
}
}
});
Route::prefix('api')
->name('api.')
->group(function () {
$panels = Filament::getPanels();
foreach ($panels as $key => $panel) {
try {
$panelId = $panel->getId();
$hasTenancy = $panel->hasTenancy();
$tenantRoutePrefix = $panel->getTenantRoutePrefix();
$tenantSlugAttribute = $panel->getTenantSlugAttribute();
$domains = $panel->getDomains();

Route::prefix($panel->getId() . '/' . (($tenantRoutePrefix) ? "{$tenantRoutePrefix}/" : '') . '{tenant' . (($tenantSlugAttribute) ? ":{$tenantSlugAttribute}" : '') . '}')
// prefix($panel->getId())
->name($panel->getId() . '.')
->group(function () use ($panel) {
$apiServicePlugin = $panel->getPlugin('api-service');
$apiServicePlugin->route($panel);
});
} catch (Exception $e) {
}
}
});
still have to check if the request has this param etc. and if tenancy is enabled and dynamically use the correct query tenant_key etc. etc.