Working with generic types
- Creating a generic type
- Creating a type that doesn't map to a DataObject
- Building a custom query
- Add a custom query for any type of data
- Adding arguments
- Add arguments to your fields, queries, and mutations
- Adding descriptions
- Add descriptions to just about anything in your schema to improve your developer experience
- Enums, unions, and interfaces
- Add some non-object types to your schema
- The resolver discovery pattern
- How you can opt out of mapping fields to resolvers by adhering to naming conventions
- Adding pagination
- Add the pagination plugin to a generic query
You are viewing docs for silverstripe/graphql 4.x. If you are using 3.x, documentation can be found in the GitHub repository
Adding pagination
So far in this section we've created a simple generic query for a Country
type called readCountries
that takes a
limit
argument.
query {
readCountries(limit: 5) {
name
code
}
}
Let's take this a step further and paginate it using a plugin.
The paginate
plugin
Since pagination is a fairly common task, we can take advantage of some reusable code here and just add a generic plugin for paginating.
If you're paginating a DataList
, you might want to consider using models with read operations (instead of declaring
them as generic types with generic queries), which paginate by default using the paginateList
plugin.
You can use generic typing and follow the below instructions too but it requires code that, for DataObject
models,
you get for free.
Let's add the plugin to our query:
# app/_graphql/schema.yml
queries:
readCountries:
type: '[Country]'
plugins:
paginate: {}
Right now the plugin will add the necessary arguments to the query, and update the return types. But we still need to provide this generic plugin a way of actually limiting the result set, so we need a resolver.
# app/_graphql/schema.yml
queries:
readCountries:
type: '[Country]'
plugins:
paginate:
resolver: ['App\GraphQL\Resolver\MyResolver', 'paginateCountries']
Let's write that resolver code now:
namespace App\GraphQL\Resolver;
use Closure;
use SilverStripe\GraphQL\Schema\Plugin\PaginationPlugin;
class MyResolver
{
public static function paginateCountries(array $context): Closure
{
$maxLimit = $context['maxLimit'];
return function (array $countries, array $args) use ($maxLimit) {
$offset = $args['offset'];
$limit = $args['limit'];
$total = count($countries);
if ($limit > $maxLimit) {
$limit = $maxLimit;
}
$limitedList = array_slice($countries, $offset, $limit);
return PaginationPlugin::createPaginationResult($total, $limitedList, $limit, $offset);
};
}
}
A couple of things are going on here:
- Notice the new design pattern of a context-aware resolver. Since the plugin is configured with a
maxLimit
parameter, we need to get this information to the function that is ultimately used in the schema. Therefore, we create a dynamic function in a static method by wrapping it with context. It's kind of like a decorator. - As long as we can do the work of counting and limiting the array, the
PaginationPlugin
can handle the rest. It will return an array includingedges
,nodes
, andpageInfo
.
Rebuild the schema and test it out:
vendor/bin/sake dev/graphql/build schema=default
query {
readCountries(limit:3, offset:4) {
nodes {
name
}
}
}