laravel-json-api / laravel

JSON:API for Laravel applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Mark routes in router as JSON API routes

MeiKatz opened this issue · comments

Currently there is no bulletproof way of filtering routes created by JSON API. But there might be a solution by adding JSON API routes as instances of a sub-class of Illuminate\Routing\Route. With this it would be easy to filter out routes from JSON API just by checking if a route is an instance of a JSON API route class or not. Or if such a route implements a contract from JSON API.

It is quite simple:

class JsonApiRoute extends Illuminate\Routing\Route {}

// get router
$router = Illuminate\Support\Facades\Route::getRoutes();
// add json api route
$router->add( new JsonApiRoute( $methods, $uri, $action ) );

// ... that's it!

Later we could filter out the json api routes like this:

$routes = Illuminate\Support\Facades\Route::getRoutes()->getRoutes();

$json_api_routes = array_filter( $routes, function ( $route ) {
  return ( $route instanceof JsonApiRoute );
});
commented

I am not fully expert in the Laravel routing at the moment, but does this also work when routes are cached with php artisan route:cache? If that is the case, I think I like this approach.

@ben221199 Good question. I haven't checked this yet but might do it later today. I will keep you updated!

Okay, did a quick check. Currently it seems like routes from Laravel JSON api are not cached at all. Therefore it seems to be a bug, because caching my routes crashes my app ...

This is my RouteServiceProvider (extract):

class RouteServiceProvider extends ServiceProvider {
  public function boot() {
    Route::prefix('api')
      ->middleware('api')
      ->group(function () {
        $this->getJsonApiServer()->resources(function ( ResourceRegistrar $server ) {
          $callback = require_once( base_path('routes/api/v1.php') );

          if ( $callback instanceof \Closure ) {
            $callback( $server );
          }
        });
      });
  }

  private function getJsonApiServer(): PendingServerRegistration {
    return (
      ResourceRegistrar::server('v1')
        ->prefix('v1')
        ->namespace('\\')
    );
  }
}

And my routes file (routes/api/v1.php) looks like this:

return function ( ResourceRegistrar $server ) {
  $server
    ->resource(
      'access_tokens',
      AccessTokenController::class
    )
    ->only(
      'store',
      'show',
      'destroy',
    );

  // ...
};

Okay, if we keep the ability to cache routes in mind, then my proposed approach will not work. But maybe it is possible to add a "tagged" "dummy middleware" and we can check the routes for the existence of this middleware. Via the "tag" we could also filter the version / server that defined a route. For example "jsonapi:v1" would tell that it is a route defined by JSON API and the "tag" would say, that it is from the server "v1".

Currently it seems like routes from Laravel JSON api are not cached at all

You mean, when using this extended class? They definitely do work for the current implementation, because I use cached routes in my production apps without any problems.

I'd be intrigued to look into the Laravel route caching to see why an extended class doesn't work. Implies they're not serializing the objects directly but doing something else.

@lindyhopchris no, when I run php artisan route:cache there are no JSON API routes in the cached file. Not sure why, because when I run $routes->compiled() all routes are included. Seems to be a problem on its own.

Anyway, I think there is no way to get the route class from the route cache file. But middlewares are included in the cache file and this might be a solution for this package.