Drupal + GraphQL Cheatsheet

<p><span class="drop-cap">H</span>ere's a cheat sheet to show you a couple of examples of <strong>GraphQL</strong> queries using version <strong>8.x-3.x</strong> of <a href="https://www.drupal.org/project/graphql">Drupal's GraphQL module</a>. This list is not intended to be exhaustive, it's just a sample of common queries that I've used in almost all decoupled sites that I've worked on.</p> <p>Enough talking, show me the code! ?</p> <h2>Get a single node by its nid</h2> <p>Suppose that we have a node with id <code>1</code>. To query the basic information about that node, we can use the query below.</p> <pre> <code class="language-json">query myQuery { nodeById(id: "1") { entityId title status } } </code></pre> <p>Only fields that belong to all entities and nodes can be fetched using this query. Keep in mind that <strong>entityId</strong> belongs to all entities (a node is an entity), and <strong>title</strong> and <strong>status</strong> are fields of all nodes, regardless of their bundle.</p> <h2>Get a single node of a specific content type by its nid</h2> <p>If we want to retrieve fields that belong only to a specific content type we have to use the fragment associated with that content type. For example, suppose that we have the <strong>Article</strong> content type, and it has the <strong>field_kicker</strong> field, so in order to get the value of the <strong>field_kicker</strong>, wrap the <strong>fieldKicker</strong> field inside the <strong>NodeArticle</strong> fragment.</p> <pre> <code class="language-json">query myQuery { nodeById(id: "1") { entityId title status ... on NodeArticle { fieldKicker } } }</code></pre> <p>Remember that fields and properties are in <em>camelCase</em>, so <strong>field_kicker</strong> becomes <strong>fieldKicker</strong>. On the other hand, the nodes' fragments name has the form <em>Node</em> + <em>Content Type name</em>, so the fragment for the content type <strong>Article</strong> is <strong>NodeArticle</strong>.</p> <h2>Querying a list of nodes</h2> <p>Imagine that our content type <strong>Article</strong> has the field <strong>field_section</strong> that indicates the section the article belongs to. For the sake of simplicity, suppose that sections are stored as strings. So, to get the 10 latest articles of <em>Sports</em> section, we can use the next query.</p> <pre> <code class="language-json">query myQuery { nodeQuery( filter: { conditions: [ { field: "type", value: "article" }, { field: "status", value: "1" }, { field: "field_section", value: "sports" }, ] }, sort: { field: "created", direction: DESC }, limit: 10, ) { entities { entityId entityLabel } } }</code></pre> <p>If you want to filter by an entity reference, use the referenced entity id in the "<em>value</em>" field.</p> <h2>Querying taxonomy terms</h2> <p>To get a taxonomy terms list of a specific vocabulary, for example, the "<em>tags</em>" vocabulary, use a query like the one below.</p> <pre> <code class="language-json">query myQuery { taxonomyTermQuery( filter: { conditions: [ { field: "vid", value: "tags" }, { field: "status", value: "1" }, ], }, ) { entities { entityId entityLabel } } }</code></pre> <h2>Using aliases</h2> <p>We can rename the result of a field to anything we want.</p> <pre> <code class="language-json">query myQuery { nodeById(id: "1") { id: entityId title ... on NodeArticle { section: fieldSection } } }</code></pre> <p>And we'll get the result</p> <pre> <code class="language-json">{ "data": { "nodeById": { "id": "1", "title": "My awesome article!", "section": "sports" } } }</code></pre> <p>Sometimes is necessary to use aliases, for example when we want to make the same query in a single request.</p> <pre> <code class="language-json">query myQuery { tags: taxonomyTermQuery( filter: { conditions: [{ field: "vid", value: "tags" }] }, ) { entities { entityLabel } } topics: taxonomyTermQuery( filter: { conditions: [{ field: "vid", value: "topics" }] }, ) { entities { entityLabel } } }</code></pre> <p>In the example above we are querying <em>tags</em> and <em>topics</em> using <strong>taxonomyTermQuery</strong> in both cases, so in order to differentiate the results, we are forced to use aliases.</p> <h2>Using fragments</h2> <p>There are times when we want to reuse the same query in many places. For example, suppose that our articles have a field <strong>field_tags</strong> that reference taxonomy terms of vocabulary <strong>Tags</strong>, and in another part of our site we show a list of those tags in the same way that we do in the articles, I mean, using the same fields. In these kinds of cases, fragments are useful to avoid code duplication.</p> <pre> <code class="language-json">query myQuery { article: nodeById(id: "1") { id: entityId title ... on NodeArticle { fieldTags { entity { ...Tag } } } } tags: taxonomyTermQuery( filter: { conditions: [{ field: "vid", value: "tags" }] }, ) { entities { ...Tag } } } fragment Tag on TaxonomyTermTags { id: entityId name: entityLabel }</code></pre> <h2>Querying referenced paragraphs</h2> <p>A very common use case for paragraphs is when a content type has a field that can reference multiple types of paragraphs. For example, the content type <strong>Page</strong> can have a <strong>field_sections</strong> field that references paragraphs of type <strong>tag_section</strong> for showing the latest tags, and a paragraph of type <strong>articles_section</strong> for the latest articles. We are not limited to show only one type of paragraph per field, to query all the values for the <strong>field_section</strong> we just need to list the supported fragments for that field. It'll be clear with an example.</p> <pre> <code class="language-json">query myQuery { page: nodeById(id: "2") { title ... on NodePage { fieldSections { entity { ...TagSection ...ArticlesSection } } } } } fragment TagSection on ParagraphTagSection { # The required fields } fragment ArticlesSection on ParagraphTagSection { # The required fields }</code></pre> <p>The use of fragments in the query above is not necessary but makes the code more readable. Note that even though the first listed fragment is the <strong>TagSection</strong>, this doesn't mean that the section to appear first will be the <strong>TagSection</strong>, the order saved in the CMS is preserved.</p> <h2>Querying image with Image Styles</h2> <p>Suppose that we have an image style <strong>square_max_800_px</strong> as machine name, in <strong>GraphQL</strong> the image style name is transformed to <strong>SQUAREMAX800PX</strong>. Note how the underscores are dropped and the image style's machine name is capitalized.</p> <pre> <code class="language-json">query myQuery { nodeById(id: "1") { title fieldImage { url alt title squareStyle: derivative(style: SQUAREMAX800PX) { url width height } } } }</code></pre> <p>We can fetch more than one image style per single query using aliases.</p> <pre> <code class="language-json">query myQuery { nodeById(id: "1") { title fieldImage { url alt title squareStyle: derivative(style: SQUAREMAX800PX) { url width height } wideStyle: derivative(style: WIDEMAX800PX) { url width height } } } }</code></pre> <h2>Format dates</h2> <p><strong>GraphQL</strong> supports some data transformations out of the box, for example, to format dates.</p> <pre> <code class="language-json">query myQuery { nodeById(id: "1") { title created: entityCreated(format: "d/m/yy") } }</code></pre> <p>The <a href="https://www.php.net/manual/es/function.date">available formats</a> are those supported by the date function in PHP.</p> <p> </p>

Related blog posts

Pantheon Discord Webhook Integration

Pantheon Quicksilver script to send notifications to Discord

Lando + Composer FTW

This is just one of those moments when I realized something obvious. I just wanted to share it.

Hand-picked content at the top of a chronologically sorted view: Option 2, Entity queues

Following the one way or another series we’ll solve the problem of overriding the default order of a view with hand picked contents through Entity queues.