wwwision/graphql
Base package to create GraphQL endpoints with Flow
Maintainers
Requires
- php: >=8.1
- neos/flow: ^7.3 || ^8.0
- psr/http-message: ^1 || ^2
- webonyx/graphql-php: ^15
- wwwision/types-graphql: ^1.2
Requires (Dev)
- roave/security-advisories: dev-latest
Suggests
None
Provides
None
Conflicts
None
Replaces
None
MIT 563b345b02563d4ae64ad3608f53323e56336efa
- bwaidelich <b.waidelich.woop@wwwision.de>
This package is auto-updated.
Last update: 2026-06-12 22:36:16 UTC
README
Easily create GraphQL APIs with https://www.neos.io/ and https://flow.neos.io/.
Background
This package is a small collection of tools that'll make it easier to provide GraphQL endpoints with Neos and Flow. It is a wrapper for the PHP port of webonyx that comes with automatic Schema generation from PHP code (using wwwision/types) and an easy-to-configure PSR-15 compatible HTTP middleware.
Usage
Install via composer:
composer require wwwision/graphql
Simple tutorial
Create a class containing at least one public method with a Query attribute (see wwwision/types-graphql for more details):
// YourApi.php <?php namespace Your\Package; use Neos\Flow\Annotations as Flow; use Wwwision\TypesGraphQL\Attributes\Query; #[Flow\Scope('singleton')] final class YourApi { #[Query] public function ping(string $name): string { return strtoupper($name); } }
Now define a virtual object for the HTTP middleware
in some Objects.yaml configuration:
// Objects.yaml 'Your.Package:GraphQLMiddleware': className: 'Wwwision\GraphQL\GraphQLMiddleware' scope: singleton factoryObjectName: Wwwision\GraphQL\GraphQLMiddlewareFactory arguments: 1: # GraphQL URL value: '/graphql' 2: # PHP Class with the Query/Mutation attributed methods value: 'Your\Package\YourApi'
And, lastly, register that custom middleware in Settings.yaml:
// Settings.yaml Neos: Flow: http: middlewares: 'Your.Package:GraphQL': position: 'before routing' middleware: 'Your.Package:GraphQLMiddleware'
And with that, a working GraphQL API is accessible underneath /graphql.
Complex types
By default, all types with the same namespace as the specified API class will be resolved automatically, so you could do:
// YourApi.php // ... #[Query] public function ping(Name $name): Name { return strtoupper($name); }
as long as there is a suitable Name object in the same namespace (Your\Package).
To support types from different namespaces, those can be specified as third argument of the GraphQLMiddlewareFactory:
// Objects.yaml 'Your.Package:GraphQLMiddleware': # ... arguments: # ... # Look for classes in the following namespaces when resolving types: 3: value: - 'Your\Package\Types' - 'SomeOther\Package\Commands'
Authentication
Commonly the GraphQL middleware is executed before the routing middleware. So the Security\Context is not yet initialized.
This package allows you to "simulate" an MVC request though in order to initialize security.
This is done with the fourth argument of the GraphQLMiddlewareFactory:
// Objects.yaml 'Your.Package:GraphQLMiddleware': # ... arguments: # ... # Simulate a request to the Neos NodeController in order to initialize the security context and trigger the default Neos backend authentication provider 4: value: 'Neos\Neos\Controller\Frontend\NodeController'
Important
There must not be any gaps in the argument definitions due to the way Flow parses this configuration
To only specify the simulated controller, you can pass an empty value: [] array for the 3rd argument
Custom Resolvers
Starting with version 5.2 custom functions can be registered that extend the behavior of types dynamically:
// Objects.yaml 'Your.Package:GraphQLMiddleware': # ... arguments: # ... # custom resolvers 5: value: 'User': 'fullName': description: 'Custom resolver for User.fullName' resolverClassName: Some\Package\SomeCustomResolvers resolverMethodName: 'getFullName' 'isAllowed': resolverClassName: Some\Package\SomeCustomResolvers
Note
The resolverMethodName can be omitted if it is equal to the custom field name
Important
There must not be any gaps in the argument definitions due to the way Flow parses this configuration
To only specify custom resolves, you can pass an empty value: [] array for the 3rd argument and value: null for the fourth
All custom resolvers have to be public functions with the extended type as first argument (and optionally additional arguments) and a specified return type
For the example above, the corresponding resolver class could look like this:
final class SomeCustomResolvers { public function __construct(private readonly SomeDependency $incjection) {} public function getFullName(User $user): string { return $user->givenName . '' . $user->familyName; } public function isAllowed(User $user, Privilege $privilege): bool { return $this->incjection->isUserPrivilegeAllowed($user->id, $privilege); } }
More
See wwwision/types and wwwision/types-graphql for more examples and how to use more complex types.
FAQ
Contribution
Contributions in the form of issues or pull requests are highly appreciated
License
See LICENSE
