graphqlite-laravel
graphqlite-laravel copied to clipboard
Can we get Lumen Support for this package?
Hi,
Thank you for the awesome work!
I was trying this package with Lumen and found that the package won't work with the framework.
I found the following issues mainly:
config_pathis not available in Lumen. Hence the the\TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProvider::boot()method will fail.- There is no
webmiddleware available in the Lumen. Since the routes definition in the package depends on thewebmiddleware, it will gracefully fail at the moment. - Dependency on
Illuminate\Contracts\Foundation\Applicationwill again become a roadblock in the\TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProvider::register()method. This is because Lumen uses different Application instance (Laravel\Lumen\Application).
I would appreciate any kind of help on this. Meanwhile, I will provide the nasty workaround for all these problems I faced below.
PS: I am using Lumen version ^8.0.
Hi a work around is given below:
- Update
bootstrap/app.phpwith below content.
$app->configure('graphqlite');
Then create config/graphqlite.php and add below content as per the doc suggests
use GraphQL\Error\Debug;
return [
'controllers' => 'App\\Http\\Controller',
'types' => 'App\\',
'debug' => Debug::RETHROW_UNSAFE_EXCEPTIONS,
'uri' => env('GRAPHQLITE_URI', '/graphql'),
'middleware' => ['web'],
'guard' => ['web'],
];
- Update
bootstrap/app.phpwithwebmiddleware definition. Here you should ideally add csrf token verification if I understand the doc correctly. For the time being, I am just providing it with theExampleMiddlewarethat comes along with Lumen framework.
$app->routeMiddleware([
'web' => App\Http\Middleware\ExampleMiddleware::class,
]);
- Finally you need to create a custom service Provider that wraps the
\TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProviderand modify theboot()andregister()method in order to avoid the issues I listed out in the above question.
So
File: bootstrap/app.php
$app->register(App\Providers\GraphQLiteProvider::class);
File: app/Providers/GraphQLiteProvider.php
<?php
namespace App\Providers;
use GraphQL\Error\Debug;
use GraphQL\Server\ServerConfig;
use GraphQL\Server\StandardServer;
use GraphQL\Type\Schema as WebonyxSchema;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Contracts\Events\Dispatcher;
use Laminas\Diactoros\ResponseFactory;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\Diactoros\StreamFactory;
use Laminas\Diactoros\UploadedFileFactory;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
use Symfony\Component\Cache\Psr16Cache;
use TheCodingMachine\GraphQLite\Context\Context;
use TheCodingMachine\GraphQLite\Exceptions\WebonyxErrorHandler;
use TheCodingMachine\GraphQLite\Laravel\Controllers\GraphQLiteController;
use TheCodingMachine\GraphQLite\Laravel\Listeners\CachePurger;
use TheCodingMachine\GraphQLite\Laravel\Mappers\PaginatorTypeMapperFactory;
use TheCodingMachine\GraphQLite\Laravel\Mappers\Parameters\ValidateFieldMiddleware;
use TheCodingMachine\GraphQLite\Laravel\SanePsr11ContainerAdapter;
use TheCodingMachine\GraphQLite\Laravel\Security\AuthenticationService;
use TheCodingMachine\GraphQLite\Laravel\Security\AuthorizationService;
use TheCodingMachine\GraphQLite\Schema;
use TheCodingMachine\GraphQLite\SchemaFactory;
use TheCodingMachine\GraphQLite\Security\AuthenticationServiceInterface;
class GraphQLiteProvider
extends \TheCodingMachine\GraphQLite\Laravel\Providers\GraphQLiteServiceProvider
{
public function boot(Dispatcher $events)
{
$this->loadRoutesFrom($this->getParentClassDirectory() . '/../routes/routes.php');
$events->listen('cache:clearing', CachePurger::class);
}
public function register()
{
$this->app->bind(WebonyxSchema::class, Schema::class);
if (!$this->app->has(ServerRequestFactoryInterface::class)) {
$this->app->bind(ServerRequestFactoryInterface::class, ServerRequestFactory::class);
}
if (!$this->app->has(StreamFactoryInterface::class)) {
$this->app->bind(StreamFactoryInterface::class, StreamFactory::class);
}
if (!$this->app->has(UploadedFileFactoryInterface::class)) {
$this->app->bind(UploadedFileFactoryInterface::class, UploadedFileFactory::class);
}
if (!$this->app->has(ResponseFactoryInterface::class)) {
$this->app->bind(ResponseFactoryInterface::class, ResponseFactory::class);
}
$this->app->bind(HttpMessageFactoryInterface::class, PsrHttpFactory::class);
$this->app->singleton(GraphQLiteController::class, function ($app) {
$debug = config('graphqlite.debug', Debug::RETHROW_UNSAFE_EXCEPTIONS);
return new GraphQLiteController($app[StandardServer::class], $app[HttpMessageFactoryInterface::class], $debug);
});
$this->app->singleton(StandardServer::class, static function ($app) {
return new StandardServer($app[ServerConfig::class]);
});
$this->app->singleton(ServerConfig::class, static function ($app) {
$serverConfig = new ServerConfig();
$serverConfig->setSchema($app[Schema::class]);
$serverConfig->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
$serverConfig->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
$serverConfig->setContext(new Context());
return $serverConfig;
});
$this->app->singleton('graphqliteCache', static function () {
if (extension_loaded('apcu') && ini_get('apc.enabled')) {
return new Psr16Cache(new ApcuAdapter());
} else {
return new Psr16Cache(new PhpFilesAdapter());
}
});
$this->app->singleton(CachePurger::class, static function ($app) {
return new CachePurger($app['graphqliteCache']);
});
$this->app->singleton(AuthenticationService::class, function($app) {
$guard = config('graphqlite.guard', $this->app['config']['auth.defaults.guard']);
if (!is_array($guard)) {
$guard = [$guard];
}
return new AuthenticationService($app[AuthFactory::class], $guard);
});
$this->app->bind(AuthenticationServiceInterface::class, AuthenticationService::class);
$this->app->singleton(SchemaFactory::class, function ($app) {
$service = new SchemaFactory($app->make('graphqliteCache'), new SanePsr11ContainerAdapter($app));
$service->setAuthenticationService($app[AuthenticationService::class]);
$service->setAuthorizationService($app[AuthorizationService::class]);
$service->addParameterMiddleware($app[ValidateFieldMiddleware::class]);
$service->addTypeMapperFactory($app[PaginatorTypeMapperFactory::class]);
$controllers = config('graphqlite.controllers', 'App\\Http\\Controllers');
if (!is_iterable($controllers)) {
$controllers = [ $controllers ];
}
$types = config('graphqlite.types', 'App\\');
if (!is_iterable($types)) {
$types = [ $types ];
}
foreach ($controllers as $namespace) {
$service->addControllerNamespace($namespace);
}
foreach ($types as $namespace) {
$service->addTypeNamespace($namespace);
}
if ($this->app->environment('production')) {
$service->prodMode();
}
return $service;
});
$this->app->singleton(Schema::class, function ($app) {
/** @var SchemaFactory $schemaFactory */
$schemaFactory = $app->make(SchemaFactory::class);
return $schemaFactory->createSchema();
});
}
private function getParentClassDirectory(): string
{
$reflection = new \ReflectionObject($this);
return pathinfo($reflection->getParentClass()->getFileName(), PATHINFO_DIRNAME);
}
}
With these changes, you are good to go.
Cheers, Rajeev