Skip to content Skip to footer navigation

GraphQL API

The GraphQL API is a read-only API for delivering content from Statamic to your frontend, external apps, SPAs, and numerous other possible sources. Content is delivered as JSON data.

(If you're interested in a REST API, we have one of those too.)

Enable GraphQL#

To enable the GraphQL API, add the following to your .env file:

STATAMIC_GRAPHQL_ENABLED=true
env
env

Or you can enable it for all environments in config/statamic/graphql.php:

'enabled' => true,
php
php

You will also need to enable the resources you want to be available. For security, they're all disabled by default.

Hot Tip!

When GraphQL is enabled, GraphiQL is available in the Control Panel. This allows you to explore and test available queries and fields.

A troll pointing a teaching stick
Heads up

If you publish the underlying package's config, the query routes will be enabled regardless of whether you've disabled it in the Statamic config.

A troll pointing a teaching stick

Enable resources#

You can enable resources (ie. Collections, Taxonomies, etc.) in your config/statamic/graphql.php config:

'resources' => [
'collections' => true,
'taxonomies' => true,
// etc.
]
php
php

Enable specific sub-resources#

If you want more granular control over which sub-resources are enabled within a resource type (ie. enabling specific Collection queries only), you can use array syntax:

'resources' => [
'collections' => [
'articles' => true,
'pages' => true,
// 'events' => false, // Sub-resources are disabled by default
],
'taxonomies' => true,
// etc.
]
php
php

Interfaces#

Statamic will provide "interface" types, which describe more generic items. For instance, an EntryInterface exists for all
entries, which would provide fields like id, slug, status, title, and so on.

In addition to the interfaces, Statamic will provide implementations of them, which would come from the blueprints.

For example, if you had a collection named pages, and it had blueprints of page and home, you would find Entry_Pages_Page
and Entry_Pages_Home types. These implementations would provide fields specific to the blueprint, like subtitle, content, etc.

{
entries {
id
title
... on Entry_Pages_Page {
subtitle
content
}
... on Entry_Pages_Home {
hero_intro
hero_image
}
}
}
graphql
graphql

Queries#

Statamic has a number of root level queries you can perform to get data.

You can read about the available queries further down the page,
but know that you can perform more than one query at a time. They just need to be at the top level of your GraphQL query body.

For example, the following would perform both entries and collections queries

{
entries {
# ...
}
collections {
# ...
}
}
graphql
graphql

The response will contain the results of both queries:

{
"entries": { /* ... */ },
"collections": { /* ... */ },
}
json
json

Note that you can even perform the same query multiple times. If you want to do this, you should use aliases:

{
home: entry(id: "home") {
title
}
contact: entry(id: "contact") {
title
}
}
graphql
graphql
{
"home": { /* ... */ },
"contact": { /* ... */ },
}
json
json

Available queries#

Ping#

Used for testing that your connection works. If you send a query of {ping}, you should receive {"data": {"ping": "pong"}}.

{
ping
}
graphql
graphql
{
"data": {
"ping": "pong"
}
}
json
json

Collections#

Used for querying collections.

Returns a list of Collection types.

{
collections {
handle
title
}
}
graphql
graphql
{
"collections": [
{ "handle": "blog", "title": "Blog Posts" },
{ "handle": "events", "title": "Events" },
]
}
json
json

Collection#

Used for querying a single collection.

Returns a Collection type.

{
collection(handle: "blog") {
handle
title
}
}
graphql
graphql
{
"collections": {
"handle": "blog",
"title": "Blog Posts"
}
}
json
json

Entries#

Used for querying multiple entries.

Returns a paginated list of EntryInterface types.

Argument Type Description
collection [String] Narrows down the results by entries in one or more collections.
limit Int The number of results to be shown per paginated page.
page Int The paginated page to be shown. Defaults to 1.
filter JsonArgument Narrows down the results based on filters.
sort [String] Sorts the results based on one or more fields and directions.

Example query and response:

{
entries {
current_page
data {
id
title
}
}
}
graphql
graphql
{
"entries": {
"current_page": 1,
"data": [
{ "id": 1, "title": "First Entry" },
{ "id": 2, "title": "Second Entry" }
]
}
}
json
json

Entry#

Used for querying a single entry.

{
entry(id: 1) {
id
title
}
}
graphql
graphql
{
"entry": {
"id": 1,
"title": "First Entry"
}
}
json
json

Asset containers#

Used for querying asset containers.

{
assetContainers {
handle
title
}
}
graphql
graphql
{
"assetContainers": [
{ "handle": "images", "title": "Images" },
{ "handle": "documents", "title": "Documents" },
]
}
json
json

Asset container#

Used for querying a single asset container.

Returns an AssetContainer type.

{
assetContainer(handle: "images") {
handle
title
}
}
graphql
graphql
{
"assetContainer": {
"handle": "images",
"title": "Images"
}
}
json
json
Argument Type Description
handle String! Specifies which asset container to retrieve.

Assets#

Used for querying multiple assets of an asset container.

Returns a paginated list of AssetInterface types.

Argument Type Description
container String! Specifies which asset container to query.
limit Int The number of results to be shown per paginated page.
page Int The paginated page to be shown. Defaults to 1.
filter JsonArgument Narrows down the results based on filters.
sort [String] Sorts the results based on one or more fields and directions.

Example query and response:

{
assets(container: "images") {
current_page
data {
url
}
}
}
graphql
graphql
{
"entries": {
"current_page": 1,
"data": [
{ "url": "/assets/images/001.jpg" },
{ "url": "/assets/images/002.jpg" },
]
}
}
json
json

Asset#

Used for querying a single asset.

{
asset(id: 1) {
id
title
}
}
graphql
graphql
{
"asset": {
"id": 1,
"title": "First Entry"
}
}
json
json

You can either query by id, or by container and path together.

Argument Type Description
id String The ID of the asset. If you use this, you don't need container or path.
container String The container to look for the asset. You must also provide the path.
path String The path to the asset, relative to the container. You must also provide the container.

Taxonomies#

Used for querying taxonomies.

{
taxonomies {
handle
title
}
}
graphql
graphql
{
"taxonomies": [
{ "handle": "tags", "title": "Tags" },
{ "handle": "categories", "title": "Categories" },
]
}
json
json

Taxonomy#

Used for querying a single taxonomy.

{
taxonomy(handle: "tags") {
handle
title
}
}
graphql
graphql
{
"taxonomy": {
"handle": "tags",
"title": "Tags"
}
}
json
json

Terms#

Used for querying multiple taxonomy terms.

Returns a paginated list of TermInterface types.

Argument Type Description
taxonomy [String] Narrows down the results by terms in one or more taxonomies.
limit Int The number of results to be shown per paginated page.
page Int The paginated page to be shown. Defaults to 1.
filter JsonArgument Narrows down the results based on filters.
sort [String] Sorts the results based on one or more fields and directions.

Example query and response:

{
terms {
current_page
data {
id
title
}
}
}
graphql
graphql
{
"terms": {
"current_page": 1,
"data": [
{ "id": "tags::one", "title": "Tag One" },
{ "id": "tags::two", "title": "Tag Two" }
]
}
}
json
json

Term#

Used for querying a single taxonomy term.

{
term(id: "tags::one") {
id
title
}
}
graphql
graphql
{
"term": {
"id": "tags::one",
"title": "Tag One"
}
}
json
json

Global sets#

Used for querying multiple global sets.

Returns a list of GlobalSetInterface types.

Argument Type Description
taxonomy [String] Narrows down the results by terms in one or more taxonomies.
limit Int The number of results to be shown per paginated page.
page Int The paginated page to be shown. Defaults to 1.
sort [String] Sorts the results based on one or more fields and directions.

Example query and response:

{
globalSets {
title
handle
... on GlobalSet_Social {
twitter
}
... on GlobalSet_Company {
company_name
}
}
}
graphql
graphql
{
"globalSets": [
{ "handle": "social", "twitter": "@statamic" },
{ "handle": "company", "company_name": "Statamic" },
]
}
json
json

Global set#

Used for querying a single global set.

{
globalSet(handle: "social") {
title
handle
... on GlobalSet_Social {
twitter
}
}
}
graphql
graphql
{
"globalSet": {
"title": "Social",
"handle": "social",
"twitter": "@statamic",
}
}
json
json

Forms#

Used for querying multiple forms.

{
forms {
handle
title
fields {
handle
display
}
}
}
graphql
graphql
{
"forms": [
{
"handle": "contact",
"title": "Contact",
"fields": [
{ "handle": "name", "display": "Name" },
{ "handle": "email", "display": "Email" },
{ "handle": "inquiry", "display": "Inquiry" }
]
}
]
}
json
json

Form#

Used for querying a single form.

{
form(handle: "contact") {
handle
title
fields {
handle
display
}
}
}
graphql
graphql
{
"form": {
"handle": "contact",
"title": "Contact",
"fields": [
{ "handle": "name", "display": "Name" },
{ "handle": "email", "display": "Email" },
{ "handle": "inquiry", "display": "Inquiry" }
]
}
}
json
json

Used for querying Navs.

{
navs {
handle
title
}
}
graphql
graphql
{
"navs": [
{ "handle": "header_links", "title": "Header Links" },
{ "handle": "footer_links", "title": "Footer Links" },
]
}
json
json

Used for querying a single Nav.

{
nav(handle: "footer") {
handle
title
}
}
graphql
graphql
{
"nav": {
"handle": "footer",
"title": "Footer Links"
}
}
json
json

Users#

Used for querying multiple users.

Argument Type Description
limit Int The number of results to be shown per paginated page.
page Int The paginated page to be shown. Defaults to 1.
filter JsonArgument Narrows down the results based on filters.
sort [String] Sorts the results based on one or more fields and directions.

Example query and response:

{
users {
current_page
data {
name
email
}
}
}
graphql
graphql
{
"users": {
"current_page": 1,
"data": [
{ "name": "David Hasselhoff", "email": "thehoff@statamic.com" },
{ "name": "Chuck Norris", "email": "norris@statamic.com" },
]
}
}
json
json

User#

Used for querying a single user.

{
user(email: "thehoff@statamic.com") {
name
email
}
}
graphql
graphql
{
"user": {
"name": "David Hasselhoff",
"email": "thehoff@statamic.com"
}
}
json
json

You can query by either id or email.

Argument Type Description
id String The ID of the user. If you use this, you don't email.
email String The email address of the user. If you use this, you don't id.

Custom queries#

Here's an example of a basic query class. It has the name attribute which is the key the user needs to put in the request, any number of middleware, the type(s) that will be returned, any arguments, and how the data should be resolved.

use Statamic\Facades\GraphQL;
use Statamic\GraphQL\Queries\Query;
 
class Products extends Query
{
protected $attributes = [
'name' => 'products',
];
 
protected $middleware = [
MyMiddleware::class,
];
 
public function type(): Type
{
return GraphQL::paginate(GraphQL::type(ProductType::NAME));
}
 
public function args(): array
{
return [
'limit' => GraphQL::int(),
];
}
 
public function resolve($root, $args)
{
return Product::paginate($args['limit']);
}
}
php
php
{
products {
name
price
}
}
graphql
graphql

You may add your own queries to Statamic's default schema.

You can add them to the config file, which makes sense for app specific queries:

// config/statamic/graphql.php
'queries' => [
MyCustomQuery::class
]
php
php

Or, you may use the addQuery method on the facade, which would be useful for addons.

GraphQL::addQuery(MyCustomQuery::class);
php
php

Types#

EntryInterface#

Field Type Description
id ID!
title String!

Each EntryInterface will also have implementations for each collection/blueprint combination.

You will need to query the implementations using fragments in order to get blueprint-specific fields.

{
entries {
id
title
... on Entry_Blog_Post {
intro
content
}
... on Entry_Blog_ArtDirected_Post {
hero_image
content
}
}
}
graphql
graphql

The fieldtypes will define their types. For instance, a text field will be a String, a grid field will expose a list of GridItem types.

Collection#

Field Type Description
handle String!
title String!
structure CollectionStructure If the collection is structured (e.g. a "pages" collection), you can use this to query its tree.

CollectionStructure#

Field Type Description
handle String!
title String!
tree [CollectionTreeBranch] A list of tree branches.

CollectionTreeBranch#

Represents a branch within a structured collection's tree.

Field Type Description
depth Int! The nesting level of the current branch.
entry (or page) EntryInterface Contains the entry's fields.
children [CollectionTreeBranch] A list of tree branches.
Hot Tip!

It's not possible to perform recursive queries in GraphQL. If you want to retrieve multiple levels of child branches, take a look at a workaround in recursive tree branches below.

A troll pointing a teaching stick

Represents a branch within a nav's tree.

Field Type Description
depth Int! The nesting level of the current branch.
page PageInterface Contains the page's fields.
children [NavTreeBranch] A list of tree branches.
Hot Tip!

It's not possible to perform recursive queries in GraphQL. If you want to retrieve multiple levels of child branches, take a look at a workaround in recursive tree branches below.

A troll pointing a teaching stick

PageInterface#

A "page" within a nav's tree.

Field Type Description
id ID! The ID of the page.
entry_id ID The entry ID.
title String For entry pages, it's the entry's title unless overridden on the branch. For basic pages, it's the title.
url String For entry pages, it's the entry's url. For basic pages, it's the url. For text-only pages it'll be null.
permalink String The absolute version of url.

If you want to query any fields that you've added to the nav's blueprint, you have 4 different options available to you that you can use as inline fragments.
You can use more than one at a time:

  • EntryInterface for all entry pages.
  • NavEntryPage_{NavHandle}_{Collection}_{Blueprint} for a specific entry/blueprint combination on entry pages.
  • NavBasicPage_{NavHandle} for basic non-entry pages.
  • NavPage_{NavHandle} for either basic or entry pages.
page {
title
url
... on EntryInterface {
# ...
}
... on NavPage_HeaderLinks {
# ...
}
... on NavBasicPage_HeaderLinks {
# ...
}
... on NavEntryPage_HeaderLinks_Blog_ArtDirected {
# ...
}
}
graphql
graphql

TermInterface#

Field Type Description
id ID!
title String!
slug String!

Each TermInterface will also have implementations for each taxonomy/blueprint combination.

You will need to query the implementations using fragments in order to get blueprint-specific fields.

{
terms {
id
title
... on Term_Tags_RegularTag {
content
}
... on Term_Tags_SpecialTag {
how_special
content
}
}
}
graphql
graphql

The fieldtypes will define their types. For instance, a text field will be a String, a grid field will expose a list of GridItem types.

AssetInterface#

Field Type Description
path String! The path to the asset.

Each AssetInterface will also have an implementation for each asset container's blueprint.

You will need to query the implementations using fragments in order to get blueprint-specific fields.

{
entries {
path
... on Asset_Images {
alt
}
}
}
graphql
graphql

The fieldtypes will define their types. For instance, a text field will be a String, a grid field will expose a list of GridItem types.

GlobalSetInterface#

Field Type Description
handle String! The handle of the set.
title String! The title of the set.

Each GlobalSetInterface will also have an implementation for each set's blueprint.

Hot Tip!

While Statamic doesn't enforce a blueprint for globals (see Blueprint is Optional), it is required within the GraphQL context. Fields that haven't been explicitly added to a blueprint will not be available.

A troll pointing a teaching stick

You will need to query the implementations using fragments in order to get blueprint-specific fields.

{
globalSets {
handle
... on GlobalSet_Social {
twitter
}
}
}
graphql
graphql

The fieldtypes will define their types. For instance, a text field will be a String, a grid field will expose a list of GridItem types.

Code#

Field Type Description
code String! The actual code value.
mode String! The language "mode".

The code fieldtype will return this type when mode_selectable is enabled. Otherwise, it'll just be a string.

{
snippet {
code
mode
}
}
graphql
graphql

Filtering#

Enabling filters#

For security, filtering is disabled by default. To enable, you'll need to opt in by defining a list of allowed_filters for each sub-resource in your config/statamic/graphql.php config:

'resources' => [
'collections' => [
'articles' => [
'allowed_filters' => ['title', 'status'],
],
'pages' => [
'allowed_filters' => ['title'],
],
'events' => true, // Enable this collection without filters
'products' => true, // Enable this collection without filters
],
'taxonomies' => [
'topics' => [
'allowed_filters' => ['slug'],
],
'tags' => true, // Enable this taxonomy without filters
],
// etc.
],
php
php

For queries that don't have sub-resources (ie. users), you can define allowed_filters at the top level of that resource config:

'resources' => [
'users' => [
'allowed_filters' => ['name', 'email'],
],
],
php
php

Using filters#

You can filter the results of listing queries (like entries) using the filter argument. This argument accepts a JSON object containing different
conditions.

{
entries(filter: {
title: { contains: "rad", ends_with: "!" }
}) {
data {
title
}
}
}
graphql
graphql
{
"data": [
{ "title": "That was so rad!" },
{ "title": "I wish I was as cool as Daniel Radcliffe!" },
]
}
json
json

If you only need to do a simple "equals" condition, then you can use a string and omit the condition name, like the rating here:

{
entries(filter: {
title: { contains: "rad" }
rating: 5
}) {
# ...
}
graphql
graphql

If you need to use the same condition on the same field more than once, you can use the array syntax:

{
entries(filter: {
title: [
{ contains: "rad" },
{ contains: "awesome" },
]
}) {
# ...
}
graphql
graphql

Advanced filtering config#

You can also allow filters on all enabled sub-resources using a * wildcard config. For example, here we'll enable only the articles, pages, and products collections, with title filtering enabled on each, in addition to status filtering on the articles collection specifically:

'resources' => [
'collections' => [
'*' => [
'allowed_filters' => ['title'], // Enabled for all collections
],
'articles' => [
'allowed_filters' => ['status'], // Also enable on articles
],
'pages' => true,
'products' => true,
],
],
php
php

If you've enabled filters using the * wildcard config, you can disable filters on a specific sub-resource by setting allowed_filters to false:

'resources' => [
'collections' => [
'*' => [
'allowed_filters' => ['title'], // Enabled for all collections
],
'articles' => [
'allowed_filters' => false, // Disable filters on articles
],
'pages' => true,
'products' => true,
],
],
php
php

Or you can enable queries and filters on all sub-resources at once by setting both enabled and allowed_filters within your * wildcard config:

'resources' => [
'collections' => [
'*' => [
'enabled' => true, // All collection queries enabled
'allowed_filters' => ['title'], // With filters enabled for all
],
],
],
php
php

Sorting#

You can sort the results of listing queries (like entries) on one or multiple fields, in any direction.

{
entries(sort: "title") {
# ...
}
graphql
graphql
{
entries(sort: "title desc") {
# ...
}
graphql
graphql
{
entries(sort: ["price desc", "title asc"]) {
# ...
}
graphql
graphql

Pagination#

Some queries (like entries) will provide their results using pagination.

In a paginated response, you will find the actual items within a data key.

By default there will be 1000 per page. You can change this using a limit argument.
You can specify the current paginated page using the page argument.

{
entries(limit: 15, page: 2) {
current_page
has_more_pages
data {
# ...
}
}
}
graphql
graphql
Field Type Description
data [mixed] A list of items on the current page. In an entries query, there will be EntryInterface types, etc.
total Int! Number of total items selected by the query.
per_page Int! Number of items returned per page.
current_page Int! Current page of the cursor.
from Int Number of the first item returned.
to Int Number of the last item returned.
last_page Int! The last page (number of pages).
has_more_pages Boolean! Determines if cursor has more pages after the current page.

Fieldtypes#

Replicator#

Replicator fields require that you query each set using a separate fragment.

The fragments are named after your configured sets using StudlyCased field and set handles. e.g. Set_{ReplicatorFieldName}_{SetHandle}

fields:
-
handle: content_blocks
field:
type: replicator
sets:
image:
fields:
-
handle: image
type: assets
max_files: 1
pull_quote:
fields:
-
handle: quote
field:
type: textarea
-
handle: author
field:
type: text
yaml
yaml
{
content_blocks {
... on Set_ContentBlocks_Image {
type
image
}
... on Set_ContentBlocks_PullQuote {
type
quote
author
}
}
}
graphql
graphql
Hot Tip!

If you have nested fields, include each parent's handle, (and grandparent's, great grandparent's etc), like so: Set_TopLevelReplicator_NestedReplicator_DeeplyNestedReplicator_SetHandle

A troll pointing a teaching stick

Bard#

Bard fields work the same as Replicator, except that you also have an additional BardText for the text fragment.

{
content_blocks {
... on BardText {
type
text
}
... on Set_ContentBlocks_Image {
type
image
}
... on Set_ContentBlocks_PullQuote {
type
quote
author
}
}
}
graphql
graphql

Grid#

Grid fields can be queried with no extra requirements. You can just use the nested field handles.

{
cars {
make
model
}
}
graphql
graphql

Select, radio, checkboxes, and button group#

These fieldtypes provide you with labels and values. You'll need to use a sub selection.

my_select_field {
value
label
}
graphql
graphql
"my_single_select_field": {
"value": "potato",
"label": "Potato"
}
json
json

The same syntax is used when multiple values are expected. e.g. a select field with multiple values enabled, or a checkboxes field. You'll just get a nested array returned.

"my_multi_select_field": [
{
"value": "potato",
"label": "Potato"
},
{
"value": "tomato",
"label": "Tomato",
}
]
json
json

Recursive tree branches#

Often, when dealing with navs, you need to recursively output all the child branches. For example, when using the nav tag in Antlers, you might do something like this:

<ul>
{{ nav }}
<li>
<a href="{{ url }}">{{ title }}</a>
{{ if children }}
<ul>{{ *recursive children* }}</ul>
{{ /if }}
</li>
{{ /nav }}
</ul>
antlers
antlers

In GraphQL, it's not possible to perform recursive queries like that. You'll need to explicitly query each level:

{
nav(handle: "links") {
tree {
page {
title
url
}
children {
page {
title
url
}
children {
page {
title
url
}
}
}
}
}
}
graphql
graphql

In this example, if you wanted anything more than title and url, you'd need to add them to each level.

This can quickly become tedious and is very repetitive, so here's a workaround using fragments.

If you wanted to add more fields, you only need to do it one spot - the Fields fragment. If you want to query more levels, you can just increase the nesting level of the RecursiveChildren fragment.

{
nav(handle: "links") {
tree {
...Fields
...RecursiveChildren
}
}
}
 
fragment Fields on NavTreeBranch {
depth
page {
title
url
# any other fields you want for each branch
}
}
 
fragment RecursiveChildren on NavTreeBranch {
children {
...Fields
children {
...Fields
children {
...Fields
# just keep repeating this as deep as necessary
}
}
}
}
graphql
graphql

Hat tip to Hash Interactive for their blog post on this technique.

Custom fieldtypes#

A fieldtype can define what GraphQL type will be used. By default, all fieldtypes will return strings.

use GraphQL\Type\Definition\Type;
 
public function toGqlType()
{
return GraphQL::string();
}
php
php

You're free to return an array with a more complicated structure in order to provide arguments, etc.

use GraphQL\Type\Definition\Type;
 
public function toGqlType()
{
return [
'type' => GraphQL::string(),
'args' => [
//
]
];
}
php
php

If you need to register any types, the fieldtype can do that in the addGqlTypes method:

public function addGqlTypes()
{
// A class that extends Rebing\GraphQL\Support\Type
$type = MyType::class; // or `new MyType;`
 
GraphQL::addType($type);
}
php
php

Laravel package#

Under the hood, Statamic uses the rebing/graphql-laravel package.

By default, the integration should feel seamless and you won't even know another package is being used. Statamic will perform the following automatic configuration of this package:

  • Setting up the default schema to Statamic's.
  • Disabling the /graphiql route (since we have our own inside the Control Panel)

However, you're free to use this package on its own, as if you've installed it into a standalone Laravel application.

If Statamic detects that you've published the package's config file (located at config/graphql.php), it will assume you're trying to use it manually and will
avoid doing the automatic setup steps mentioned above.

If you'd like to use Statamic's GraphQL schema within the config file (maybe you want a different default, and want Statamic's one at /graphql/statamic) you can use the DefaultSchema class.

[
'schemas' => [
'statamic' => \Statamic\GraphQL\DefaultSchema::class
]
]
php
php

Authorization#

By default, all queries are allowed by anyone. We plan to add native features in the future.

You can define custom authorization logic for any query by providing a closure to the static auth method.

EntriesQuery::auth(function () {
return true; // true authorizes, false denies.
});
php
php

Custom fields#

You can add fields to certain types by using the addField method on the facade.

The method expects the type name, the field name, and a closure that returns a GraphQL field definition array.

For example, if you wanted to include a thumbnail from an asset field named image, you could do that here. You can even have arguments. In this example, we'll expect the width of the thumbnail to be passed in.

use GraphQL\Type\Definition\Type;
use Statamic\Facades\GraphQL;
use Statamic\Facades\Image;
use Statamic\Facades\URL;
 
GraphQL::addField('EntryInterface', 'thumbnail', function () {
return [
'type' => GraphQL::string(),
'args' => [
'width' => [
'type' => GraphQL::int(),
]
],
'resolve' => function ($entry, $args) {
$asset = $entry->image;
$url = Image::manipulate($asset)->width($args['width'])->build();
return URL::makeAbsolute($url);
}
];
});
php
php
{
entry(id: 1) {
thumbnail(width: 100)
}
}
graphql
graphql
{
"entry": {
"thumbnail": "http://yoursite.com/img/asset/abc123?w=100"
}
}
json
json

The closure you pass to the method should return a GraphQL field definition array.

You may add custom fields to the following types and any of their implementations:

  • EntryInterface
  • PageInterface
  • TermInterface
  • AssetInterface
  • GlobalSetInterface

Caching#

GraphQL uses a basic whole-response cache by default. Each query/variables combination's response will be cached for an hour. You may customize the cache expiry in config/statamic/graphql.php.

'cache' => [
'expiry' => 60,
],
php
php

Cache invalidation#

Cached responses are automatically invalidated when content is changed. Depending on your GraphQL usage and blueprint schema, you may also wish to ignore specific events when invalidating.

'cache' => [
'expiry' => 60,
'ignored_events' => [
\Statamic\Events\UserSaved::class,
\Statamic\Events\UserDeleted::class,
],
],
php
php

Disabling caching#

If you wish to disable caching altogether, set cache to false.

'cache' => false,
php
php

Custom middleware#

You may add custom middleware, which are identical to any other Laravel middleware class. They will be executed on all GraphQL requests (unless another middleware, e.g. caching, prevents it).

Use the handle method to perform some action, and pass the request on.

use Closure;
 
class MyMiddleware
{
public function handle($request, Closure $next)
{
// do something
 
return $next($request);
}
}
php
php

You may add your own middleware to Statamic's default schema.

You can add them to the config file, which makes sense for app specific middleware:

// config/statamic/graphql.php
'middleware' => [
MyMiddleware::class
]
php
php

Or, you may use the addMiddleware method on the facade, which would be useful for addons.

GraphQL::addMiddleware(MyMiddleware::class);
php
php

Troubleshooting#

"Cannot query field" error#

If you see an error like Cannot query field "entries" on type "Query", this likely means you haven't enabled that query. See Enable GraphQL.
After enabling it, you may need to clear your cache as the request would probably have been cached.