Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Rapid Data APIs using GraphQL and Drupal
Search
dirtystylus
May 10, 2019
Programming
78
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Rapid Data APIs using GraphQL and Drupal
A quick introduction to using GraphQL with Drupal 8.
dirtystylus
May 10, 2019
More Decks by dirtystylus
See All by dirtystylus
Decoupled Drupal Days 2018: Drupal Unhitched
mllobrera
0
390
Drupaldelphia 2018: Drupal Unhitched: The CMS in Decoupled Architectures
mllobrera
0
340
Drupalcon 2017: Pattern Language: Pattern Libraries in the Wild
mllobrera
0
530
DIW 2016: Github Project Scaffolding with Google Sheets and Zenhub
mllobrera
0
290
Philly Tech Week 2016: Decoupled Development: Feeding Your Application with Off-the-Shelf Tools
mllobrera
0
210
Drupal Beyond the Browser: Using Drupal to Power Apps and Touchscreens
mllobrera
0
410
Decoupled Development with WordPress JSON APIs
mllobrera
0
850
Drupaldelphia 2014: Progressive Enhancement in Drupal 7, Using Ajax-Include Patterns
mllobrera
0
210
Drupaldelphia 2013 - Tyler School of Art: A Case Study in User-Centered Decision-Making
mllobrera
0
1.1k
Other Decks in Programming
See All in Programming
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
290
機械に任せるテスト、人が見るテスト ⽣成AIで引き直した、運⽤保守フェーズの線引き
dske104
0
190
ハーネス設計入門 〜 基礎知識の整理から実務へのステップアップ 〜
kinopeee
28
32k
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
180
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
270
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
750
S3 TablesのIcebergデータに、AIエージェントからアクセスしてみた / 20261016developersio2026joetsu-accessing-iceberg-in-s3tables-via-agent
kasacchiful
PRO
0
150
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
R8 の設定見直しでアプリサイズ削減
andpad
0
220
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
1k
C#の現在地 進化の歴史と、AI時代の.NET Everywhere
neuecc
5
5.2k
現場と育てるRailsモデリング 〜Spreadsheetから始めたモデル設計〜
siroemk
2
590
Featured
See All Featured
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
340
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
500
Rails Girls Zürich Keynote
gr2m
96
14k
Technical Leadership for Architectural Decision Making
baasie
3
600
Producing Creativity
orderedlist
PRO
348
41k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
780
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
2
410
A better future with KSS
kneath
240
18k
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1k
Deep Space Network (abreviated)
tonyrice
0
370
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.7k
Paper Plane
katiecoart
PRO
5
54k
Transcript
Rapid Data APIs using GraphQL and Drupal Drupaldelphia 2019
Hi. Mark Llobrera Technology Director, Bluecadet Philadelphia @dirtystylus @bluecadet
None
None
None
None
Descriptive, Not Prescriptive Share experiences, not solutions.
What is GraphQL?
https://twitter.com/sarahmei/status/991878504391229440/photo/1
What is GraphQL?
What is GraphQL? •https://graphql.org
What is GraphQL? •https://graphql.org •“GraphQL is a query language for
your API.”
What is GraphQL? •https://graphql.org •“GraphQL is a query language for
your API.” •Facebook circa 2012, open-sourced circa 2015
Where We’ve Been
Where We’ve Been •REST (ish)
Where We’ve Been •REST (ish) •JSON API
What did we want from REST?
What did we want from REST? •More flexibility — lots
of schema revisions
What did we want from REST? •More flexibility — lots
of schema revisions •Speed in Development
What did we want from REST? •More flexibility — lots
of schema revisions •Speed in Development •Fewer requests
[ { "title": "Kim Stanley Robinson", "body": "<p>Author of the
<em>Mars</em> trilogy and <em>New York 2140</em>.</p>\r\n", "field_books": [ "2" ] }, { "title": "N. K. Jemisin", "body": "<p>Hugo Award-winning author of the Broken Earth trilogy.</p>\r\n", "field_books": [ "1" ] } ]
Use Cases for GraphQL (Bluecadet)
Use Cases for GraphQL (Bluecadet) •Multi-product projects
Use Cases for GraphQL (Bluecadet) •Multi-product projects •API to be
used by (un)known future products
Use Cases for GraphQL (Bluecadet) •Multi-product projects •API to be
used by (un)known future products •If your teams are not in tight communication
Drupal Implementation
Drupal Implementation •https://www.drupal.org/project/graphql
Drupal Implementation •https://www.drupal.org/project/graphql •https://github.com/drupal-graphql/graphql
Drupal Implementation •https://www.drupal.org/project/graphql •https://github.com/drupal-graphql/graphql •Drupal has schema support for Nodes,
Views, etc
Drupal Implementation •https://www.drupal.org/project/graphql •https://github.com/drupal-graphql/graphql •Drupal has schema support for Nodes,
Views, etc •Plugin architecture
Installation Note
Installation Note •Download the module (or use drush dl graphql)
Installation Note •Download the module (or use drush dl graphql)
•Run composer require webonyx/graphql-php at root level (error otherwise)
Installation Note •Download the module (or use drush dl graphql)
•Run composer require webonyx/graphql-php at root level (error otherwise) •Enable the module
GraphQL Basics
Queries query { nodeQuery { entities { entityId, entityLabel }
} }
Queries /graphql/?query=query%20{nodeQuery%20{entities%20{entityId,entityLabel}}}
Queries •You can run this in the GraphQL Explorer /graphql/?query=query%20{nodeQuery%20{entities%20{entityId,entityLabel}}}
Queries •You can run this in the GraphQL Explorer •What
about the browser? You can copy the query and pass it to the /graphql endpoint with the query parameter: /graphql/?query=query%20{nodeQuery%20{entities%20{entityId,entityLabel}}}
None
None
Queries: Alias query { nodeQuery { entities { node_id: entityId,
title: entityLabel } } }
Queries: Alias •This allows you to rename stuff query {
nodeQuery { entities { node_id: entityId, title: entityLabel } } }
Queries: Alias •This allows you to rename stuff query {
nodeQuery { entities { node_id: entityId, title: entityLabel } } }
Queries: Alias •This allows you to rename stuff query {
nodeQuery { entities { node_id: entityId, title: entityLabel } } }
Queries: Alias •This allows you to rename stuff query {
nodeQuery { entities { node_id: entityId, title: entityLabel } } }
Queries: Alias •This allows you to rename stuff query {
nodeQuery { entities { node_id: entityId, title: entityLabel } } }
Queries: Alias { "node_id": "26", "title": "The Herreshoff Manufacturing Company
is officially established." },
Queries: Alias { "entityId": "26", "entityLabel": "The Herreshoff Manufacturing Company
is officially established." },
Queries: Alias query { ship1: nodeById(id: "5") { entityLabel }
ship2:nodeById(id: "7") { entityLabel } }
Queries: Alias •Also allows you to run query twice query
{ ship1: nodeById(id: "5") { entityLabel } ship2:nodeById(id: "7") { entityLabel } }
Queries: Alias { "data": { "ship1": { "entityLabel": "Reliance -
America's Cup Defender" }, "ship2": { "entityLabel": "Alera - An Icon of One-Design Racing" } } }
Queries: Fragments query { nodeQuery { entities { entityId entityType
entityLabel } } }
Queries: Fragments query { nodeQuery { entities { ...nodeBasics }
} } fragment nodeBasics on Node { entityId entityType entityLabel }
Queries: Fragments
Queries: Fragments •DRYing out your queries using fragments can make
it easier to keep track of common structures
Queries: Arguments query getShip($nid: String = "") { nodeById(id:$nid) {
entityId entityType entityLabel } } {"nid": "5"}
Queries: Arguments •query query getShip($nid: String = "") { nodeById(id:$nid)
{ entityId entityType entityLabel } } {"nid": "5"}
Queries: Arguments •query query getShip($nid: String = "") { nodeById(id:$nid)
{ entityId entityType entityLabel } } {"nid": "5"}
Queries: Arguments •query query getShip($nid: String = "") { nodeById(id:$nid)
{ entityId entityType entityLabel } } •variable {"nid": "5"}
Queries: Arguments { "data": { "nodeById": { "entityId": "5", "entityType":
"node", "entityLabel": "Reliance - America's Cup Defender" } } }
Queries: Referenced Entities query { nodeQuery(filter:{conditions: [{field: "type", value: "vessel"}]})
{ entities { ...on NodeVessel { entityId, entityLabel, slides: fieldVSlides { slide: entity { fieldDisplayTitle { value } } } } } } }
Queries: Referenced Entities query { nodeQuery(filter:{conditions: [{field: "type", value: "vessel"}]})
{ entities { ...on NodeVessel { entityId, entityLabel, slides: fieldVSlides { slide: entity { fieldDisplayTitle { value } } } } } } }
Queries: Referenced Entities query { nodeQuery(filter:{conditions: [{field: "type", value: "vessel"}]})
{ entities { ...on NodeVessel { entityId, entityLabel, slides: fieldVSlides { slide: entity { fieldDisplayTitle { value } } } } } } }
Queries: Referenced Entities "entities": [ { "entityId": "5", "entityLabel": "Reliance
- America's Cup Defender", "slides": [ { "slide": { "fieldDisplayTitle": { "value": "Pushing The Limits of Design And Engineering" } } }, { "slide": { "fieldDisplayTitle": { "value": "Pushing the Bounds to Win" } } }, { "slide": { "fieldDisplayTitle": { "value": "Pushing the Bounds to Win" } } },
Queries: Filters query { nodeQuery(filter:{conditions: [{field: "type", value: "vessel"}]}) {
entities { entityId, entityLabel } } }
Queries: Filters •Usually node type query { nodeQuery(filter:{conditions: [{field: "type",
value: "vessel"}]}) { entities { entityId, entityLabel } } }
Queries: Filters { "entityId": "5", "entityLabel": "Reliance - America's Cup
Defender" }, { "entityId": "43", "entityLabel": "Cushing" }, { "entityId": "40", "entityLabel": "Asahi" }, { "entityId": "8", "entityLabel": "Buzzards Bay- Fast, Agile & Affordable" },
Slightly more advanced
Custom Types: Adding Fields to Types { nodeQuery(filter: {conditions: [{field:
"type", value: "vessel", operator: EQUAL}]}) { entities { ... on NodeVessel { entityId, title, fieldTimelineThumbnail { entity { fieldMediaImage { url } } } } } } }
Custom Types: Adding Fields to Types { nodeQuery(filter: {conditions: [{field:
"type", value: "vessel", operator: EQUAL}]}) { entities { ... on NodeVessel { entityId, title, fieldTimelineThumbnail { entity { fieldMediaImage { url } } } } } } }
Custom Types: Adding Fields to Types { nodeQuery(filter: {conditions: [{field:
"type", value: "vessel", operator: EQUAL}]}) { entities { ... on NodeVessel { entityId, title, fieldTimelineThumbnail { entity { fieldMediaImage { url } } } } } } }
Custom Types: Adding Fields to Types { "entityId": "5", "title":
"Reliance - America's Cup Defender", "fieldTimelineThumbnail": { "entity": { "fieldMediaImage": { "url": "https://mit-lsf-cms.ml/sites/default/files/images/Reliance_1.jpg" } } } },
Custom Types: Adding Fields to Types (Annotations) namespace Drupal\mit_graphql\Plugin\GraphQL\Fields\Entity\Fields\Image; use
Drupal\graphql\GraphQL\Execution\ResolveContext; use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase; use Drupal\image\Plugin\Field\FieldType\ImageItem; use GraphQL\Type\Definition\ResolveInfo; /** * Retrieve the image relative path. * * @GraphQLField( * id = "image_relative_path", * secure = true, * name = "relative_path", * type = "String", * field_types = {"image"}, * provider = "image", * deriver = "Drupal\graphql_core\Plugin\Deriver\Fields\EntityFieldPropertyDeriver" * ) */ class ImageRelativePath extends FieldPluginBase { /** * {@inheritdoc} */ protected function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) { if ($value instanceof ImageItem && $value->entity->access('view')) { $url = file_create_url($value->entity->getFileUri()); $relative_path = urldecode(file_url_transform_relative($url)); $replaced_relative_path = str_replace('/sites/default/files', '', $relative_path); yield $replaced_relative_path; } } }
Custom Types: Adding Fields to Types (Annotations) * @GraphQLField( *
id = "image_relative_path", * secure = true, * name = "relative_path", * type = "String", * field_types = {"image"}, * provider = "image", * deriver = "Drupal\graphql_core\Plugin\Deriver\Fields\EntityFieldPropertyDeriver" * )
Custom Types: Adding Fields to Types (Annotations) * @GraphQLField( *
id = "image_relative_path", * secure = true, * name = "relative_path", * type = "String", * field_types = {"image"}, * provider = "image", * deriver = "Drupal\graphql_core\Plugin\Deriver\Fields\EntityFieldPropertyDeriver" * )
Custom Types: Adding Fields to Types (Annotations) * @GraphQLField( *
id = "image_relative_path", * secure = true, * name = "relative_path", * type = "String", * field_types = {"image"}, * provider = "image", * deriver = "Drupal\graphql_core\Plugin\Deriver\Fields\EntityFieldPropertyDeriver" * )
Custom Types: Adding Fields to Types (Annotations) protected function resolveValues($value,
array $args, ResolveContext $context, ResolveInfo $info) { if ($value instanceof ImageItem && $value->entity->access('view')) { $url = file_create_url($value->entity->getFileUri()); $relative_path = urldecode(file_url_transform_relative($url)); $replaced_relative_path = str_replace('/sites/default/files', '', $relative_path); yield $replaced_relative_path; } }
{ nodeQuery(filter: {conditions: [{field: "type", value: "vessel", operator: EQUAL}]}) {
entities { ... on NodeVessel { entityId, title, fieldTimelineThumbnail { entity { fieldMediaImage { url, relative_path } } } } } } }
{ nodeQuery(filter: {conditions: [{field: "type", value: "vessel", operator: EQUAL}]}) {
entities { ... on NodeVessel { entityId, title, fieldTimelineThumbnail { entity { fieldMediaImage { url, relative_path } } } } } } }
{ nodeQuery(filter: {conditions: [{field: "type", value: "vessel", operator: EQUAL}]}) {
entities { ... on NodeVessel { entityId, title, fieldTimelineThumbnail { entity { fieldMediaImage { url, relative_path } } } } } } }
What about documentation?
For examples: graphql/tests/modules/graphql_plugin_test/src/Plugin/GraphQL/Fields Annotations: https://www.drupal.org/docs/8/api/plugin-api/ annotations-based-plugins
Custom Types: Settings Form
Custom Types: Settings Form •A node is in the core
entity types. But what about stuff that isn’t?
Custom Types: Settings Form •A node is in the core
entity types. But what about stuff that isn’t? •Settings example
Custom Types: Settings Form
Custom Types: Settings Form •Basic custom form for saving to
Drupal::state()
Custom Types: Settings Form <?php namespace Drupal\thf_graphql\Plugin\GraphQL\Fields; use Drupal\graphql\GraphQL\Execution\ResolveContext; use
Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase; use GraphQL\Type\Definition\ResolveInfo; use Symfony\Component\HttpFoundation\Response; /** * A JSON Payload of the custom settings object.ResolveInfo * * @GraphQLField( * id = "payload", * secure = true, * name = "Payload", * type = "string", * multi = true, * arguments = { * "settings_name" = "String" * }, * ) */ class Payload extends FieldPluginBase { /** * {@inheritdoc} */ public function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) { $settings = \Drupal::state()->get($args['settings_name'], []); yield json_encode($settings); } }
Custom Types: Settings Form /** * A JSON Payload of
the custom settings object.ResolveInfo * * @GraphQLField( * id = "payload", * secure = true, * name = "Payload", * type = "string", * multi = true, * arguments = { * "settings_name" = "String" * }, * ) */
Custom Types: Settings Form query{ Payload(settings_name:"thf_utility.gcib_settings") }
Custom Types: Settings Form { "data": { "Payload": [ "{
"text_cta": "See More", "story_cta": "Read the Story", "scroll_wheel_cta": "Continue", "lens_cta": "View More", "draw_cta": "Draw Now", "quiz_cta": "Take the Quiz" }" ] } }
Case Study
Case Study •Two products
Case Study •Two products •One Cinder touchscreen
Case Study •Two products •One Cinder touchscreen •One JS touchscreen
None
What did we learn?
What did we learn? •Empty structures/values break Cinder
What did we learn? •Empty structures/values break Cinder •Making queries
consistent is a challenge
What did we learn? •Empty structures/values break Cinder •Making queries
consistent is a challenge •Decoupling is both a technical thing as well as a philosophical thing
DEMO TIME
None
None
We’re hiring.
None
THANK YOU. Mark Llobrera Technology Director, Bluecadet Philadelphia https://speakerdeck.com/mllobrera @dirtystylus
@bluecadet