CINXE.COM

<!DOCTYPE html><html lang="en" class="dark [--scroll-mt:9.875rem] lg:[--scroll-mt:6.3125rem]"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><link rel="preconnect" href="https://HB50HVJDY8-dsn.algolia.net" crossorigin="true"/><meta name="next-head-count" content="3"/><link href="https://www.wpgraphql.com/api/feeds/feed.json" rel="alternate" type="application/feed+json" title="WPGraphQL Blog JSON Feed"/><link href="https://www.wpgraphql.com/api/feeds/rss.xml" rel="alternate" type="application/rss+xml" title="WPGraphQL Blog XML Feed"/><link href="https://www.wpgraphql.com/api/feeds/feed.atom" rel="alternate" type="application/atom+xml" title="WPGraphQL Blog Atom Feed"/><link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon.png?v=4"/><link rel="icon" type="image/png" sizes="32x32" href="/favicons/favicon-32x32.png?v=4"/><link rel="icon" type="image/png" sizes="16x16" href="/favicons/favicon-16x16.png?v=4"/><link rel="manifest" href="/favicons/site.webmanifest?v=4"/><link rel="mask-icon" href="/favicons/safari-pinned-tab.svg?v=4" color="#38bdf8"/><link rel="shortcut icon" href="/favicon.ico?v=4"/><meta name="apple-mobile-web-app-title" content="WPGraphQL"/><meta name="application-name" content="WPGraphQL"/><meta name="theme-color" content="#172A53"/><script> try { if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.documentElement.classList.add('dark') } else { document.documentElement.classList.remove('dark') } } catch (_) {} </script><link rel="preload" href="/_next/static/css/2b9b19fe04f89500.css" as="style"/><link rel="stylesheet" href="/_next/static/css/2b9b19fe04f89500.css" data-n-g=""/><noscript data-n-css=""></noscript><link rel="preload" href="/_next/static/chunks/webpack-aaf2b736bc265890.js" as="script"/><link rel="preload" href="/_next/static/chunks/framework-02398e00071ab346.js" as="script"/><link rel="preload" href="/_next/static/chunks/main-c66e5e2c08594bca.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/_app-807050be1a0fea29.js" as="script"/><link rel="preload" href="/_next/static/chunks/pages/%5B...wordpressNode%5D-8553865619f9b237.js" as="script"/></head><body class="antialiased text-slate-500 dark:text-slate-400 font-inter bg-white dark:bg-navy"><div id="__next"><header class="relative bg-white sticky top-0 z-50 dark:bg-navy border-b-2 border-b-gray-100 dark:border-b-navy" data-headlessui-state=""><div class="max-w-8xl mx-auto flex justify-between items-center px-4 py-4 sm:px-6 md:justify-start md:space-x-10"><div class="flex justify-start lg:w-0 lg:flex-1"><a href="/"><span class="sr-only">WPGraphQL</span><div class="relative h-full w-auto sm:h-10"><span style="box-sizing:border-box;display:inline-block;overflow:hidden;width:initial;height:initial;background:none;opacity:1;border:0;margin:0;padding:0;position:relative;max-width:100%"><span style="box-sizing:border-box;display:block;width:initial;height:initial;background:none;opacity:1;border:0;margin:0;padding:0;max-width:100%"><img style="display:block;max-width:100%;width:initial;height:initial;background:none;opacity:1;border:0;margin:0;padding:0" alt="" aria-hidden="true" src="data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20version=%271.1%27%20width=%2742%27%20height=%2742%27/%3e"/></span><img alt="WPGraphQL Logo" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" decoding="async" data-nimg="intrinsic" style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%"/><noscript><img alt="WPGraphQL Logo" srcSet="/logo-wpgraphql.svg 1x, /logo-wpgraphql.svg 2x" src="/logo-wpgraphql.svg" decoding="async" data-nimg="intrinsic" style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%" loading="lazy"/></noscript></span></div></a></div><div class="-mr-2 -my-2 md:hidden flex justify-items-end items-center"><button type="button" class="ml-auto text-slate-500 w-8 h-8 -my-1 flex items-center justify-center hover:text-slate-600 lg:hidden dark:text-slate-400 dark:hover:text-slate-300"><span class="sr-only">Search</span><svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m19 19-3.5-3.5"></path><circle cx="11" cy="11" r="6"></circle></svg></button><label class="sr-only" id="headlessui-listbox-label-:Rd4lt6:" data-headlessui-state="">Theme</label><button type="button" id="headlessui-listbox-button-:Rl4lt6:" aria-haspopup="listbox" aria-expanded="false" data-headlessui-state=""><span class="dark:hidden"><svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6"><path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" class="stroke-slate-400 dark:stroke-slate-500"></path><path d="M12 4v1M17.66 6.344l-.828.828M20.005 12.004h-1M17.66 17.664l-.828-.828M12 20.01V19M6.34 17.664l.835-.836M3.995 12.004h1.01M6 6l.835.836" class="stroke-slate-400 dark:stroke-slate-500"></path></svg></span><span class="hidden dark:inline"><svg viewBox="0 0 24 24" fill="none" class="w-6 h-6"><path fill-rule="evenodd" clip-rule="evenodd" d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z" class="fill-transparent"></path><path d="m17.715 15.15.95.316a1 1 0 0 0-1.445-1.185l.495.869ZM9 6.035l.846.534a1 1 0 0 0-1.14-1.49L9 6.035Zm8.221 8.246a5.47 5.47 0 0 1-2.72.718v2a7.47 7.47 0 0 0 3.71-.98l-.99-1.738Zm-2.72.718A5.5 5.5 0 0 1 9 9.5H7a7.5 7.5 0 0 0 7.5 7.5v-2ZM9 9.5c0-1.079.31-2.082.845-2.93L8.153 5.5A7.47 7.47 0 0 0 7 9.5h2Zm-4 3.368C5 10.089 6.815 7.75 9.292 6.99L8.706 5.08C5.397 6.094 3 9.201 3 12.867h2Zm6.042 6.136C7.718 19.003 5 16.268 5 12.867H3c0 4.48 3.588 8.136 8.042 8.136v-2Zm5.725-4.17c-.81 2.433-3.074 4.17-5.725 4.17v2c3.552 0 6.553-2.327 7.622-5.537l-1.897-.632Z" class="fill-slate-400 dark:fill-slate-500"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M17 3a1 1 0 0 1 1 1 2 2 0 0 0 2 2 1 1 0 1 1 0 2 2 2 0 0 0-2 2 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 1 1 0-2 2 2 0 0 0 2-2 1 1 0 0 1 1-1Z" class="fill-slate-400 dark:fill-slate-500"></path></svg></span></button><button class="bg-white rounded-md p-2 ml-3 inline-flex items-center justify-center text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-sky-500 dark:bg-navy" type="button" aria-expanded="false" data-headlessui-state=""><span class="sr-only">Open menu</span><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" class="h-6 w-6"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"></path></svg></button></div><nav class="hidden md:flex space-x-10"><div class="relative" data-headlessui-state=""><button class="text-gray-700 dark:text-gray-100 group bg-white dark:bg-navy rounded-md inline-flex items-center text-base font-medium hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-200" type="button" aria-expanded="false" data-headlessui-state=""><span>Docs</span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="text-gray-400 ml-2 h-5 w-5 group-hover:text-gray-500"><path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"></path></svg></button></div><div class="relative" data-headlessui-state=""><button class="text-gray-700 dark:text-gray-100 group bg-white dark:bg-navy rounded-md inline-flex items-center text-base font-medium hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-200" type="button" aria-expanded="false" data-headlessui-state=""><span>Developer Reference</span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="text-gray-400 ml-2 h-5 w-5 group-hover:text-gray-500"><path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"></path></svg></button></div><a class="text-base font-medium text-gray-700 hover:text-gray-900 dark:text-white dark:hover:text-gray-300" href="/extensions">Extensions</a><a class="text-base font-medium text-gray-700 hover:text-gray-900 dark:text-white dark:hover:text-gray-300" href="/blog">Blog</a><a class="text-base font-medium text-gray-700 hover:text-gray-900 dark:text-white dark:hover:text-gray-300" href="https://repl.wpgraphql.com/">REPL</a></nav><div hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></div><div class="hidden md:flex items-center gap-4 justify-end md:flex-1 lg:w-0"><button type="button" class="ml-auto text-slate-500 w-8 h-8 -my-1 flex items-center justify-center hover:text-slate-600 dark:text-slate-400 dark:hover:text-slate-300"><span class="sr-only">Search</span><svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m19 19-3.5-3.5"></path><circle cx="11" cy="11" r="6"></circle></svg></button><a href="https://github.com/wp-graphql/wp-graphql" class="text-gray-600 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-100" target="_blank" rel="noreferrer"><span class="sr-only">GitHub</span><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path></svg></a><a href="https://wordpress.org/plugins/wp-graphql" class="text-gray-600 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-100" target="_blank" rel="noreferrer"><span class="sr-only">WordPress Plugin</span><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M21.469 6.825c.84 1.537 1.318 3.3 1.318 5.175 0 3.979-2.156 7.456-5.363 9.325l3.295-9.527c.615-1.54.82-2.771.82-3.864 0-.405-.026-.78-.07-1.11m-7.981.105c.647-.03 1.232-.105 1.232-.105.582-.075.514-.93-.067-.899 0 0-1.755.135-2.88.135-1.064 0-2.85-.15-2.85-.15-.585-.03-.661.855-.075.885 0 0 .54.061 1.125.09l1.68 4.605-2.37 7.08L5.354 6.9c.649-.03 1.234-.1 1.234-.1.585-.075.516-.93-.065-.896 0 0-1.746.138-2.874.138-.2 0-.438-.008-.69-.015C4.911 3.15 8.235 1.215 12 1.215c2.809 0 5.365 1.072 7.286 2.833-.046-.003-.091-.009-.141-.009-1.06 0-1.812.923-1.812 1.914 0 .89.513 1.643 1.06 2.531.411.72.89 1.643.89 2.977 0 .915-.354 1.994-.821 3.479l-1.075 3.585-3.9-11.61.001.014zM12 22.784c-1.059 0-2.081-.153-3.048-.437l3.237-9.406 3.315 9.087c.024.053.05.101.078.149-1.12.393-2.325.609-3.582.609M1.211 12c0-1.564.336-3.05.935-4.39L7.29 21.709C3.694 19.96 1.212 16.271 1.211 12M12 0C5.385 0 0 5.385 0 12s5.385 12 12 12 12-5.385 12-12S18.615 0 12 0"></path></svg></a><label class="sr-only" id="headlessui-listbox-label-:Rdolt6:" data-headlessui-state="">Theme</label><button type="button" id="headlessui-listbox-button-:Rlolt6:" aria-haspopup="listbox" aria-expanded="false" data-headlessui-state=""><span class="dark:hidden"><svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-6 h-6"><path d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" class="stroke-slate-400 dark:stroke-slate-500"></path><path d="M12 4v1M17.66 6.344l-.828.828M20.005 12.004h-1M17.66 17.664l-.828-.828M12 20.01V19M6.34 17.664l.835-.836M3.995 12.004h1.01M6 6l.835.836" class="stroke-slate-400 dark:stroke-slate-500"></path></svg></span><span class="hidden dark:inline"><svg viewBox="0 0 24 24" fill="none" class="w-6 h-6"><path fill-rule="evenodd" clip-rule="evenodd" d="M17.715 15.15A6.5 6.5 0 0 1 9 6.035C6.106 6.922 4 9.645 4 12.867c0 3.94 3.153 7.136 7.042 7.136 3.101 0 5.734-2.032 6.673-4.853Z" class="fill-transparent"></path><path d="m17.715 15.15.95.316a1 1 0 0 0-1.445-1.185l.495.869ZM9 6.035l.846.534a1 1 0 0 0-1.14-1.49L9 6.035Zm8.221 8.246a5.47 5.47 0 0 1-2.72.718v2a7.47 7.47 0 0 0 3.71-.98l-.99-1.738Zm-2.72.718A5.5 5.5 0 0 1 9 9.5H7a7.5 7.5 0 0 0 7.5 7.5v-2ZM9 9.5c0-1.079.31-2.082.845-2.93L8.153 5.5A7.47 7.47 0 0 0 7 9.5h2Zm-4 3.368C5 10.089 6.815 7.75 9.292 6.99L8.706 5.08C5.397 6.094 3 9.201 3 12.867h2Zm6.042 6.136C7.718 19.003 5 16.268 5 12.867H3c0 4.48 3.588 8.136 8.042 8.136v-2Zm5.725-4.17c-.81 2.433-3.074 4.17-5.725 4.17v2c3.552 0 6.553-2.327 7.622-5.537l-1.897-.632Z" class="fill-slate-400 dark:fill-slate-500"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M17 3a1 1 0 0 1 1 1 2 2 0 0 0 2 2 1 1 0 1 1 0 2 2 2 0 0 0-2 2 1 1 0 1 1-2 0 2 2 0 0 0-2-2 1 1 0 1 1 0-2 2 2 0 0 0 2-2 1 1 0 0 1 1-1Z" class="fill-slate-400 dark:fill-slate-500"></path></svg></span></button></div></div></header><div hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></div><div class="overflow-hidden"><div class="mx-auto mt-10 px-4 pb-6 sm:mt-16 sm:px-6 md:px-8 xl:px-12 xl:max-w-6xl"><main class="content"><article class="relative pt-10 max-w-3xl mx-auto"><header><dl><dt class="sr-only">Date</dt><dd class="absolute top-0 inset-x-0 text-navy text-center dark:text-slate-400"><time dateTime="2021-03-09T14:31:03">Tuesday, March 9, 2021</time></dd></dl><div class="space-y-6"><h1 class="col-span-full break-words text-3xl sm:text-4xl text-center xl:mb-5 font-extrabold tracking-tight text-navy dark:text-slate-200">Gutenberg and Decoupled Applications</h1><div class="flex flex-wrap justify-center"><a class="text-base font-semibold tracking-wider text-purple-600 dark:text-purple-400 px-3" href="/category/gutenberg">Gutenberg</a><a class="text-base font-semibold tracking-wider text-purple-600 dark:text-purple-400 px-3" href="/category/wordpress">WordPress</a></div></div><div class="flex justify-center my-8"><dl><div class="justify-center"><dt class="sr-only">Author</dt><dd class="flex justify-center font-medium mt-6 mx-3"><span style="box-sizing:border-box;display:inline-block;overflow:hidden;width:initial;height:initial;background:none;opacity:1;border:0;margin:0;padding:0;position:relative;max-width:100%"><span style="box-sizing:border-box;display:block;width:initial;height:initial;background:none;opacity:1;border:0;margin:0;padding:0;max-width:100%"><img style="display:block;max-width:100%;width:initial;height:initial;background:none;opacity:1;border:0;margin:0;padding:0" alt="" aria-hidden="true" src="data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20version=%271.1%27%20width=%2750%27%20height=%2750%27/%3e"/></span><img alt="Jason Bahl" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" decoding="async" data-nimg="intrinsic" class="mr-3 w-10 h-10 rounded-full bg-slate-50 dark:bg-slate-800" style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%"/><noscript><img alt="Jason Bahl" srcSet="/_next/image?url=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F94bf4ea789246f76c48bcf8509bcf01e%3Fs%3D96%26d%3Dmm%26r%3Dg&amp;w=64&amp;q=75 1x, /_next/image?url=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F94bf4ea789246f76c48bcf8509bcf01e%3Fs%3D96%26d%3Dmm%26r%3Dg&amp;w=128&amp;q=75 2x" src="/_next/image?url=https%3A%2F%2Fsecure.gravatar.com%2Favatar%2F94bf4ea789246f76c48bcf8509bcf01e%3Fs%3D96%26d%3Dmm%26r%3Dg&amp;w=128&amp;q=75" decoding="async" data-nimg="intrinsic" style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%" class="mr-3 w-10 h-10 rounded-full bg-slate-50 dark:bg-slate-800" loading="lazy"/></noscript></span></dd><dd class="text-center items-center"><a class="text-sky-500 dark:text-sky-300 dark:hover:text-sky-400 hover:text-sky-600 dark:text-sky-400 pt-5 text-center" href="/author/jasonbahl">Jason Bahl</a></dd></div></dl></div></header><div class="mx-auto py-12 px-4 max-w-7xl sm:px-6 lg:px-8 lg:py-12"><div id="content" class="prose dark:prose-dark"> <p>In this article I want to dive into the current state of Gutenberg and WPGraphQL. </p> <p>This is a technical article about using Gutenberg blocks in the context of decoupled / headless / API-driven WordPress, and makes the assumption that you already know what Gutenberg is and have some general understanding of how it works.</p> <h2 class="wp-block-heading">TL;DR</h2> <p>Client-server contracts around the shape of data is fundamental to achieving &#8220;separation of concerns&#8221;, a pillar of modular and decoupled application development. </p> <p>While much of WordPress was built with <a href="#wordpress-contracts" data-type="internal" data-id="#wordpress-contracts">decoupling in mind</a>, the <a href="#the-wp-rest-api-provides-a-schema">WP REST API</a> and <a href="#the-gutenberg-block-registry">Gutenberg were not</a>.</p> <p>As a result, decoupled application developers interacting with WordPress are <a href="#challenges-and-the-current-state-of-gutenberg">limited in what they can achieve</a>.</p> <p> With the <a href="https://wpengine.com/headless-cms-research/">growing demand for headless WordPress</a>, this is a key limitation that will hamper growth.</p> <p>Fortunately, even with <a href="#challenges-and-the-current-state-of-gutenberg">the limitations</a>, there are ways forward. In this article <a href="#three-approaches-to-using-gutenberg-in-decoupled-applications-today">I walk through 3 approaches</a> you can implement to use Gutenberg in decoupled applications today, tradeoffs included, and <a href="#whats-next">propose a plan</a> to make the future of Gutenberg for decoupled applications a better one.</p> <h2 class="wp-block-heading" id="replacing-my-door-lock">Replacing my door lock</h2> <p>I recently replaced the lock on the front door of my house.</p> <p>I ordered the lock from an online retailer. I was able to select a specific brand of lock in a specific color. </p> <p>When the lock arrived and I opened the package, it was the same brand and color that I ordered. It wasn&#8217;t just any random lock, it was the one that I agreed to pay for, and the online retailer agreed to mail me. </p> <p>I was able install the lock without any surprises. I didn&#8217;t have to drill any new holes in my door. The new lock fit the hole in my door that I removed the old lock from. </p> <p>The new lock wasn&#8217;t made by the same manufacturer that made the door, and yet, the lock installed on my door just fine. In fact, there were <em>at least</em> 30 different locks from a variety of manufacturers that I could have selected that all would have worked in my door without any complications. </p> <h2 class="wp-block-heading">Decoupled systems</h2> <p>This wasn&#8217;t <em>really</em> a story about doors and locks. It&#8217;s a story about decoupled systems, and the contracts, or agreements, that make them work. </p> <p>And its intent is to help frame what I&#8217;m talking about with using WordPress, and specifically Gutenberg, in decoupled contexts. </p> <p>In order for decoupled systems to work well, whether it&#8217;s doors and door locks, or WordPress and a decoupled JavaScript application, there needs to be some sort of agreement between the different parts of the system.</p> <p>In the case of door and lock manufacturers, it&#8217;s an agreement over the size and positioning of the holes in the door.</p> <div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-1024x890.jpg" alt="" class="wp-image-8165" width="512" height="445" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-1024x890.jpg 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-300x261.jpg 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-768x668.jpg 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram.jpg 1200w" sizes="auto, (max-width: 512px) 100vw, 512px" /><figcaption>Diagram showing measurements for a door lock hole </figcaption></figure></div> <p>Door manufacturers can build doors at their leisure and lock manufacturers at theirs, and when the time comes to bring them together, they work without issue because both parties are adhering to an agreement.</p> <p>In the case of e-commerce, there are agreements about what a consumer purchases and what should be delivered. In my case, the online store provided a list of locks that were available to purchase. I selected a specific lock, paid for it, and in response I received the lock we agreed to, in exchange for my payment. </p> <h3 class="wp-block-heading">Decoupled tech, decoupled teams</h3> <p>When WPGraphQL first started, I was working at a newspaper that had a CMS team that focused on WordPress, a Native Mobile team that focused on the iOS and Android applications, a Data Warehouse team that collected various data from the organization and a Print team that took the data from WordPress and prepared it for Print.</p> <p>WordPress was the entry point for content creators to write content, but the web was only one of many channels where content was being used. </p> <p>Not only was the technology decoupled (PHP for the CMS, React Native for mobile apps, Python for Data warehousing and some legacy system I forget the name of for print), but the teams were also decoupled.</p> <p>The only team that really needed to understand WordPress was the CMS team. The other teams were able to use WPGraphQL Schema Introspection to build tools for their teams using data from WordPress, without needing to understand anything about PHP or WordPress under the hood. </p> <p>Much like door and lock manufacturers don&#8217;t need to be experts at what the other is building, WPGraphQL&#8217;s schema served as the contract, enabling many different teams to use WordPress data when, and how, they needed. </p> <p>WPGraphQL served as the contract between the CMS team and the other teams as well as WordPress the system and the other team&#8217;s decoupled systems.</p> <h2 class="wp-block-heading">WordPress contracts</h2> <p>For WordPress, one of the common contracts, or agreements established between multiple systems (such as plugins, themes, and WordPress core) comes in the form of registries.</p> <p>WordPress has registries for Post Types, Taxonomies, Settings, Meta and more. </p> <p>The <a href="https://developer.wordpress.org/reference/functions/register_post_type/">register_post_type</a> function has more than 30 options that can be configured to define the contract between the Post Type existing and how WordPress core and decoupled systems (namely plugins and themes) should interact with it. </p> <p>The <a href="https://www.google.com/url?client=internal-element-cse&amp;cx=012566942813864066925:bnbfebp99hs&amp;q=https://developer.wordpress.org/reference/functions/register_taxonomy/&amp;sa=U&amp;ved=2ahUKEwi0o7DeqZXvAhXaHc0KHTbtBe0QFjAAegQIABAB&amp;usg=AOvVaw2n2TUd-jKjirfxq2j5y_Wm">register_taxonomy</a>, <a href="https://developer.wordpress.org/reference/functions/register_meta/">register_meta</a>, <a href="https://developer.wordpress.org/reference/functions/register_setting/">register_setting</a>, <a href="https://developer.wordpress.org/reference/functions/register_sidebar/">register_sidebar</a> and other <a href="https://wordpress.org/search/register_?in=developer_documentation">register_*</a> functions in WordPress serve a similar purpose. They allow for a contract to be established so that many different systems can work with WordPress in an agreed upon way.</p> <p>These registries serve as a contract between WordPress core and decoupled systems (themes and plugins) that can work with WordPress. Because these registries establish an agreement with how WordPress core will behave, plugins and themes can latch onto these registries and extend WordPress core in some powerful ways.</p> <p>The decoupled (pluggable) architecture of WordPress is enabled by these contracts. </p> <div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM.png" alt="Image showing WordPress in the middle with the logos for ElasticPress, WordPress SEO by Yoast, WPGraphQL and Advanced Custom Fields around it." class="wp-image-8171" width="600" height="528" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM.png 857w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM-300x264.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM-768x676.png 768w" sizes="auto, (max-width: 600px) 100vw, 600px" /><figcaption>WordPress registries enable plugins to iterate outside of WordPress core</figcaption></figure></div> <p>Registering a new post type to WordPress can get you a UI in the WordPress dashboard, but it can also get your content indexed to Elastic Search via <a href="https://wordpress.org/plugins/elasticpress/">ElasticPress</a>, powerful SEO tools from <a href="https://wordpress.org/plugins/wordpress-seo/">WordPress SEO</a>, custom admin functionality from <a href="https://wordpress.org/plugins/advanced-custom-fields/">Advanced Custom Fields</a>, and API access via <a href="https://wordpress.org/plugins/wp-graphql/">WPGraphQL</a>.</p> <p>If the next release of WordPress started hiding the UI for all post types that were registered with <code>show_ui =&gt; true</code>, or stopped allowing plugins from reading the post type registry, there would likely be a bug (or hundreds) reported on <a href="https://core.trac.wordpress.org/">Trac</a>, (and Twitter, and Slack, etc), as that would mean WordPress was breaking the established contract. </p> <h2 class="wp-block-heading">The client/server contract</h2> <p>Like we discussed earlier, decoupled systems need some sort of shared agreement in order to work well together. It doesn&#8217;t have to be a GraphQL API, but it has to be <em>something.</em><br><br>For WordPress, this comes in the form of APIs. </p> <p>WordPress core has 2 built-in APIs that enable decoupled applications to interact with WordPress data, <a href="https://codex.wordpress.org/XML-RPC_WordPress_API">XML-RPC</a> and the <a href="https://developer.wordpress.org/rest-api/">WP REST API</a>. </p> <p>And, of course, there&#8217;s yours truly, <a href="https://wordpress.org/plugins/wp-graphql/">WPGraphQL</a>, a free open-source WordPress plugin that provides an extendable GraphQL schema and API for any WordPress site.</p> <figure class="wp-block-image size-large is-resized"><img loading="lazy" decoding="async" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM.png" alt="Blocks representing REST, GraphQL and RPC API on top of a block representing the Authorization and Business logic layers of WordPress, and at the bottom is a block representing the Persistence Layer (MySQL)." class="wp-image-8176" width="537" height="493" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM.png 592w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM-300x276.png 300w" sizes="auto, (max-width: 537px) 100vw, 537px" /><figcaption>Diagram of the WordPress server + API setup</figcaption></figure> <p>In order for decoupled applications, such as <a href="https://www.gatsbyjs.com/plugins/gatsby-source-wordpress/">Gatsby</a>, <a href="https://github.com/WebDevStudios/nextjs-wordpress-starter">NextJS</a>, <a href="https://frontity.org/">Frontity</a>, native mobile applications or others, to work with WordPress, the APIs must establish a contract that WordPress and the decoupled application can both work against.</p> <h3 class="wp-block-heading">The WP REST API provides a Schema</h3> <p>The WordPress REST API provides a Schema that acts as this contract. The Schema is introspect-able, allowing remote systems to see what&#8217;s available before asking for the data. </p> <p>This is a good thing! </p> <h3 class="wp-block-heading">But the Schema is not enforced</h3> <p>However, the WP REST API doesn&#8217;t <em>enforce</em> the Schema. </p> <p>WordPress plugins that extend the WP REST API Schema can add fields to the API without defining what data will be returned in the REST API Schema. Or, they can register fields that return &#8220;object&#8221; as a wildcard catch-all. </p> <p>This is a bad thing! </p> <p>Decoupled teams and applications cannot reliably use the WordPress REST API if it doesn&#8217;t enforce any type of contract. </p> <h4 class="wp-block-heading">Optional Schema and wildcard return types</h4> <p>Plugins such as the <a href="https://wordpress.org/plugins/acf-to-rest-api/">Advanced Custom Fields to REST API</a> add a single &#8220;acf&#8221; field to the REST endpoints and declare in the WP REST API that the field will return &#8220;an object&#8221;. </p> <p>We can see this if we introspect the WP REST API of a WordPress install with this plugin active: </p> <figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="459" height="456" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection.png" alt="" class="wp-image-8107" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection.png 459w, https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection-300x298.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection-150x150.png 150w" sizes="auto, (max-width: 459px) 100vw, 459px" /><figcaption>Screenshot showing the Introspection of the ACF to REST API Schema definition</figcaption></figure> <p>This means that decoupled applications, and the teams building them, have no way to predict what data this field will ever return. This also means that even if a decoupled application does manage to get built, it could break at any time, because there&#8217;s no contract agreed to between the client and the server. The WordPress server can return <em>anything</em> at <em>anytime</em>.</p> <h4 class="wp-block-heading">Unpredictable data is frustrating for API consumers</h4> <p>With the field defined as &#8220;object&#8221; the data returned can be different from page to page, post to post, user to user, and so on. There&#8217;s no predictable way decoupled application developers can prepare for the data the API will return. </p> <p>This would be like me trying to purchase that door lock, but instead of the website showing me a list of door locks with specific colors to chose from, I was just given one &#8220;product&#8221; as the option to purchase.</p> <p>The &#8220;product&#8221; might be a hat or some new sunglasses, or if I&#8217;m really lucky, it might be a door lock. I don&#8217;t have any way of knowing what the &#8220;product&#8221; is, until I receive it. </p> <p>As an e-commerce consumer, this is not helpful. </p> <p>And as a decoupled application developer, this type of API is frustrating.</p> <p>Decoupled systems don&#8217;t work well if part of the equation is to &#8220;just guess&#8221;.</p> <h3 class="wp-block-heading">GraphQL enforces Schema and Strong Types</h3> <p>WPGraphQL, on the other hand, <em>enforces</em> a strongly Typed Schema. There is no option to extend the WPGraphQL API without describing the type of data the API will return. Additionally, there is no &#8220;wildcard&#8221; type. </p> <p>A plugin cannot register a field to the WPGraphQL Schema that returns a door lock on one request, and sunglasses or a hat on the next request.</p> <p>To extend WPGraphQL, plugins must register fields that declare a <em>specific</em> Type of data that will be returned. And this contract must be upheld. </p> <p>This removes the &#8220;just guess&#8221; part of the equation. </p> <p>Decoupled application developers always know what to expect.</p> <p>Much like I, as an e-commerce consumer, was able to browse the list of door locks that were possible to purchase on the online store, decoupled application developers can use a tool such as GraphiQL to browse the GraphQL Schema and see what Types and Fields are available to query from the GraphQL API. </p> <p>The screenshot below shows GraphiQL being used to explore a GraphQL Schema. The screenshot shows the type named &#8220;Post&#8221; in the GraphQL Schema with a field named &#8220;slug&#8221; which declares that it will return a String.</p> <figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="356" height="176" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM.png" alt="" class="wp-image-8087" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM.png 356w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM-300x148.png 300w" sizes="auto, (max-width: 356px) 100vw, 356px" /><figcaption>Screenshot of GraphiQL showing the &#8220;slug&#8221; field on the &#8220;Post&#8221; type.</figcaption></figure> <p>Application developers can take the information they get from the Schema and construct queries that are now predictable.</p> <p>And the GraphQL Schema serves as the contract between the server and the client application, ensuring that the server will return the data in the same shape the client was promised.</p> <p>Just like I received the specific door lock matching the brand and color that I specified in my order, client applications can specify the Types and Fields they require with a GraphQL Query, and the response will match what was asked for. </p> <p>In the example below, the GraphQL Query asks for a Post and the &#8220;slug&#8221; field, which we can see in the Schema that it will return a String. And in response to this query, the GraphQL server will provide just what was asked for. </p> <p>The &#8220;just guess&#8221; part of the server/client equation is eliminated.</p> <h3 class="wp-block-heading">Example GraphQL Query &amp; Response</h3> <pre class="wp-block-code lang-graphql"><code>query { post( id: 1, idType: DATABASE_ID ) { slug } }</code></pre> <pre class="wp-block-code lang-json"><code>{ "data": { "post": { "slug": "hello-world", }, }, }</code></pre> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1010" height="289" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM.png" alt="" class="wp-image-8147" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM.png 1010w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM-300x86.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM-768x220.png 768w" sizes="auto, (max-width: 1010px) 100vw, 1010px" /><figcaption>Screenshot showing a GraphQL Query and Response in the GraphiQL IDE</figcaption></figure></div> <h2 class="wp-block-heading">The Gutenberg block registry</h2> <p>Now that we&#8217;re on the same page about contracts between decoupled systems and how WPGraphQL provides a contract between the WordPress server and client applications, let&#8217;s move on to discuss Gutenberg more specifically. </p> <h3 class="wp-block-heading">Early integration with WPGraphQL</h3> <p>Gutenberg as a concept was fascinating to me early on. Like many others, I saw the potential for this block-based editor to impact WordPress users and the WordPress ecosystem greatly, WPGraphQL included.</p> <p>I explored exposing Gutenberg blocks as queryable data in WPGraphQL as far back as June 2017: </p> <figure class="wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter"><div class="wp-block-embed__wrapper"> <blockquote class="twitter-tweet" data-width="550" data-dnt="true"><p lang="en" dir="ltr">Turns out <a href="https://twitter.com/wpgraphql?ref_src=twsrc%5Etfw">@wpgraphql</a> plays nice with the new Gutenberg editor. <a href="https://twitter.com/hashtag/WordPress?src=hash&amp;ref_src=twsrc%5Etfw">#WordPress</a> <a href="https://twitter.com/hashtag/graphql?src=hash&amp;ref_src=twsrc%5Etfw">#graphql</a> <a href="https://t.co/0Vlav4w75k">pic.twitter.com/0Vlav4w75k</a></p>&mdash; GraphQL for WordPress (@wpgraphql) <a href="https://twitter.com/wpgraphql/status/878364222253481984?ref_src=twsrc%5Etfw">June 23, 2017</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </div></figure> <h3 class="wp-block-heading">Challenges and the current state of Gutenberg</h3> <p>While a basic initial integration was straightforward, I ran into roadblocks quickly. </p> <p>Gutenberg didn&#8217;t have a server-side registry for blocks. At this time, all blocks in Gutenberg were fully registered in JavaScript, which is not executed or understood by the WordPress server. </p> <p>This means that unlike Post Types, Taxonomies, Meta, Sidebars, Settings, and other constructs that make up WordPress, Gutenberg blocks don&#8217;t adhere to any type of contract with the WordPress server. </p> <p>This means that the WordPress server knows nothing about blocks. There are no agreements between Gutenberg blocks and other systems in WordPress, or systems trying to interact with WordPress via APIs. </p> <p>Blocks were practically non-existent as far as the application layer of WordPress was concerned. </p> <p>There were no WP-CLI commands to create, update, delete or list blocks. No WP REST API Schema or endpoints for blocks. No XML-RPC methods for blocks. <em>And no way to expose blocks to WPGraphQL</em>.</p> <p>Without any kind of agreement between the WordPress server and the Gutenberg JavaScript application, the WordPress server can&#8217;t interact with blocks in meaningful ways. </p> <p>For example, the WordPress server cannot validate user input on Gutenberg blocks. Data that users input into the fields in Gutenberg blocks is trusted without question and saved to the database without the server having final say. This is a dangerous precedent, especially as Gutenberg is moving outside of editing Post content and into other parts of full-site editing. As far as I know, the lack of block input validation by the WordPress server is still a problem today.</p> <p>Anyway, without the WordPress server having any knowledge of blocks, WPGraphQL also could not provide a meaningful integration with Gutenberg. </p> <p>I was sad, because I was optimistic that this integration could lead to some really great innovations for decoupled applications. </p> <p>Shortly after my tweet above and running into roadblocks, I raised these concerns with the Gutenberg team on Twitter and Slack. The Gutenberg team asked me to post my thoughts in a Gutenberg Github issue, <a href="https://github.com/nas/content/live/wpgqlcontent/gutenberg/issues/2751#issuecomment-330930741">which I did at length</a>. While my comments received a lot of positive emoji reactions from the community. Unfortunately the issue has been closed with many of the concerns outstanding.</p> <p>Months later I also <a href="https://make.wordpress.org/mobile/2018/03/21/gutenberg-on-mobile/#comment-19383">voiced similar concerns</a> on the Make WordPress post about Gutenberg and Mobile, pointing out that without a proper server registry and API, decoupled applications, such as the WordPress native mobile application, won&#8217;t be able to support Custom Blocks, or even per-site adjustments to core blocks. </p> <p>As of today, my understanding is that the WordPress native mobile applications still do not support custom blocks or adjustments to core blocks, making the App nearly useless for sites that have adopted Gutenberg.</p> <p>Even with the limitations of Gutenberg, the headless WordPress community has been determined to use Gutenberg with decoupled applications.</p> <h2 class="wp-block-heading">Three approaches to using Gutenberg in decoupled applications, today</h2> <p>Below are some of the different approaches, including tradeoffs, that you can implement today to start using Gutenberg in decoupled applications.</p> <h3 class="wp-block-heading">Gutenberg blocks as HTML</h3> <p>I believe the <em>fastest</em> way to get started using Gutenberg in decoupled applications today, is to query the &#8220;content&#8221; field from WPGraphQL (or the WP REST API, if it&#8217;s still your flavor).</p> <p>This is the approach that <a href="https://frontity.org/blog/connecting-gutenberg-and-frontity/">Frontity is using</a>.<br><br>This is also the approach I&#8217;m using for WPGraphQL.com, which is in use on this very blog post you&#8217;re reading right now. </p> <p>This post is written in Gutenberg, queried by Gatsby using WPGraphQL, and rendered using React components!<br><br>Here&#8217;s how it works (and please don&#8217;t judge my hacky JavaScript skills ????): </p> <ul class="wp-block-list"><li>The GraphQL Query in Gatsby gets the content (<a href="https://github.com/wp-graphql/wpgraphql.com/blob/master/src/templates/WpDocument.js#L86-L88">see the code</a>)</li><li>The content is passed through a parser (<a href="https://github.com/wp-graphql/wpgraphql.com/blob/master/src/templates/WpDocument.js#L56">see the code</a>)</li><li>The parser converts standard HTML elements into the <a href="https://chakra-ui.com/">Chakra UI</a> equivalent to play nice with theming (<a href="https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parse-html.js#L98-L103">see the code</a>)</li><li>The parser also converts things like HTML for Twitter embeds, and `&lt;code&gt;` blocks into React components (<a href="https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parsed-components/index.js#L146-L167">see the code</a>)<ul><li><a href="https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parsed-components/index.js#L94-L101">This is how</a> we get neat things like the Syntax highlighting and &#8220;copy&#8221; button on the code snippets</li></ul></li></ul> <h4 class="wp-block-heading">Tradeoff: Lack of Introspection, unpredictable data</h4> <p>While this is working for me and WPGraphQL.com, I can&#8217;t recommend it for everyone.</p> <p>Using HTML as the API defeats much of the purpose of decoupled systems. In order to use the markup as an API, the developers of the decoupled application need to be able to predict all the markup that might come out of the editor. </p> <p>Querying HTML and trying to predict all the markup to parse is like me ordering &#8220;product&#8221; at the store. At any time I (or other users of WordPress) could add blocks with markup that my parser doesn&#8217;t recognize and the consuming application might not work as intended. </p> <h4 class="wp-block-heading" id="block-4c1b3d70-cbce-4b4c-9d03-1019c7014449">Tradeoff: Missing data</h4> <p>Content creators can modify attributes of blocks, and Gutenberg saves these attributes as HTML comments in the post_content. But when the content is prepared for public use in WordPress themes, the WP REST API or WPGraphQL, the raw block attributes are not available, so a parser like the one I described will not have all the block data to work with. </p> <h4 class="wp-block-heading" id="block-ebd15260-ea14-49b4-9d6f-c1e78cdcd1b3">Tradeoff: Undefined Types</h4> <p>To overcome the &#8220;missing data&#8221; issue, it&#8217;s possible to pass attributes from Gutenberg blocks as HTML data-attributes in the <a href="https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/creating-dynamic-blocks/">render_callback</a> for blocks, as a way to get Gutenberg attributes passed from the editor to the rendered HTML and available for a parser to use, but even doing this leads to client applications not knowing what to expect, and leads to undefined Types as all data-attributes are strings, so mapping data-attributes to something like a React or Vue component is difficult and fragile with this method.</p> <h4 class="wp-block-heading">When to use</h4> <p>This approach works for me, because I personally control both sides of wpgraphql.com, what blocks are available in the WordPress install, what content is published, and the Gatsby consumer application that queries the content and renders the site. In the e-commerce analogy, I&#8217;m both the person ordering the &#8220;product&#8221; <em>and</em> the person fulfilling the order, so there are no surprises. I&#8217;m not working with different teams, or even different team members, and I&#8217;m the primary content creator.</p> <p>For projects that have multiple team members, multiple authors, multiple teams and/or multi-channel distribution of content, I would not recommend this approach. And multi-team, I would argue, includes the team that builds the project, <em>and</em> the team that maintains it after it&#8217;s live, which in many agencies are different teams. </p> <h3 class="wp-block-heading">Gutenberg Object Plugin</h3> <p>In late 2018, <a href="https://twitter.com/royboy789" data-type="URL" data-id="https://twitter.com/royboy789">Roy Sivan</a>, a Senior JavaScript Engineer and recurring <a href="https://twitter.com/search?q=%40royboy789%20happy%20birthday%20%40benUNC&amp;src=typed_query">Happy Birthday wisher to Ben Meredith</a>, released a plugin that exposed Gutenberg blocks to the WP REST API: </p> <figure class="wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter"><div class="wp-block-embed__wrapper"> <blockquote class="twitter-tweet" data-width="550" data-dnt="true"><p lang="en" dir="ltr">Yup, a plugin I made that takes <a href="https://twitter.com/hashtag/Gutenberg?src=hash&amp;ref_src=twsrc%5Etfw">#Gutenberg</a> data and stores the data elegantly into the DB which is accessible via REST API directly or via normal /posts/ endpoint. <br><br>Been coming in handy for this headless React App. <a href="https://t.co/16pAAZoM4g">https://t.co/16pAAZoM4g</a></p>&mdash; 🟣☮ Roy (@royboy789) <a href="https://twitter.com/royboy789/status/1047542119252938752?ref_src=twsrc%5Etfw">October 3, 2018</a></blockquote><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </div></figure> <p>This plugin exposes Gutenberg block data to the WP REST API so that data saved to pages can be consumed as JSON objects. </p> <p>Exposing Gutenberg data as JSON is what a lot of developers building decoupled applications want. They want to take the data in a structured form, and pass the data to React / Vue / Native components. This plugin gets things headed in the right direction! </p> <h4 class="wp-block-heading">Tradeoff: Lack of Introspection, unpredictable data</h4> <p>But, because of the lack of a server-side registry for Gutenberg blocks, and the non-enforced Schema of the WP REST API, this <a href="https://github.com/royboy789/gutenberg-object-plugin">plugin</a> also suffers from the &#8220;just guess&#8221; pattern for decoupled applications.</p> <p>This plugin is unable to register blocks or fields to the WP REST API, so inspecting the Schema leaves decoupled application developers guessing. </p> <p>If we Introspect the REST API Schema from this plugin and we can see that the Schema doesn&#8217;t provide any information to the decoupled application developer about what to expect.</p> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="457" height="368" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM.png" alt="" class="wp-image-8134" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM.png 457w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM-300x242.png 300w" sizes="auto, (max-width: 457px) 100vw, 457px" /><figcaption>Screenshot of the introspection of the Gutenberg Object Plugin REST endpoint</figcaption></figure></div> <p>It&#8217;s like ordering a &#8220;product&#8221; from an e-commerce store. The endpoint can return anything at any time, and can change from page to page, request to request. </p> <p>There&#8217;s no contract between the REST endpoints and the consumer application. There&#8217;s no scalable way for decoupled application developers to know what type of data the endpoints will return, now or in the future.</p> <h4 class="wp-block-heading">Tradeoff: Only available in REST</h4> <p>If you&#8217;re building headless applications using WPGraphQL, taking advantages of <a href="https://www.wpgraphql.com/docs/wpgraphql-vs-wp-rest-api/" data-type="docs" data-id="864">features that differentiate WPGraphQL from REST,</a> you would not be able to use the GraphQL Objects plugin in your decoupled application without enduring additional pain points, in addition to the lack of introspection.</p> <p>Caching clients such as <a href="https://www.apollographql.com/docs/react/">Apollo</a> would have to be customized to work with data from these endpoints, and still may not work well with the rest of the application that might be using GraphQL. Additionally, when using REST endpoints with related resources, it becomes the clients responsibility to determine how to map the various block endpoint data to the components that need the data. There&#8217;s no concept of coupling <a href="https://www.wpgraphql.com/docs/intro-to-graphql/#fragments">GraphQL Query Fragments</a> with Components, like you can do with GraphQL.</p> <h4 class="wp-block-heading">When to use:</h4> <p>Again, if you are the developer controlling both sides, the WordPress server and the client application, this approach could work, at least while you&#8217;re building the application and the capabilities are fresh in your mind. But in general, this approach can cause some pain points that that might be difficult to identify and fix when things go wrong. For example, 6 months down the road, even the person that built the application will likely forget all the details, and when there&#8217;s a bug, and no contract between the applications to refer to, it can be hard to diagnose and fix.</p> <p>Even when things break with GraphQL applications (and they do), the explicit nature of GraphQL Queries serve as a &#8220;documentation of intent&#8221; from the original application developer and can make it much easier for teams to diagnose, down to specific leaf fields, what is broken. </p> <h3 class="wp-block-heading">WPGraphQL for Gutenberg</h3> <p>In early 2019 <a href="https://twitter.com/pristas_peter">Peter Pristas</a> introduced the <a href="https://github.com/pristas-peter/wp-graphql-gutenberg">WPGraphQL for Gutenberg</a> plugin. </p> <p>The intent of this plugin is to expose Gutenberg blocks to the WPGraphQL Schema, so that decoupled application developers could use tools such as GraphiQL to inspect what blocks were possible to be queried from the API, and compose GraphQL Queries asking for specific fields of specific blocks that they wanted to support. </p> <p>Now, content creators can publish content with Gutenberg, and decoupled application developers can introspect the Schema and construct queries asking for the specific blocks and fields their application supports.</p> <p>Decoupled application developers can move at their own pace, independent from the developers and content creators working on the CMS. The decoupled application can specify which blocks are supported, and ask for the exact fields they need. Much like an e-commerce consumer can specify the specific color door lock they want to order from the store! The Schema serves as the contract between the server and the client. Clients can predictably ask for what they want, and get just that in response. </p> <h4 class="wp-block-heading">Creating a page</h4> <p>Content creators can use Gutenberg to create pages. In the example blow, we see a page in Gutenberg with a Paragraph block and an Image block. </p> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="468" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1024x468.png" alt="Screenshot showing the Gutenberg editor with a paragraph and image block." class="wp-image-8128" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1024x468.png 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-300x137.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-768x351.png 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1536x702.png 1536w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1568x717.png 1568w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM.png 1918w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Screenshot showing the Gutenberg editor with a paragraph and image block.</figcaption></figure></div> <h4 class="wp-block-heading">Exploring the Schema</h4> <p>With the plugin installed and activated (for demo sake I have <a href="https://github.com/wp-graphql/wp-graphql/releases/tag/v1.2.5">WPGraphQL v1.2.5</a> and <a href="https://github.com/pristas-peter/wp-graphql-gutenberg/releases/tag/v0.3.8">WPGraphQL for Gutenberg v0.3.8</a> active), decoupled application developers can use GraphiQL to browse the Schema to see what Gutenberg Blocks are available to query and interact with. </p> <div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex"> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"> <div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM.png" alt="" class="wp-image-8122" width="356" height="813" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM.png 356w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM-131x300.png 131w" sizes="auto, (max-width: 356px) 100vw, 356px" /><figcaption>Screenshot of GraphiQL showing Gutenberg Blocks</figcaption></figure></div> </div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow"> <div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM.png" alt="" class="wp-image-8120" width="362" height="812" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM.png 361w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM-134x300.png 134w" sizes="auto, (max-width: 362px) 100vw, 362px" /><figcaption>Screenshot of GraphiQL showing the CoreParagraphBlock and its fields</figcaption></figure></div> </div> </div> <h4 class="wp-block-heading">Querying the blocks</h4> <p>And using the Schema, developers can construct a query to ask for the blocks and fields that their application supports.</p> <p><strong>Here&#8217;s an example query:</strong></p> <pre class="wp-block-code lang-graphql"><code>{ post(id: 6, idType: DATABASE_ID) { id databaseId title blocks { __typename name ... on CoreImageBlock { attributes { ... on CoreImageBlockAttributes { url alt caption } } } ... on CoreParagraphBlock { attributes { ... on CoreParagraphBlockAttributes { content } } } } } } </code></pre> <p><strong>And the response:</strong></p> <p>You can see that the response includes the exact fields that were asked for. No surprises. </p> <pre class="wp-block-code lang-json"><code>{ "data": { "post": { "id": "cG9zdDo2", "databaseId": 6, "title": "Test Gutenberg Post", "blocks": [ { "__typename": "CoreParagraphBlock", "name": "core/paragraph", "attributes": { "content": "This is a paragraph" } }, { "__typename": "CoreImageBlock", "name": "core/image", "attributes": { "url": "http://wpgraphql.local/wp-content/uploads/2021/03/Screen-Shot-2021-03-04-at-12.11.53-PM-1024x490.png", "alt": "Jason Bahl, dressed in character as JamStackMullet with a Mullet wig and sunglasses, watches the WP Engine Decode conference", "caption": "Screenshot of the JamStackMullet watching WP Engine Decode conference" } } }</code></pre> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="555" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-1024x555.png" alt="" class="wp-image-8132" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-1024x555.png 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-300x163.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-768x416.png 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM.png 1376w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption>Screenshot of a query for a post and some blocks using WPGraphQL for Gutenberg.</figcaption></figure></div> <h4 class="wp-block-heading">GraphQL Schema as the contract</h4> <p>Having the GraphQL Schema serve as the contract between the client and server allows each part of the application to move forward at its own pace. There&#8217;s now an agreement for how things will behave. If the contract is broken, for example, if the server changed the shape of one of the Types in the GraphQL Schema, it&#8217;s easily identifiable and can be fixed quickly, because the client specified exactly what was needed from the server by way of a GraphQL Query. </p> <p>This removes the &#8220;just guess&#8221; pattern from decoupled application development with Gutenberg.</p> <p>Teams that know nothing about WordPress can even make use of the data. For example, a data warehouse team, a native mobile team, a print team, etc. The GraphQL Schema and tooling such as GraphiQL frees up different teams to use the data in their applications how they want.</p> <h4 class="wp-block-heading">Client in control</h4> <p>With clients querying Gutenberg blocks as data, this gives clients full control over the presentation of the blocks. Whether the blocks are used in a React or Vue website, or used for a Native iOS app that doesn&#8217;t render HTML, or used to prepare a newspaper for print, the client gets to ask for the fields that it needs, and gets to decide what happens with the data. No unexpected changes from the server, the client is in control.</p> <h4 class="wp-block-heading">Tradeoffs: Scaling issues</h4> <p>While WPGraphQL for Gutenberg gets us much closer to being able to query Gutenberg blocks as data, it unfortunately has a dependency that makes it very difficult to scale, and it comes back, again, to the lack of a proper server side registry for blocks. </p> <p>Since Gutenberg Blocks aren&#8217;t registered on the server, WPGraphQL for Gutenberg has a settings page where users must click a button to &#8220;Update the Block Registry&#8221;. </p> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="848" height="128" src="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM.png" alt="" class="wp-image-8136" srcset="https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM.png 848w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM-300x45.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM-768x116.png 768w" sizes="auto, (max-width: 848px) 100vw, 848px" /><figcaption>Screenshot of the WPGraphQL for Gutenberg settings page</figcaption></figure></div> <p>Clicking this button opens up Gutenberg in a hidden iFrame, executes the JavaScript to instantiate Gutenberg, gets the Block Registry from Gutenberg initialized in JavaScript, sends the list of registered blocks to the server and stores the registry in the options table of the WordPress database. The registered blocks that are stored in the database are then used to map to the GraphQL Schema.</p> <p>Peter Pristas deservers an award, because this approach is a very creative solution to the frustrating problem of Gutenberg not respecting the WordPress server. </p> <p>Unfortunately this solution doesn&#8217;t scale well. <br><br>Since Gutenberg blocks are registered in JavaScript, this means that the JavaScript to register any given block might be enqueued from WordPress on only specific pages, specific post types, or other unique individualized criteria. </p> <p>That means the JavaScript Block Registry for Page A and Page B might be different from each other, and maybe also different from the registry for Post Type C or Post Type D. So loading one page in an iframe to get the block registry might not get the full picture of what blocks are <em>possible</em> to interact with in a decoupled application. </p> <p>In order for the block registry that is generated from the iframe to be accurate, <em>every</em> page of every post type that Gutenberg is enabled on in the site would need to be loaded by iframe to account for cases where blocks were registered to specific contexts. Yikes!</p> <h4 class="wp-block-heading">Tradoffs: Schema design issues</h4> <p>In addition to the scaling issues, there are some concerns with some of the Schema design choices, and I&#8217;ll even take the blame for some of this, as I had many conversations with Peter as he worked on the plugin, and he followed my lead with some of my also poor Schema design choices. </p> <p>One issue is infinite nesting. Gutenberg blocks, as previously discussed, can sometimes have nested inner blocks. In WPGraphQL for Gutenberg, querying inner blocks requires explicit queries and without knowing what level of depth inner blocks might reach, it&#8217;s difficult to compose queries that properly return all inner blocks. </p> <p>WPGraphQL used to expose hierarchical data in a similar way, but has since changed to expose hierarchical data, such as Nav Menu Items, in flat lists. This allows for nested data in any depth to be queried, and <a href="https://www.wpgraphql.com/docs/menus/#hierarchical-data">re-structured in a hierarchy on the client</a>.</p> <p>The unlimited depth issue is commonly reported for projects such as <a href="https://github.com/gatsbyjs/gatsby-source-wordpress-experimental/issues?q=is%3Aissue+gutenberg+is%3Aclosed">Gatsby Source WordPress</a>. </p> <h4 class="wp-block-heading">When to use</h4> <p>If Gutenberg is a requirement for your headless project, this might be a good option, as it allows you to query Gutenberg blocks as structured data. You gain a lot of the predictability that you miss with the other options, and can benefit greatly from features of GraphQL such as <a href="https://www.wpgraphql.com/docs/wpgraphql-vs-wp-rest-api/#batch-queries">Batch Queries</a>, <a href="https://www.wpgraphql.com/docs/intro-to-graphql/#fragments">coupling Fragments with components</a>, and more.</p> <p>So while WPGraphQL for Gutenberg is probably the closest option available for being able to predictably query Gutenberg blocks as data in decoupled applications, there are some serious questions in regards to production readiness, <em>especially</em> on larger projects, and you should consider these issues before choosing it for your next project.</p> <p>Tradeoffs in mind, agencies such as WebDevStudios are <a href="https://webdevstudios.com/2021/03/09/next-js-headless-wordpress/" target="_blank" rel="noreferrer noopener">using this approach in production</a>, even for large sites.</p> <h2 class="wp-block-heading">Progress for the server side block registry</h2> <p>In 2020, some progress was made in regards to a server side registry for Gutenberg blocks. </p> <p>While the <a href="https://developer.wordpress.org/block-editor/tutorials/block-tutorial/writing-your-first-block-type/#enqueuing-block-scripts">official Gutenberg documentation</a> still shows developers how to create new blocks fully JavaScript with no server awareness, the core Gutenberg blocks have started transitioning to have <em>some</em> data registered on the server.</p> <p>You can <a href="https://github.com/nas/content/live/wpgqlcontent/WordPress/tree/master/wp-includes/blocks">see here</a> that (as of Gutenberg 5.6.2, released in February 2021) core Gutenberg blocks are now registered with JSON files that can be used by the PHP server as well as the JavaScript client. </p> <p>These JSON files are now used to expose blocks to the WP REST API.</p> <p> This is progress! </p> <h3 class="wp-block-heading">Inner blocks, inner peace?</h3> <p>Unfortunately it&#8217;s not all the progress needed to have meaningful impact for decoupled applications to use Gutenberg. There&#8217;s a lot of information that a decoupled application would need about blocks that is not described in the server registry. One example (of many) being inner blocks.</p> <p>Gutenberg has a concept called &#8220;Inner Blocks&#8221;, which is blocks that can have other blocks nested within. For example, a &#8220;Column&#8221; block can have other blocks nested within each column, while other blocks such as an Image block cannot have nested inner blocks.</p> <p>The bit of server side registry that is now available for core Gutenberg blocks doesn&#8217;t declare this information. If we take a look at the Column block&#8217;s <a href="https://github.com/nas/content/live/wpgqlcontent/WordPress/blob/5.6.2/wp-includes/blocks/column/block.json">block.json</a> file, we can see there&#8217;s no mention of inner blocks being supported. Additionally, if. we look at the Image block&#8217;s <a href="https://github.com/nas/content/live/wpgqlcontent/WordPress/blob/5.6.2/wp-includes/blocks/image/block.json">block.json</a> file, we don&#8217;t see any mention of inner blocks <em>not</em> being supported. </p> <p>In order for a decoupled application, such as the official WordPress iOS app, to know what blocks can or cannot have inner blocks, this information needs to be exposed to an API that the decoupled application can read. Without the server knowing about this information, decoupled applications cannot know this information either.</p> <p>So, while there&#8217;s been a bit of a migration for the core WordPress blocks to have some server (and REST API) awareness, there&#8217;s still a lot of missing information. Also the community of 3rd party block developers are still being directed to build blocks entirely in JavaScript, which means that all new blocks will have no server awareness until the server registry becomes more of a 1st-class citizen for Gutenberg.</p> <h2 class="wp-block-heading">What&#8217;s next?</h2> <p>The beginnings of a move toward a server-side registry gives hope, and gives a bit of a path toward blocks being properly introspect-able and useful by decoupled teams and applications.</p> <h3 class="wp-block-heading">Specification for Server Side Registering Blocks</h3> <p>I believe that the step forward for Gutenberg + decoupled applications, is to come up with a specification for how Gutenberg blocks can be registered on the server to work properly with server APIs. </p> <p>Once a specification is discussed, vetted, tested and published, the WP REST API, WP CLI and WPGraphQL, and therefore decoupled applications such as the WordPress native mobile app, would all make use of the spec to be able to interact with Gutenberg blocks.</p> <p><em>I don&#8217;t fully know what this spec needs to look like, but I believe it needs to exist in some form.</em></p> <p>Projects such as <a href="https://github.com/rtCamp/gutenberg-fields-middleware">Gutenberg Fields Middleware</a> from <a href="https://rtcamp.com/">rtCamp</a>, <a href="https://www.advancedcustomfields.com/resources/blocks/">ACF Blocks</a>, and <a href="https://wordpress.org/plugins/genesis-custom-blocks/">Genesis Custom Blocks</a> all take a server-first approach to creating new Gutenberg blocks, and I think there&#8217;s a lot to learn from these projects. </p> <p>The blocks from these tools are created in a way that the WordPress server knows what blocks exist, what attributes and fields the blocks have, and the server can then pass the description of the blocks to the Gutenberg JavaScript application, which then renders the blocks for users to interact with. </p> <p>Since the server provides the Gutenberg JavaScript application with the information needed to render the blocks to a content producer, this means the server can <em>also</em> provide the information to other clients, such as the native mobile WordPress app, or teams building decoupled front-ends with Gatsby, Gridsome or NextJS.</p> <h3 class="wp-block-heading">The future of decoupled Gutenberg</h3> <p>I believe that with a proper specification for registering blocks on the server, Gutenberg can enable some incredibly powerful integrations across the web.</p> <p>My thoughts are that we can arrive at a specification for registering blocks that can enable block developers to provide pleasant editing experiences, while providing decoupled application developers with the ability to Introspect the GraphQL API, predictably write GraphQL Queries (and Mutations) to interact with blocks, and get predictable, strongly typed results that can be used in decoupled applications.</p> <p>In an effort to start discussing what the future of a Gutenberg Block Server Registry Specification like this might look like, I&#8217;ve opened the following Github issue: <a rel="noreferrer noopener" href="https://github.com/wp-graphql/wp-graphql/issues/1764" target="_blank">https://github.com/wp-graphql/wp-graphql/issues/1764</a></p> <p>If this topic interests you, and you&#8217;d like to be involved in discussing what such a specification might look like, please contribute ideas to that issue. Additionally, you can <a href="https://join.slack.com/t/wp-graphql/shared_invite/zt-3vloo60z-PpJV2PFIwEathWDOxCTTLA">Join the WPGraphQL community on Slack</a> community, and visit the <code>#gutenberg</code> channel and discuss in there.</p> </div></div></article></main></div></div><footer class="border-t border-slate-200 dark:border-slate-200/5 py-10"><div class="max-w-7xl mx-auto px-10 flex flex-col gap-6 justify-between md:flex-row text-slate-500"><div class="flex justify-center space-x-6 md:order-2"><a href="https://github.com/wp-graphql/wp-graphql" class="text-gray-600 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-100" target="_blank" rel="noreferrer"><div><span class="sr-only">GitHub</span><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path></svg></div></a><a href="https://wordpress.org/plugins/wp-graphql" class="text-gray-600 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-100" target="_blank" rel="noreferrer"><div><span class="sr-only">WordPress Plugin</span><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M21.469 6.825c.84 1.537 1.318 3.3 1.318 5.175 0 3.979-2.156 7.456-5.363 9.325l3.295-9.527c.615-1.54.82-2.771.82-3.864 0-.405-.026-.78-.07-1.11m-7.981.105c.647-.03 1.232-.105 1.232-.105.582-.075.514-.93-.067-.899 0 0-1.755.135-2.88.135-1.064 0-2.85-.15-2.85-.15-.585-.03-.661.855-.075.885 0 0 .54.061 1.125.09l1.68 4.605-2.37 7.08L5.354 6.9c.649-.03 1.234-.1 1.234-.1.585-.075.516-.93-.065-.896 0 0-1.746.138-2.874.138-.2 0-.438-.008-.69-.015C4.911 3.15 8.235 1.215 12 1.215c2.809 0 5.365 1.072 7.286 2.833-.046-.003-.091-.009-.141-.009-1.06 0-1.812.923-1.812 1.914 0 .89.513 1.643 1.06 2.531.411.72.89 1.643.89 2.977 0 .915-.354 1.994-.821 3.479l-1.075 3.585-3.9-11.61.001.014zM12 22.784c-1.059 0-2.081-.153-3.048-.437l3.237-9.406 3.315 9.087c.024.053.05.101.078.149-1.12.393-2.325.609-3.582.609M1.211 12c0-1.564.336-3.05.935-4.39L7.29 21.709C3.694 19.96 1.212 16.271 1.211 12M12 0C5.385 0 0 5.385 0 12s5.385 12 12 12 12-5.385 12-12S18.615 0 12 0"></path></svg></div></a><a href="https://twitter.com/wpgraphql" class="text-gray-600 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-100" target="_blank" rel="noreferrer"><div><span class="sr-only">Twitter</span><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M21.543 7.104c.015.211.015.423.015.636 0 6.507-4.954 14.01-14.01 14.01v-.003A13.94 13.94 0 0 1 0 19.539a9.88 9.88 0 0 0 7.287-2.041 4.93 4.93 0 0 1-4.6-3.42 4.916 4.916 0 0 0 2.223-.084A4.926 4.926 0 0 1 .96 9.167v-.062a4.887 4.887 0 0 0 2.235.616A4.928 4.928 0 0 1 1.67 3.148 13.98 13.98 0 0 0 11.82 8.292a4.929 4.929 0 0 1 8.39-4.49 9.868 9.868 0 0 0 3.128-1.196 4.941 4.941 0 0 1-2.165 2.724A9.828 9.828 0 0 0 24 4.555a10.019 10.019 0 0 1-2.457 2.549z"></path></svg></div></a><a href="https://www.youtube.com/c/WPGraphQL" class="text-gray-600 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-100" target="_blank" rel="noreferrer"><div><span class="sr-only">YouTube</span><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"></path></svg></div></a><a href="/discord" class="text-gray-600 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-100" target="_blank" rel="noreferrer"><div><span class="sr-only">Discord</span><svg stroke="currentColor" fill="currentColor" stroke-width="0" role="img" viewBox="0 0 24 24" class="h-6 w-6" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><title></title><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"></path></svg></div></a></div><div class="mt-8 md:mt-0 md:order-1 prose dark:prose-invert"><p class="text-center text-base text-navy dark:text-gray-300">© <!-- -->2025<!-- --> WPGraphQL. All rights reserved.</p></div></div></footer></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"__SEED_NODE__":{"__typename":"Post","uri":"/2021/03/09/gutenberg-and-decoupled-applications/","id":"cG9zdDo4MDU4","databaseId":8058,"isContentNode":true,"slug":"gutenberg-and-decoupled-applications","contentType":{"__typename":"ContentNodeToContentTypeConnectionEdge","node":{"__typename":"ContentType","name":"post"}},"template":{"__typename":"DefaultTemplate","templateName":"Default"}},"__TEMPLATE_QUERY_DATA__":{"post":{"__typename":"Post","id":"cG9zdDo4MDU4","title":"Gutenberg and Decoupled Applications","uri":"/2021/03/09/gutenberg-and-decoupled-applications/","date":"2021-03-09T14:31:03","content":"\n\u003cp\u003eIn this article I want to dive into the current state of Gutenberg and WPGraphQL. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis is a technical article about using Gutenberg blocks in the context of decoupled / headless / API-driven WordPress, and makes the assumption that you already know what Gutenberg is and have some general understanding of how it works.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eTL;DR\u003c/h2\u003e\n\n\n\n\u003cp\u003eClient-server contracts around the shape of data is fundamental to achieving \u0026#8220;separation of concerns\u0026#8221;, a pillar of modular and decoupled application development. \u003c/p\u003e\n\n\n\n\u003cp\u003eWhile much of WordPress was built with \u003ca href=\"#wordpress-contracts\" data-type=\"internal\" data-id=\"#wordpress-contracts\"\u003edecoupling in mind\u003c/a\u003e, the \u003ca href=\"#the-wp-rest-api-provides-a-schema\"\u003eWP REST API\u003c/a\u003e and \u003ca href=\"#the-gutenberg-block-registry\"\u003eGutenberg were not\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eAs a result, decoupled application developers interacting with WordPress are \u003ca href=\"#challenges-and-the-current-state-of-gutenberg\"\u003elimited in what they can achieve\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003e With the \u003ca href=\"https://wpengine.com/headless-cms-research/\"\u003egrowing demand for headless WordPress\u003c/a\u003e, this is a key limitation that will hamper growth.\u003c/p\u003e\n\n\n\n\u003cp\u003eFortunately, even with \u003ca href=\"#challenges-and-the-current-state-of-gutenberg\"\u003ethe limitations\u003c/a\u003e, there are ways forward. In this article \u003ca href=\"#three-approaches-to-using-gutenberg-in-decoupled-applications-today\"\u003eI walk through 3 approaches\u003c/a\u003e you can implement to use Gutenberg in decoupled applications today, tradeoffs included, and \u003ca href=\"#whats-next\"\u003epropose a plan\u003c/a\u003e to make the future of Gutenberg for decoupled applications a better one.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\" id=\"replacing-my-door-lock\"\u003eReplacing my door lock\u003c/h2\u003e\n\n\n\n\u003cp\u003eI recently replaced the lock on the front door of my house.\u003c/p\u003e\n\n\n\n\u003cp\u003eI ordered the lock from an online retailer. I was able to select a specific brand of lock in a specific color. \u003c/p\u003e\n\n\n\n\u003cp\u003eWhen the lock arrived and I opened the package, it was the same brand and color that I ordered. It wasn\u0026#8217;t just any random lock, it was the one that I agreed to pay for, and the online retailer agreed to mail me. \u003c/p\u003e\n\n\n\n\u003cp\u003eI was able install the lock without any surprises. I didn\u0026#8217;t have to drill any new holes in my door. The new lock fit the hole in my door that I removed the old lock from. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe new lock wasn\u0026#8217;t made by the same manufacturer that made the door, and yet, the lock installed on my door just fine. In fact, there were \u003cem\u003eat least\u003c/em\u003e 30 different locks from a variety of manufacturers that I could have selected that all would have worked in my door without any complications. \u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eDecoupled systems\u003c/h2\u003e\n\n\n\n\u003cp\u003eThis wasn\u0026#8217;t \u003cem\u003ereally\u003c/em\u003e a story about doors and locks. It\u0026#8217;s a story about decoupled systems, and the contracts, or agreements, that make them work. \u003c/p\u003e\n\n\n\n\u003cp\u003eAnd its intent is to help frame what I\u0026#8217;m talking about with using WordPress, and specifically Gutenberg, in decoupled contexts. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn order for decoupled systems to work well, whether it\u0026#8217;s doors and door locks, or WordPress and a decoupled JavaScript application, there needs to be some sort of agreement between the different parts of the system.\u003c/p\u003e\n\n\n\n\u003cp\u003eIn the case of door and lock manufacturers, it\u0026#8217;s an agreement over the size and positioning of the holes in the door.\u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-1024x890.jpg\" alt=\"\" class=\"wp-image-8165\" width=\"512\" height=\"445\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-1024x890.jpg 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-300x261.jpg 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-768x668.jpg 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram.jpg 1200w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" /\u003e\u003cfigcaption\u003eDiagram showing measurements for a door lock hole \u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eDoor manufacturers can build doors at their leisure and lock manufacturers at theirs, and when the time comes to bring them together, they work without issue because both parties are adhering to an agreement.\u003c/p\u003e\n\n\n\n\u003cp\u003eIn the case of e-commerce, there are agreements about what a consumer purchases and what should be delivered. In my case, the online store provided a list of locks that were available to purchase. I selected a specific lock, paid for it, and in response I received the lock we agreed to, in exchange for my payment. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eDecoupled tech, decoupled teams\u003c/h3\u003e\n\n\n\n\u003cp\u003eWhen WPGraphQL first started, I was working at a newspaper that had a CMS team that focused on WordPress, a Native Mobile team that focused on the iOS and Android applications, a Data Warehouse team that collected various data from the organization and a Print team that took the data from WordPress and prepared it for Print.\u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress was the entry point for content creators to write content, but the web was only one of many channels where content was being used. \u003c/p\u003e\n\n\n\n\u003cp\u003eNot only was the technology decoupled (PHP for the CMS, React Native for mobile apps, Python for Data warehousing and some legacy system I forget the name of for print), but the teams were also decoupled.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe only team that really needed to understand WordPress was the CMS team. The other teams were able to use WPGraphQL Schema Introspection to build tools for their teams using data from WordPress, without needing to understand anything about PHP or WordPress under the hood. \u003c/p\u003e\n\n\n\n\u003cp\u003eMuch like door and lock manufacturers don\u0026#8217;t need to be experts at what the other is building, WPGraphQL\u0026#8217;s schema served as the contract, enabling many different teams to use WordPress data when, and how, they needed. \u003c/p\u003e\n\n\n\n\u003cp\u003eWPGraphQL served as the contract between the CMS team and the other teams as well as WordPress the system and the other team\u0026#8217;s decoupled systems.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eWordPress contracts\u003c/h2\u003e\n\n\n\n\u003cp\u003eFor WordPress, one of the common contracts, or agreements established between multiple systems (such as plugins, themes, and WordPress core) comes in the form of registries.\u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress has registries for Post Types, Taxonomies, Settings, Meta and more. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u003ca href=\"https://developer.wordpress.org/reference/functions/register_post_type/\"\u003eregister_post_type\u003c/a\u003e function has more than 30 options that can be configured to define the contract between the Post Type existing and how WordPress core and decoupled systems (namely plugins and themes) should interact with it. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u003ca href=\"https://www.google.com/url?client=internal-element-cse\u0026amp;cx=012566942813864066925:bnbfebp99hs\u0026amp;q=https://developer.wordpress.org/reference/functions/register_taxonomy/\u0026amp;sa=U\u0026amp;ved=2ahUKEwi0o7DeqZXvAhXaHc0KHTbtBe0QFjAAegQIABAB\u0026amp;usg=AOvVaw2n2TUd-jKjirfxq2j5y_Wm\"\u003eregister_taxonomy\u003c/a\u003e, \u003ca href=\"https://developer.wordpress.org/reference/functions/register_meta/\"\u003eregister_meta\u003c/a\u003e, \u003ca href=\"https://developer.wordpress.org/reference/functions/register_setting/\"\u003eregister_setting\u003c/a\u003e, \u003ca href=\"https://developer.wordpress.org/reference/functions/register_sidebar/\"\u003eregister_sidebar\u003c/a\u003e and other \u003ca href=\"https://wordpress.org/search/register_?in=developer_documentation\"\u003eregister_*\u003c/a\u003e functions in WordPress serve a similar purpose. They allow for a contract to be established so that many different systems can work with WordPress in an agreed upon way.\u003c/p\u003e\n\n\n\n\u003cp\u003eThese registries serve as a contract between WordPress core and decoupled systems (themes and plugins) that can work with WordPress. Because these registries establish an agreement with how WordPress core will behave, plugins and themes can latch onto these registries and extend WordPress core in some powerful ways.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe decoupled (pluggable) architecture of WordPress is enabled by these contracts. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM.png\" alt=\"Image showing WordPress in the middle with the logos for ElasticPress, WordPress SEO by Yoast, WPGraphQL and Advanced Custom Fields around it.\" class=\"wp-image-8171\" width=\"600\" height=\"528\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM.png 857w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM-300x264.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM-768x676.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" /\u003e\u003cfigcaption\u003eWordPress registries enable plugins to iterate outside of WordPress core\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eRegistering a new post type to WordPress can get you a UI in the WordPress dashboard, but it can also get your content indexed to Elastic Search via \u003ca href=\"https://wordpress.org/plugins/elasticpress/\"\u003eElasticPress\u003c/a\u003e, powerful SEO tools from \u003ca href=\"https://wordpress.org/plugins/wordpress-seo/\"\u003eWordPress SEO\u003c/a\u003e, custom admin functionality from \u003ca href=\"https://wordpress.org/plugins/advanced-custom-fields/\"\u003eAdvanced Custom Fields\u003c/a\u003e, and API access via \u003ca href=\"https://wordpress.org/plugins/wp-graphql/\"\u003eWPGraphQL\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eIf the next release of WordPress started hiding the UI for all post types that were registered with \u003ccode\u003eshow_ui =\u0026gt; true\u003c/code\u003e, or stopped allowing plugins from reading the post type registry, there would likely be a bug (or hundreds) reported on \u003ca href=\"https://core.trac.wordpress.org/\"\u003eTrac\u003c/a\u003e, (and Twitter, and Slack, etc), as that would mean WordPress was breaking the established contract. \u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eThe client/server contract\u003c/h2\u003e\n\n\n\n\u003cp\u003eLike we discussed earlier, decoupled systems need some sort of shared agreement in order to work well together. It doesn\u0026#8217;t have to be a GraphQL API, but it has to be \u003cem\u003esomething.\u003c/em\u003e\u003cbr\u003e\u003cbr\u003eFor WordPress, this comes in the form of APIs. \u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress core has 2 built-in APIs that enable decoupled applications to interact with WordPress data, \u003ca href=\"https://codex.wordpress.org/XML-RPC_WordPress_API\"\u003eXML-RPC\u003c/a\u003e and the \u003ca href=\"https://developer.wordpress.org/rest-api/\"\u003eWP REST API\u003c/a\u003e. \u003c/p\u003e\n\n\n\n\u003cp\u003eAnd, of course, there\u0026#8217;s yours truly, \u003ca href=\"https://wordpress.org/plugins/wp-graphql/\"\u003eWPGraphQL\u003c/a\u003e, a free open-source WordPress plugin that provides an extendable GraphQL schema and API for any WordPress site.\u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-image size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM.png\" alt=\"Blocks representing REST, GraphQL and RPC API on top of a block representing the Authorization and Business logic layers of WordPress, and at the bottom is a block representing the Persistence Layer (MySQL).\" class=\"wp-image-8176\" width=\"537\" height=\"493\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM.png 592w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM-300x276.png 300w\" sizes=\"auto, (max-width: 537px) 100vw, 537px\" /\u003e\u003cfigcaption\u003eDiagram of the WordPress server + API setup\u003c/figcaption\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eIn order for decoupled applications, such as \u003ca href=\"https://www.gatsbyjs.com/plugins/gatsby-source-wordpress/\"\u003eGatsby\u003c/a\u003e, \u003ca href=\"https://github.com/WebDevStudios/nextjs-wordpress-starter\"\u003eNextJS\u003c/a\u003e, \u003ca href=\"https://frontity.org/\"\u003eFrontity\u003c/a\u003e, native mobile applications or others, to work with WordPress, the APIs must establish a contract that WordPress and the decoupled application can both work against.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eThe WP REST API provides a Schema\u003c/h3\u003e\n\n\n\n\u003cp\u003eThe WordPress REST API provides a Schema that acts as this contract. The Schema is introspect-able, allowing remote systems to see what\u0026#8217;s available before asking for the data. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis is a good thing! \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eBut the Schema is not enforced\u003c/h3\u003e\n\n\n\n\u003cp\u003eHowever, the WP REST API doesn\u0026#8217;t \u003cem\u003eenforce\u003c/em\u003e the Schema. \u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress plugins that extend the WP REST API Schema can add fields to the API without defining what data will be returned in the REST API Schema. Or, they can register fields that return \u0026#8220;object\u0026#8221; as a wildcard catch-all. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis is a bad thing! \u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled teams and applications cannot reliably use the WordPress REST API if it doesn\u0026#8217;t enforce any type of contract. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eOptional Schema and wildcard return types\u003c/h4\u003e\n\n\n\n\u003cp\u003ePlugins such as the \u003ca href=\"https://wordpress.org/plugins/acf-to-rest-api/\"\u003eAdvanced Custom Fields to REST API\u003c/a\u003e add a single \u0026#8220;acf\u0026#8221; field to the REST endpoints and declare in the WP REST API that the field will return \u0026#8220;an object\u0026#8221;. \u003c/p\u003e\n\n\n\n\u003cp\u003eWe can see this if we introspect the WP REST API of a WordPress install with this plugin active: \u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-image size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"459\" height=\"456\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection.png\" alt=\"\" class=\"wp-image-8107\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection.png 459w, https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection-300x298.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection-150x150.png 150w\" sizes=\"auto, (max-width: 459px) 100vw, 459px\" /\u003e\u003cfigcaption\u003eScreenshot showing the Introspection of the ACF to REST API Schema definition\u003c/figcaption\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eThis means that decoupled applications, and the teams building them, have no way to predict what data this field will ever return. This also means that even if a decoupled application does manage to get built, it could break at any time, because there\u0026#8217;s no contract agreed to between the client and the server. The WordPress server can return \u003cem\u003eanything\u003c/em\u003e at \u003cem\u003eanytime\u003c/em\u003e.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eUnpredictable data is frustrating for API consumers\u003c/h4\u003e\n\n\n\n\u003cp\u003eWith the field defined as \u0026#8220;object\u0026#8221; the data returned can be different from page to page, post to post, user to user, and so on. There\u0026#8217;s no predictable way decoupled application developers can prepare for the data the API will return. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis would be like me trying to purchase that door lock, but instead of the website showing me a list of door locks with specific colors to chose from, I was just given one \u0026#8220;product\u0026#8221; as the option to purchase.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u0026#8220;product\u0026#8221; might be a hat or some new sunglasses, or if I\u0026#8217;m really lucky, it might be a door lock. I don\u0026#8217;t have any way of knowing what the \u0026#8220;product\u0026#8221; is, until I receive it. \u003c/p\u003e\n\n\n\n\u003cp\u003eAs an e-commerce consumer, this is not helpful. \u003c/p\u003e\n\n\n\n\u003cp\u003eAnd as a decoupled application developer, this type of API is frustrating.\u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled systems don\u0026#8217;t work well if part of the equation is to \u0026#8220;just guess\u0026#8221;.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eGraphQL enforces Schema and Strong Types\u003c/h3\u003e\n\n\n\n\u003cp\u003eWPGraphQL, on the other hand, \u003cem\u003eenforces\u003c/em\u003e a strongly Typed Schema. There is no option to extend the WPGraphQL API without describing the type of data the API will return. Additionally, there is no \u0026#8220;wildcard\u0026#8221; type. \u003c/p\u003e\n\n\n\n\u003cp\u003eA plugin cannot register a field to the WPGraphQL Schema that returns a door lock on one request, and sunglasses or a hat on the next request.\u003c/p\u003e\n\n\n\n\u003cp\u003eTo extend WPGraphQL, plugins must register fields that declare a \u003cem\u003especific\u003c/em\u003e Type of data that will be returned. And this contract must be upheld. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis removes the \u0026#8220;just guess\u0026#8221; part of the equation. \u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled application developers always know what to expect.\u003c/p\u003e\n\n\n\n\u003cp\u003eMuch like I, as an e-commerce consumer, was able to browse the list of door locks that were possible to purchase on the online store, decoupled application developers can use a tool such as GraphiQL to browse the GraphQL Schema and see what Types and Fields are available to query from the GraphQL API. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe screenshot below shows GraphiQL being used to explore a GraphQL Schema. The screenshot shows the type named \u0026#8220;Post\u0026#8221; in the GraphQL Schema with a field named \u0026#8220;slug\u0026#8221; which declares that it will return a String.\u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-image size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"356\" height=\"176\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM.png\" alt=\"\" class=\"wp-image-8087\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM.png 356w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM-300x148.png 300w\" sizes=\"auto, (max-width: 356px) 100vw, 356px\" /\u003e\u003cfigcaption\u003eScreenshot of GraphiQL showing the \u0026#8220;slug\u0026#8221; field on the \u0026#8220;Post\u0026#8221; type.\u003c/figcaption\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eApplication developers can take the information they get from the Schema and construct queries that are now predictable.\u003c/p\u003e\n\n\n\n\u003cp\u003eAnd the GraphQL Schema serves as the contract between the server and the client application, ensuring that the server will return the data in the same shape the client was promised.\u003c/p\u003e\n\n\n\n\u003cp\u003eJust like I received the specific door lock matching the brand and color that I specified in my order, client applications can specify the Types and Fields they require with a GraphQL Query, and the response will match what was asked for. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn the example below, the GraphQL Query asks for a Post and the \u0026#8220;slug\u0026#8221; field, which we can see in the Schema that it will return a String. And in response to this query, the GraphQL server will provide just what was asked for. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u0026#8220;just guess\u0026#8221; part of the server/client equation is eliminated.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eExample GraphQL Query \u0026amp; Response\u003c/h3\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-graphql\"\u003e\u003ccode\u003equery {\n post( id: 1, idType: DATABASE_ID ) {\n slug\n }\n}\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-json\"\u003e\u003ccode\u003e{\n \"data\": {\n \"post\": {\n \"slug\": \"hello-world\",\n },\n },\n}\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"1010\" height=\"289\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM.png\" alt=\"\" class=\"wp-image-8147\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM.png 1010w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM-300x86.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM-768x220.png 768w\" sizes=\"auto, (max-width: 1010px) 100vw, 1010px\" /\u003e\u003cfigcaption\u003eScreenshot showing a GraphQL Query and Response in the GraphiQL IDE\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eThe Gutenberg block registry\u003c/h2\u003e\n\n\n\n\u003cp\u003eNow that we\u0026#8217;re on the same page about contracts between decoupled systems and how WPGraphQL provides a contract between the WordPress server and client applications, let\u0026#8217;s move on to discuss Gutenberg more specifically. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eEarly integration with WPGraphQL\u003c/h3\u003e\n\n\n\n\u003cp\u003eGutenberg as a concept was fascinating to me early on. Like many others, I saw the potential for this block-based editor to impact WordPress users and the WordPress ecosystem greatly, WPGraphQL included.\u003c/p\u003e\n\n\n\n\u003cp\u003eI explored exposing Gutenberg blocks as queryable data in WPGraphQL as far back as June 2017: \u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter\"\u003e\u003cdiv class=\"wp-block-embed__wrapper\"\u003e\n\u003cblockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eTurns out \u003ca href=\"https://twitter.com/wpgraphql?ref_src=twsrc%5Etfw\"\u003e@wpgraphql\u003c/a\u003e plays nice with the new Gutenberg editor. \u003ca href=\"https://twitter.com/hashtag/WordPress?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#WordPress\u003c/a\u003e \u003ca href=\"https://twitter.com/hashtag/graphql?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#graphql\u003c/a\u003e \u003ca href=\"https://t.co/0Vlav4w75k\"\u003epic.twitter.com/0Vlav4w75k\u003c/a\u003e\u003c/p\u003e\u0026mdash; GraphQL for WordPress (@wpgraphql) \u003ca href=\"https://twitter.com/wpgraphql/status/878364222253481984?ref_src=twsrc%5Etfw\"\u003eJune 23, 2017\u003c/a\u003e\u003c/blockquote\u003e\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n\u003c/div\u003e\u003c/figure\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eChallenges and the current state of Gutenberg\u003c/h3\u003e\n\n\n\n\u003cp\u003eWhile a basic initial integration was straightforward, I ran into roadblocks quickly. \u003c/p\u003e\n\n\n\n\u003cp\u003eGutenberg didn\u0026#8217;t have a server-side registry for blocks. At this time, all blocks in Gutenberg were fully registered in JavaScript, which is not executed or understood by the WordPress server. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis means that unlike Post Types, Taxonomies, Meta, Sidebars, Settings, and other constructs that make up WordPress, Gutenberg blocks don\u0026#8217;t adhere to any type of contract with the WordPress server. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis means that the WordPress server knows nothing about blocks. There are no agreements between Gutenberg blocks and other systems in WordPress, or systems trying to interact with WordPress via APIs. \u003c/p\u003e\n\n\n\n\u003cp\u003eBlocks were practically non-existent as far as the application layer of WordPress was concerned. \u003c/p\u003e\n\n\n\n\u003cp\u003eThere were no WP-CLI commands to create, update, delete or list blocks. No WP REST API Schema or endpoints for blocks. No XML-RPC methods for blocks. \u003cem\u003eAnd no way to expose blocks to WPGraphQL\u003c/em\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eWithout any kind of agreement between the WordPress server and the Gutenberg JavaScript application, the WordPress server can\u0026#8217;t interact with blocks in meaningful ways. \u003c/p\u003e\n\n\n\n\u003cp\u003eFor example, the WordPress server cannot validate user input on Gutenberg blocks. Data that users input into the fields in Gutenberg blocks is trusted without question and saved to the database without the server having final say. This is a dangerous precedent, especially as Gutenberg is moving outside of editing Post content and into other parts of full-site editing. As far as I know, the lack of block input validation by the WordPress server is still a problem today.\u003c/p\u003e\n\n\n\n\u003cp\u003eAnyway, without the WordPress server having any knowledge of blocks, WPGraphQL also could not provide a meaningful integration with Gutenberg. \u003c/p\u003e\n\n\n\n\u003cp\u003eI was sad, because I was optimistic that this integration could lead to some really great innovations for decoupled applications. \u003c/p\u003e\n\n\n\n\u003cp\u003eShortly after my tweet above and running into roadblocks, I raised these concerns with the Gutenberg team on Twitter and Slack. The Gutenberg team asked me to post my thoughts in a Gutenberg Github issue, \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/gutenberg/issues/2751#issuecomment-330930741\"\u003ewhich I did at length\u003c/a\u003e. While my comments received a lot of positive emoji reactions from the community. Unfortunately the issue has been closed with many of the concerns outstanding.\u003c/p\u003e\n\n\n\n\u003cp\u003eMonths later I also \u003ca href=\"https://make.wordpress.org/mobile/2018/03/21/gutenberg-on-mobile/#comment-19383\"\u003evoiced similar concerns\u003c/a\u003e on the Make WordPress post about Gutenberg and Mobile, pointing out that without a proper server registry and API, decoupled applications, such as the WordPress native mobile application, won\u0026#8217;t be able to support Custom Blocks, or even per-site adjustments to core blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eAs of today, my understanding is that the WordPress native mobile applications still do not support custom blocks or adjustments to core blocks, making the App nearly useless for sites that have adopted Gutenberg.\u003c/p\u003e\n\n\n\n\u003cp\u003eEven with the limitations of Gutenberg, the headless WordPress community has been determined to use Gutenberg with decoupled applications.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eThree approaches to using Gutenberg in decoupled applications, today\u003c/h2\u003e\n\n\n\n\u003cp\u003eBelow are some of the different approaches, including tradeoffs, that you can implement today to start using Gutenberg in decoupled applications.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eGutenberg blocks as HTML\u003c/h3\u003e\n\n\n\n\u003cp\u003eI believe the \u003cem\u003efastest\u003c/em\u003e way to get started using Gutenberg in decoupled applications today, is to query the \u0026#8220;content\u0026#8221; field from WPGraphQL (or the WP REST API, if it\u0026#8217;s still your flavor).\u003c/p\u003e\n\n\n\n\u003cp\u003eThis is the approach that \u003ca href=\"https://frontity.org/blog/connecting-gutenberg-and-frontity/\"\u003eFrontity is using\u003c/a\u003e.\u003cbr\u003e\u003cbr\u003eThis is also the approach I\u0026#8217;m using for WPGraphQL.com, which is in use on this very blog post you\u0026#8217;re reading right now. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis post is written in Gutenberg, queried by Gatsby using WPGraphQL, and rendered using React components!\u003cbr\u003e\u003cbr\u003eHere\u0026#8217;s how it works (and please don\u0026#8217;t judge my hacky JavaScript skills ????): \u003c/p\u003e\n\n\n\n\u003cul class=\"wp-block-list\"\u003e\u003cli\u003eThe GraphQL Query in Gatsby gets the content (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/master/src/templates/WpDocument.js#L86-L88\"\u003esee the code\u003c/a\u003e)\u003c/li\u003e\u003cli\u003eThe content is passed through a parser (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/master/src/templates/WpDocument.js#L56\"\u003esee the code\u003c/a\u003e)\u003c/li\u003e\u003cli\u003eThe parser converts standard HTML elements into the \u003ca href=\"https://chakra-ui.com/\"\u003eChakra UI\u003c/a\u003e equivalent to play nice with theming (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parse-html.js#L98-L103\"\u003esee the code\u003c/a\u003e)\u003c/li\u003e\u003cli\u003eThe parser also converts things like HTML for Twitter embeds, and `\u0026lt;code\u0026gt;` blocks into React components (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parsed-components/index.js#L146-L167\"\u003esee the code\u003c/a\u003e)\u003cul\u003e\u003cli\u003e\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parsed-components/index.js#L94-L101\"\u003eThis is how\u003c/a\u003e we get neat things like the Syntax highlighting and \u0026#8220;copy\u0026#8221; button on the code snippets\u003c/li\u003e\u003c/ul\u003e\u003c/li\u003e\u003c/ul\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoff: Lack of Introspection, unpredictable data\u003c/h4\u003e\n\n\n\n\u003cp\u003eWhile this is working for me and WPGraphQL.com, I can\u0026#8217;t recommend it for everyone.\u003c/p\u003e\n\n\n\n\u003cp\u003eUsing HTML as the API defeats much of the purpose of decoupled systems. In order to use the markup as an API, the developers of the decoupled application need to be able to predict all the markup that might come out of the editor. \u003c/p\u003e\n\n\n\n\u003cp\u003eQuerying HTML and trying to predict all the markup to parse is like me ordering \u0026#8220;product\u0026#8221; at the store. At any time I (or other users of WordPress) could add blocks with markup that my parser doesn\u0026#8217;t recognize and the consuming application might not work as intended. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\" id=\"block-4c1b3d70-cbce-4b4c-9d03-1019c7014449\"\u003eTradeoff: Missing data\u003c/h4\u003e\n\n\n\n\u003cp\u003eContent creators can modify attributes of blocks, and Gutenberg saves these attributes as HTML comments in the post_content. But when the content is prepared for public use in WordPress themes, the WP REST API or WPGraphQL, the raw block attributes are not available, so a parser like the one I described will not have all the block data to work with. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\" id=\"block-ebd15260-ea14-49b4-9d6f-c1e78cdcd1b3\"\u003eTradeoff: Undefined Types\u003c/h4\u003e\n\n\n\n\u003cp\u003eTo overcome the \u0026#8220;missing data\u0026#8221; issue, it\u0026#8217;s possible to pass attributes from Gutenberg blocks as HTML data-attributes in the \u003ca href=\"https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/creating-dynamic-blocks/\"\u003erender_callback\u003c/a\u003e for blocks, as a way to get Gutenberg attributes passed from the editor to the rendered HTML and available for a parser to use, but even doing this leads to client applications not knowing what to expect, and leads to undefined Types as all data-attributes are strings, so mapping data-attributes to something like a React or Vue component is difficult and fragile with this method.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eWhen to use\u003c/h4\u003e\n\n\n\n\u003cp\u003eThis approach works for me, because I personally control both sides of wpgraphql.com, what blocks are available in the WordPress install, what content is published, and the Gatsby consumer application that queries the content and renders the site. In the e-commerce analogy, I\u0026#8217;m both the person ordering the \u0026#8220;product\u0026#8221; \u003cem\u003eand\u003c/em\u003e the person fulfilling the order, so there are no surprises. I\u0026#8217;m not working with different teams, or even different team members, and I\u0026#8217;m the primary content creator.\u003c/p\u003e\n\n\n\n\u003cp\u003eFor projects that have multiple team members, multiple authors, multiple teams and/or multi-channel distribution of content, I would not recommend this approach. And multi-team, I would argue, includes the team that builds the project, \u003cem\u003eand\u003c/em\u003e the team that maintains it after it\u0026#8217;s live, which in many agencies are different teams. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eGutenberg Object Plugin\u003c/h3\u003e\n\n\n\n\u003cp\u003eIn late 2018, \u003ca href=\"https://twitter.com/royboy789\" data-type=\"URL\" data-id=\"https://twitter.com/royboy789\"\u003eRoy Sivan\u003c/a\u003e, a Senior JavaScript Engineer and recurring \u003ca href=\"https://twitter.com/search?q=%40royboy789%20happy%20birthday%20%40benUNC\u0026amp;src=typed_query\"\u003eHappy Birthday wisher to Ben Meredith\u003c/a\u003e, released a plugin that exposed Gutenberg blocks to the WP REST API: \u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter\"\u003e\u003cdiv class=\"wp-block-embed__wrapper\"\u003e\n\u003cblockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eYup, a plugin I made that takes \u003ca href=\"https://twitter.com/hashtag/Gutenberg?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#Gutenberg\u003c/a\u003e data and stores the data elegantly into the DB which is accessible via REST API directly or via normal /posts/ endpoint. \u003cbr\u003e\u003cbr\u003eBeen coming in handy for this headless React App. \u003ca href=\"https://t.co/16pAAZoM4g\"\u003ehttps://t.co/16pAAZoM4g\u003c/a\u003e\u003c/p\u003e\u0026mdash; 🟣☮ Roy (@royboy789) \u003ca href=\"https://twitter.com/royboy789/status/1047542119252938752?ref_src=twsrc%5Etfw\"\u003eOctober 3, 2018\u003c/a\u003e\u003c/blockquote\u003e\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n\u003c/div\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eThis plugin exposes Gutenberg block data to the WP REST API so that data saved to pages can be consumed as JSON objects. \u003c/p\u003e\n\n\n\n\u003cp\u003eExposing Gutenberg data as JSON is what a lot of developers building decoupled applications want. They want to take the data in a structured form, and pass the data to React / Vue / Native components. This plugin gets things headed in the right direction! \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoff: Lack of Introspection, unpredictable data\u003c/h4\u003e\n\n\n\n\u003cp\u003eBut, because of the lack of a server-side registry for Gutenberg blocks, and the non-enforced Schema of the WP REST API, this \u003ca href=\"https://github.com/royboy789/gutenberg-object-plugin\"\u003eplugin\u003c/a\u003e also suffers from the \u0026#8220;just guess\u0026#8221; pattern for decoupled applications.\u003c/p\u003e\n\n\n\n\u003cp\u003eThis plugin is unable to register blocks or fields to the WP REST API, so inspecting the Schema leaves decoupled application developers guessing. \u003c/p\u003e\n\n\n\n\u003cp\u003eIf we Introspect the REST API Schema from this plugin and we can see that the Schema doesn\u0026#8217;t provide any information to the decoupled application developer about what to expect.\u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"457\" height=\"368\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM.png\" alt=\"\" class=\"wp-image-8134\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM.png 457w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM-300x242.png 300w\" sizes=\"auto, (max-width: 457px) 100vw, 457px\" /\u003e\u003cfigcaption\u003eScreenshot of the introspection of the Gutenberg Object Plugin REST endpoint\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eIt\u0026#8217;s like ordering a \u0026#8220;product\u0026#8221; from an e-commerce store. The endpoint can return anything at any time, and can change from page to page, request to request. \u003c/p\u003e\n\n\n\n\u003cp\u003eThere\u0026#8217;s no contract between the REST endpoints and the consumer application. There\u0026#8217;s no scalable way for decoupled application developers to know what type of data the endpoints will return, now or in the future.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoff: Only available in REST\u003c/h4\u003e\n\n\n\n\u003cp\u003eIf you\u0026#8217;re building headless applications using WPGraphQL, taking advantages of \u003ca href=\"https://www.wpgraphql.com/docs/wpgraphql-vs-wp-rest-api/\" data-type=\"docs\" data-id=\"864\"\u003efeatures that differentiate WPGraphQL from REST,\u003c/a\u003e you would not be able to use the GraphQL Objects plugin in your decoupled application without enduring additional pain points, in addition to the lack of introspection.\u003c/p\u003e\n\n\n\n\u003cp\u003eCaching clients such as \u003ca href=\"https://www.apollographql.com/docs/react/\"\u003eApollo\u003c/a\u003e would have to be customized to work with data from these endpoints, and still may not work well with the rest of the application that might be using GraphQL. Additionally, when using REST endpoints with related resources, it becomes the clients responsibility to determine how to map the various block endpoint data to the components that need the data. There\u0026#8217;s no concept of coupling \u003ca href=\"https://www.wpgraphql.com/docs/intro-to-graphql/#fragments\"\u003eGraphQL Query Fragments\u003c/a\u003e with Components, like you can do with GraphQL.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eWhen to use:\u003c/h4\u003e\n\n\n\n\u003cp\u003eAgain, if you are the developer controlling both sides, the WordPress server and the client application, this approach could work, at least while you\u0026#8217;re building the application and the capabilities are fresh in your mind. But in general, this approach can cause some pain points that that might be difficult to identify and fix when things go wrong. For example, 6 months down the road, even the person that built the application will likely forget all the details, and when there\u0026#8217;s a bug, and no contract between the applications to refer to, it can be hard to diagnose and fix.\u003c/p\u003e\n\n\n\n\u003cp\u003eEven when things break with GraphQL applications (and they do), the explicit nature of GraphQL Queries serve as a \u0026#8220;documentation of intent\u0026#8221; from the original application developer and can make it much easier for teams to diagnose, down to specific leaf fields, what is broken. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eWPGraphQL for Gutenberg\u003c/h3\u003e\n\n\n\n\u003cp\u003eIn early 2019 \u003ca href=\"https://twitter.com/pristas_peter\"\u003ePeter Pristas\u003c/a\u003e introduced the \u003ca href=\"https://github.com/pristas-peter/wp-graphql-gutenberg\"\u003eWPGraphQL for Gutenberg\u003c/a\u003e plugin. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe intent of this plugin is to expose Gutenberg blocks to the WPGraphQL Schema, so that decoupled application developers could use tools such as GraphiQL to inspect what blocks were possible to be queried from the API, and compose GraphQL Queries asking for specific fields of specific blocks that they wanted to support. \u003c/p\u003e\n\n\n\n\u003cp\u003eNow, content creators can publish content with Gutenberg, and decoupled application developers can introspect the Schema and construct queries asking for the specific blocks and fields their application supports.\u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled application developers can move at their own pace, independent from the developers and content creators working on the CMS. The decoupled application can specify which blocks are supported, and ask for the exact fields they need. Much like an e-commerce consumer can specify the specific color door lock they want to order from the store! The Schema serves as the contract between the server and the client. Clients can predictably ask for what they want, and get just that in response. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eCreating a page\u003c/h4\u003e\n\n\n\n\u003cp\u003eContent creators can use Gutenberg to create pages. In the example blow, we see a page in Gutenberg with a Paragraph block and an Image block. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"468\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1024x468.png\" alt=\"Screenshot showing the Gutenberg editor with a paragraph and image block.\" class=\"wp-image-8128\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1024x468.png 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-300x137.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-768x351.png 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1536x702.png 1536w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1568x717.png 1568w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM.png 1918w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" /\u003e\u003cfigcaption\u003eScreenshot showing the Gutenberg editor with a paragraph and image block.\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eExploring the Schema\u003c/h4\u003e\n\n\n\n\u003cp\u003eWith the plugin installed and activated (for demo sake I have \u003ca href=\"https://github.com/wp-graphql/wp-graphql/releases/tag/v1.2.5\"\u003eWPGraphQL v1.2.5\u003c/a\u003e and \u003ca href=\"https://github.com/pristas-peter/wp-graphql-gutenberg/releases/tag/v0.3.8\"\u003eWPGraphQL for Gutenberg v0.3.8\u003c/a\u003e active), decoupled application developers can use GraphiQL to browse the Schema to see what Gutenberg Blocks are available to query and interact with. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex\"\u003e\n\u003cdiv class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"\u003e\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM.png\" alt=\"\" class=\"wp-image-8122\" width=\"356\" height=\"813\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM.png 356w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM-131x300.png 131w\" sizes=\"auto, (max-width: 356px) 100vw, 356px\" /\u003e\u003cfigcaption\u003eScreenshot of GraphiQL showing Gutenberg Blocks\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\u003c/div\u003e\n\n\n\n\u003cdiv class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"\u003e\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM.png\" alt=\"\" class=\"wp-image-8120\" width=\"362\" height=\"812\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM.png 361w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM-134x300.png 134w\" sizes=\"auto, (max-width: 362px) 100vw, 362px\" /\u003e\u003cfigcaption\u003eScreenshot of GraphiQL showing the CoreParagraphBlock and its fields\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eQuerying the blocks\u003c/h4\u003e\n\n\n\n\u003cp\u003eAnd using the Schema, developers can construct a query to ask for the blocks and fields that their application supports.\u003c/p\u003e\n\n\n\n\u003cp\u003e\u003cstrong\u003eHere\u0026#8217;s an example query:\u003c/strong\u003e\u003c/p\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-graphql\"\u003e\u003ccode\u003e{\n post(id: 6, idType: DATABASE_ID) {\n id\n databaseId\n title\n blocks {\n __typename\n name\n ... on CoreImageBlock {\n attributes {\n ... on CoreImageBlockAttributes {\n url\n alt\n caption\n }\n }\n }\n ... on CoreParagraphBlock {\n attributes {\n ... on CoreParagraphBlockAttributes {\n content\n }\n }\n }\n }\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cp\u003e\u003cstrong\u003eAnd the response:\u003c/strong\u003e\u003c/p\u003e\n\n\n\n\u003cp\u003eYou can see that the response includes the exact fields that were asked for. No surprises. \u003c/p\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-json\"\u003e\u003ccode\u003e{\n \"data\": {\n \"post\": {\n \"id\": \"cG9zdDo2\",\n \"databaseId\": 6,\n \"title\": \"Test Gutenberg Post\",\n \"blocks\": [\n {\n \"__typename\": \"CoreParagraphBlock\",\n \"name\": \"core/paragraph\",\n \"attributes\": {\n \"content\": \"This is a paragraph\"\n }\n },\n {\n \"__typename\": \"CoreImageBlock\",\n \"name\": \"core/image\",\n \"attributes\": {\n \"url\": \"http://wpgraphql.local/wp-content/uploads/2021/03/Screen-Shot-2021-03-04-at-12.11.53-PM-1024x490.png\",\n \"alt\": \"Jason Bahl, dressed in character as JamStackMullet with a Mullet wig and sunglasses, watches the WP Engine Decode conference\",\n \"caption\": \"Screenshot of the JamStackMullet watching WP Engine Decode conference\"\n }\n }\n }\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"555\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-1024x555.png\" alt=\"\" class=\"wp-image-8132\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-1024x555.png 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-300x163.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-768x416.png 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM.png 1376w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" /\u003e\u003cfigcaption\u003eScreenshot of a query for a post and some blocks using WPGraphQL for Gutenberg.\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eGraphQL Schema as the contract\u003c/h4\u003e\n\n\n\n\u003cp\u003eHaving the GraphQL Schema serve as the contract between the client and server allows each part of the application to move forward at its own pace. There\u0026#8217;s now an agreement for how things will behave. If the contract is broken, for example, if the server changed the shape of one of the Types in the GraphQL Schema, it\u0026#8217;s easily identifiable and can be fixed quickly, because the client specified exactly what was needed from the server by way of a GraphQL Query. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis removes the \u0026#8220;just guess\u0026#8221; pattern from decoupled application development with Gutenberg.\u003c/p\u003e\n\n\n\n\u003cp\u003eTeams that know nothing about WordPress can even make use of the data. For example, a data warehouse team, a native mobile team, a print team, etc. The GraphQL Schema and tooling such as GraphiQL frees up different teams to use the data in their applications how they want.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eClient in control\u003c/h4\u003e\n\n\n\n\u003cp\u003eWith clients querying Gutenberg blocks as data, this gives clients full control over the presentation of the blocks. Whether the blocks are used in a React or Vue website, or used for a Native iOS app that doesn\u0026#8217;t render HTML, or used to prepare a newspaper for print, the client gets to ask for the fields that it needs, and gets to decide what happens with the data. No unexpected changes from the server, the client is in control.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoffs: Scaling issues\u003c/h4\u003e\n\n\n\n\u003cp\u003eWhile WPGraphQL for Gutenberg gets us much closer to being able to query Gutenberg blocks as data, it unfortunately has a dependency that makes it very difficult to scale, and it comes back, again, to the lack of a proper server side registry for blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eSince Gutenberg Blocks aren\u0026#8217;t registered on the server, WPGraphQL for Gutenberg has a settings page where users must click a button to \u0026#8220;Update the Block Registry\u0026#8221;. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"848\" height=\"128\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM.png\" alt=\"\" class=\"wp-image-8136\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM.png 848w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM-300x45.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM-768x116.png 768w\" sizes=\"auto, (max-width: 848px) 100vw, 848px\" /\u003e\u003cfigcaption\u003eScreenshot of the WPGraphQL for Gutenberg settings page\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eClicking this button opens up Gutenberg in a hidden iFrame, executes the JavaScript to instantiate Gutenberg, gets the Block Registry from Gutenberg initialized in JavaScript, sends the list of registered blocks to the server and stores the registry in the options table of the WordPress database. The registered blocks that are stored in the database are then used to map to the GraphQL Schema.\u003c/p\u003e\n\n\n\n\u003cp\u003ePeter Pristas deservers an award, because this approach is a very creative solution to the frustrating problem of Gutenberg not respecting the WordPress server. \u003c/p\u003e\n\n\n\n\u003cp\u003eUnfortunately this solution doesn\u0026#8217;t scale well. \u003cbr\u003e\u003cbr\u003eSince Gutenberg blocks are registered in JavaScript, this means that the JavaScript to register any given block might be enqueued from WordPress on only specific pages, specific post types, or other unique individualized criteria. \u003c/p\u003e\n\n\n\n\u003cp\u003eThat means the JavaScript Block Registry for Page A and Page B might be different from each other, and maybe also different from the registry for Post Type C or Post Type D. So loading one page in an iframe to get the block registry might not get the full picture of what blocks are \u003cem\u003epossible\u003c/em\u003e to interact with in a decoupled application. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn order for the block registry that is generated from the iframe to be accurate, \u003cem\u003eevery\u003c/em\u003e page of every post type that Gutenberg is enabled on in the site would need to be loaded by iframe to account for cases where blocks were registered to specific contexts. Yikes!\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradoffs: Schema design issues\u003c/h4\u003e\n\n\n\n\u003cp\u003eIn addition to the scaling issues, there are some concerns with some of the Schema design choices, and I\u0026#8217;ll even take the blame for some of this, as I had many conversations with Peter as he worked on the plugin, and he followed my lead with some of my also poor Schema design choices. \u003c/p\u003e\n\n\n\n\u003cp\u003eOne issue is infinite nesting. Gutenberg blocks, as previously discussed, can sometimes have nested inner blocks. In WPGraphQL for Gutenberg, querying inner blocks requires explicit queries and without knowing what level of depth inner blocks might reach, it\u0026#8217;s difficult to compose queries that properly return all inner blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eWPGraphQL used to expose hierarchical data in a similar way, but has since changed to expose hierarchical data, such as Nav Menu Items, in flat lists. This allows for nested data in any depth to be queried, and \u003ca href=\"https://www.wpgraphql.com/docs/menus/#hierarchical-data\"\u003ere-structured in a hierarchy on the client\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe unlimited depth issue is commonly reported for projects such as \u003ca href=\"https://github.com/gatsbyjs/gatsby-source-wordpress-experimental/issues?q=is%3Aissue+gutenberg+is%3Aclosed\"\u003eGatsby Source WordPress\u003c/a\u003e. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eWhen to use\u003c/h4\u003e\n\n\n\n\u003cp\u003eIf Gutenberg is a requirement for your headless project, this might be a good option, as it allows you to query Gutenberg blocks as structured data. You gain a lot of the predictability that you miss with the other options, and can benefit greatly from features of GraphQL such as \u003ca href=\"https://www.wpgraphql.com/docs/wpgraphql-vs-wp-rest-api/#batch-queries\"\u003eBatch Queries\u003c/a\u003e, \u003ca href=\"https://www.wpgraphql.com/docs/intro-to-graphql/#fragments\"\u003ecoupling Fragments with components\u003c/a\u003e, and more.\u003c/p\u003e\n\n\n\n\u003cp\u003eSo while WPGraphQL for Gutenberg is probably the closest option available for being able to predictably query Gutenberg blocks as data in decoupled applications, there are some serious questions in regards to production readiness, \u003cem\u003eespecially\u003c/em\u003e on larger projects, and you should consider these issues before choosing it for your next project.\u003c/p\u003e\n\n\n\n\u003cp\u003eTradeoffs in mind, agencies such as WebDevStudios are \u003ca href=\"https://webdevstudios.com/2021/03/09/next-js-headless-wordpress/\" target=\"_blank\" rel=\"noreferrer noopener\"\u003eusing this approach in production\u003c/a\u003e, even for large sites.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eProgress for the server side block registry\u003c/h2\u003e\n\n\n\n\u003cp\u003eIn 2020, some progress was made in regards to a server side registry for Gutenberg blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eWhile the \u003ca href=\"https://developer.wordpress.org/block-editor/tutorials/block-tutorial/writing-your-first-block-type/#enqueuing-block-scripts\"\u003eofficial Gutenberg documentation\u003c/a\u003e still shows developers how to create new blocks fully JavaScript with no server awareness, the core Gutenberg blocks have started transitioning to have \u003cem\u003esome\u003c/em\u003e data registered on the server.\u003c/p\u003e\n\n\n\n\u003cp\u003eYou can \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/WordPress/tree/master/wp-includes/blocks\"\u003esee here\u003c/a\u003e that (as of Gutenberg 5.6.2, released in February 2021) core Gutenberg blocks are now registered with JSON files that can be used by the PHP server as well as the JavaScript client. \u003c/p\u003e\n\n\n\n\u003cp\u003eThese JSON files are now used to expose blocks to the WP REST API.\u003c/p\u003e\n\n\n\n\u003cp\u003e This is progress! \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eInner blocks, inner peace?\u003c/h3\u003e\n\n\n\n\u003cp\u003eUnfortunately it\u0026#8217;s not all the progress needed to have meaningful impact for decoupled applications to use Gutenberg. There\u0026#8217;s a lot of information that a decoupled application would need about blocks that is not described in the server registry. One example (of many) being inner blocks.\u003c/p\u003e\n\n\n\n\u003cp\u003eGutenberg has a concept called \u0026#8220;Inner Blocks\u0026#8221;, which is blocks that can have other blocks nested within. For example, a \u0026#8220;Column\u0026#8221; block can have other blocks nested within each column, while other blocks such as an Image block cannot have nested inner blocks.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe bit of server side registry that is now available for core Gutenberg blocks doesn\u0026#8217;t declare this information. If we take a look at the Column block\u0026#8217;s \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/WordPress/blob/5.6.2/wp-includes/blocks/column/block.json\"\u003eblock.json\u003c/a\u003e file, we can see there\u0026#8217;s no mention of inner blocks being supported. Additionally, if. we look at the Image block\u0026#8217;s \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/WordPress/blob/5.6.2/wp-includes/blocks/image/block.json\"\u003eblock.json\u003c/a\u003e file, we don\u0026#8217;t see any mention of inner blocks \u003cem\u003enot\u003c/em\u003e being supported. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn order for a decoupled application, such as the official WordPress iOS app, to know what blocks can or cannot have inner blocks, this information needs to be exposed to an API that the decoupled application can read. Without the server knowing about this information, decoupled applications cannot know this information either.\u003c/p\u003e\n\n\n\n\u003cp\u003eSo, while there\u0026#8217;s been a bit of a migration for the core WordPress blocks to have some server (and REST API) awareness, there\u0026#8217;s still a lot of missing information. Also the community of 3rd party block developers are still being directed to build blocks entirely in JavaScript, which means that all new blocks will have no server awareness until the server registry becomes more of a 1st-class citizen for Gutenberg.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eWhat\u0026#8217;s next?\u003c/h2\u003e\n\n\n\n\u003cp\u003eThe beginnings of a move toward a server-side registry gives hope, and gives a bit of a path toward blocks being properly introspect-able and useful by decoupled teams and applications.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eSpecification for Server Side Registering Blocks\u003c/h3\u003e\n\n\n\n\u003cp\u003eI believe that the step forward for Gutenberg + decoupled applications, is to come up with a specification for how Gutenberg blocks can be registered on the server to work properly with server APIs. \u003c/p\u003e\n\n\n\n\u003cp\u003eOnce a specification is discussed, vetted, tested and published, the WP REST API, WP CLI and WPGraphQL, and therefore decoupled applications such as the WordPress native mobile app, would all make use of the spec to be able to interact with Gutenberg blocks.\u003c/p\u003e\n\n\n\n\u003cp\u003e\u003cem\u003eI don\u0026#8217;t fully know what this spec needs to look like, but I believe it needs to exist in some form.\u003c/em\u003e\u003c/p\u003e\n\n\n\n\u003cp\u003eProjects such as \u003ca href=\"https://github.com/rtCamp/gutenberg-fields-middleware\"\u003eGutenberg Fields Middleware\u003c/a\u003e from \u003ca href=\"https://rtcamp.com/\"\u003ertCamp\u003c/a\u003e, \u003ca href=\"https://www.advancedcustomfields.com/resources/blocks/\"\u003eACF Blocks\u003c/a\u003e, and \u003ca href=\"https://wordpress.org/plugins/genesis-custom-blocks/\"\u003eGenesis Custom Blocks\u003c/a\u003e all take a server-first approach to creating new Gutenberg blocks, and I think there\u0026#8217;s a lot to learn from these projects. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe blocks from these tools are created in a way that the WordPress server knows what blocks exist, what attributes and fields the blocks have, and the server can then pass the description of the blocks to the Gutenberg JavaScript application, which then renders the blocks for users to interact with. \u003c/p\u003e\n\n\n\n\u003cp\u003eSince the server provides the Gutenberg JavaScript application with the information needed to render the blocks to a content producer, this means the server can \u003cem\u003ealso\u003c/em\u003e provide the information to other clients, such as the native mobile WordPress app, or teams building decoupled front-ends with Gatsby, Gridsome or NextJS.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eThe future of decoupled Gutenberg\u003c/h3\u003e\n\n\n\n\u003cp\u003eI believe that with a proper specification for registering blocks on the server, Gutenberg can enable some incredibly powerful integrations across the web.\u003c/p\u003e\n\n\n\n\u003cp\u003eMy thoughts are that we can arrive at a specification for registering blocks that can enable block developers to provide pleasant editing experiences, while providing decoupled application developers with the ability to Introspect the GraphQL API, predictably write GraphQL Queries (and Mutations) to interact with blocks, and get predictable, strongly typed results that can be used in decoupled applications.\u003c/p\u003e\n\n\n\n\u003cp\u003eIn an effort to start discussing what the future of a Gutenberg Block Server Registry Specification like this might look like, I\u0026#8217;ve opened the following Github issue: \u003ca rel=\"noreferrer noopener\" href=\"https://github.com/wp-graphql/wp-graphql/issues/1764\" target=\"_blank\"\u003ehttps://github.com/wp-graphql/wp-graphql/issues/1764\u003c/a\u003e\u003c/p\u003e\n\n\n\n\u003cp\u003eIf this topic interests you, and you\u0026#8217;d like to be involved in discussing what such a specification might look like, please contribute ideas to that issue. Additionally, you can \u003ca href=\"https://join.slack.com/t/wp-graphql/shared_invite/zt-3vloo60z-PpJV2PFIwEathWDOxCTTLA\"\u003eJoin the WPGraphQL community on Slack\u003c/a\u003e community, and visit the \u003ccode\u003e#gutenberg\u003c/code\u003e channel and discuss in there.\u003c/p\u003e\n","author":{"__typename":"NodeWithAuthorToUserConnectionEdge","node":{"__typename":"User","name":"Jason Bahl","uri":"/author/jasonbahl/","avatar":{"__typename":"Avatar","url":"https://secure.gravatar.com/avatar/94bf4ea789246f76c48bcf8509bcf01e?s=96\u0026d=mm\u0026r=g"}}},"categories":{"__typename":"PostToCategoryConnection","nodes":[{"__typename":"Category","id":"dGVybTo3Njk0Nzk=","name":"Gutenberg","uri":"/category/gutenberg/"},{"__typename":"Category","id":"dGVybTo3Njk0NzQ=","name":"WordPress","uri":"/category/wordpress/"}]}},"menu":{"__typename":"Menu","id":"dGVybTozMA==","name":"Primary Nav","menuItems":{"__typename":"MenuToMenuItemConnection","nodes":[{"__typename":"MenuItem","id":"cG9zdDoyNjE3","label":"Docs","description":"Learn how to use WPGraphQL to build headless apps","url":"/docs/introduction/","target":null,"path":"/docs/introduction/","parentId":null,"cssClasses":["icon-BookOpenIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDI1","label":"Getting Started","description":"Get up and running with WPGraphQL.","url":"/docs/introduction/","target":null,"path":"/docs/introduction/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-BookOpenIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDI0","label":"Beginner Guides","description":"Learn the basics of GraphQL and using it with WordPress","url":"/docs/intro-to-graphql/","target":null,"path":"/docs/intro-to-graphql/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-BookOpenIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDI2","label":"Using WPGraphQL","description":"Learn the details of using WPGraphQL","url":"/docs/posts-and-pages/","target":null,"path":"/docs/posts-and-pages/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-ChartBarIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDI3","label":"Advanced Concepts","description":"Dig deeper into WPGraphQL’s features","url":"/docs/wpgraphql-concepts/","target":null,"path":"/docs/wpgraphql-concepts/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-ShieldCheckIcon"]},{"__typename":"MenuItem","id":"cG9zdDoyNjI0","label":"Developer Reference","description":"Learn how to extend WPGraphQL on the server","url":"/developer-reference/","target":null,"path":"/developer-reference/","parentId":null,"cssClasses":["icon-Bars4Icon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDM3","label":"Recipes","description":"Helpful Snippets to help developers extend or modify WPGraphQL","url":"/recipes","target":null,"path":"/recipes","parentId":"cG9zdDoyNjI0","cssClasses":["icon-CommandLineIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDQw","label":"Functions","description":"Helpful functions for developers interacting with WPGraphQL","url":"/functions","target":null,"path":"/functions","parentId":"cG9zdDoyNjI0","cssClasses":["icon-CodeBracketIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDM4","label":"Actions","description":"WordPress actions you can use to hook into WPGraphQL","url":"/actions","target":null,"path":"/actions","parentId":"cG9zdDoyNjI0","cssClasses":["icon-BoltIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4NDM5","label":"Filters","description":"WordPress filters you can use to modify WPGraphQL","url":"/filters","target":null,"path":"/filters","parentId":"cG9zdDoyNjI0","cssClasses":["icon-FunnelIcon"]},{"__typename":"MenuItem","id":"cG9zdDoyNjIw","label":"Extensions","description":"Find extensions that add functionality to WPGrapQL","url":"/extensions","target":null,"path":"/extensions","parentId":null,"cssClasses":["icon-PuzzlePieceIcon"]},{"__typename":"MenuItem","id":"cG9zdDo4MzMzNzM=","label":"Blog","description":null,"url":"/blog","target":null,"path":"/blog","parentId":null,"cssClasses":[]},{"__typename":"MenuItem","id":"cG9zdDo4MzMyMzA=","label":"REPL","description":"WPGraphQL REPL","url":"https://repl.wpgraphql.com/","target":"_blank","path":"https://repl.wpgraphql.com/","parentId":null,"cssClasses":[]}]}},"__typename":"RootQuery"},"__TEMPLATE_VARIABLES__":{"uri":"/2021/03/09/gutenberg-and-decoupled-applications/"},"__FAUST_QUERIES__":null,"__APOLLO_STATE__":{"Post:cG9zdDo4MDU4":{"__typename":"Post","uri":"/2021/03/09/gutenberg-and-decoupled-applications/","id":"cG9zdDo4MDU4","databaseId":8058,"isContentNode":true,"slug":"gutenberg-and-decoupled-applications","contentType":{"__typename":"ContentNodeToContentTypeConnectionEdge","node":{"__typename":"ContentType","name":"post"}},"template":{"__typename":"DefaultTemplate","templateName":"Default"},"title":"Gutenberg and Decoupled Applications","date":"2021-03-09T14:31:03","content":"\n\u003cp\u003eIn this article I want to dive into the current state of Gutenberg and WPGraphQL. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis is a technical article about using Gutenberg blocks in the context of decoupled / headless / API-driven WordPress, and makes the assumption that you already know what Gutenberg is and have some general understanding of how it works.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eTL;DR\u003c/h2\u003e\n\n\n\n\u003cp\u003eClient-server contracts around the shape of data is fundamental to achieving \u0026#8220;separation of concerns\u0026#8221;, a pillar of modular and decoupled application development. \u003c/p\u003e\n\n\n\n\u003cp\u003eWhile much of WordPress was built with \u003ca href=\"#wordpress-contracts\" data-type=\"internal\" data-id=\"#wordpress-contracts\"\u003edecoupling in mind\u003c/a\u003e, the \u003ca href=\"#the-wp-rest-api-provides-a-schema\"\u003eWP REST API\u003c/a\u003e and \u003ca href=\"#the-gutenberg-block-registry\"\u003eGutenberg were not\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eAs a result, decoupled application developers interacting with WordPress are \u003ca href=\"#challenges-and-the-current-state-of-gutenberg\"\u003elimited in what they can achieve\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003e With the \u003ca href=\"https://wpengine.com/headless-cms-research/\"\u003egrowing demand for headless WordPress\u003c/a\u003e, this is a key limitation that will hamper growth.\u003c/p\u003e\n\n\n\n\u003cp\u003eFortunately, even with \u003ca href=\"#challenges-and-the-current-state-of-gutenberg\"\u003ethe limitations\u003c/a\u003e, there are ways forward. In this article \u003ca href=\"#three-approaches-to-using-gutenberg-in-decoupled-applications-today\"\u003eI walk through 3 approaches\u003c/a\u003e you can implement to use Gutenberg in decoupled applications today, tradeoffs included, and \u003ca href=\"#whats-next\"\u003epropose a plan\u003c/a\u003e to make the future of Gutenberg for decoupled applications a better one.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\" id=\"replacing-my-door-lock\"\u003eReplacing my door lock\u003c/h2\u003e\n\n\n\n\u003cp\u003eI recently replaced the lock on the front door of my house.\u003c/p\u003e\n\n\n\n\u003cp\u003eI ordered the lock from an online retailer. I was able to select a specific brand of lock in a specific color. \u003c/p\u003e\n\n\n\n\u003cp\u003eWhen the lock arrived and I opened the package, it was the same brand and color that I ordered. It wasn\u0026#8217;t just any random lock, it was the one that I agreed to pay for, and the online retailer agreed to mail me. \u003c/p\u003e\n\n\n\n\u003cp\u003eI was able install the lock without any surprises. I didn\u0026#8217;t have to drill any new holes in my door. The new lock fit the hole in my door that I removed the old lock from. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe new lock wasn\u0026#8217;t made by the same manufacturer that made the door, and yet, the lock installed on my door just fine. In fact, there were \u003cem\u003eat least\u003c/em\u003e 30 different locks from a variety of manufacturers that I could have selected that all would have worked in my door without any complications. \u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eDecoupled systems\u003c/h2\u003e\n\n\n\n\u003cp\u003eThis wasn\u0026#8217;t \u003cem\u003ereally\u003c/em\u003e a story about doors and locks. It\u0026#8217;s a story about decoupled systems, and the contracts, or agreements, that make them work. \u003c/p\u003e\n\n\n\n\u003cp\u003eAnd its intent is to help frame what I\u0026#8217;m talking about with using WordPress, and specifically Gutenberg, in decoupled contexts. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn order for decoupled systems to work well, whether it\u0026#8217;s doors and door locks, or WordPress and a decoupled JavaScript application, there needs to be some sort of agreement between the different parts of the system.\u003c/p\u003e\n\n\n\n\u003cp\u003eIn the case of door and lock manufacturers, it\u0026#8217;s an agreement over the size and positioning of the holes in the door.\u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-1024x890.jpg\" alt=\"\" class=\"wp-image-8165\" width=\"512\" height=\"445\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-1024x890.jpg 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-300x261.jpg 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram-768x668.jpg 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/door-lock-diagram.jpg 1200w\" sizes=\"auto, (max-width: 512px) 100vw, 512px\" /\u003e\u003cfigcaption\u003eDiagram showing measurements for a door lock hole \u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eDoor manufacturers can build doors at their leisure and lock manufacturers at theirs, and when the time comes to bring them together, they work without issue because both parties are adhering to an agreement.\u003c/p\u003e\n\n\n\n\u003cp\u003eIn the case of e-commerce, there are agreements about what a consumer purchases and what should be delivered. In my case, the online store provided a list of locks that were available to purchase. I selected a specific lock, paid for it, and in response I received the lock we agreed to, in exchange for my payment. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eDecoupled tech, decoupled teams\u003c/h3\u003e\n\n\n\n\u003cp\u003eWhen WPGraphQL first started, I was working at a newspaper that had a CMS team that focused on WordPress, a Native Mobile team that focused on the iOS and Android applications, a Data Warehouse team that collected various data from the organization and a Print team that took the data from WordPress and prepared it for Print.\u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress was the entry point for content creators to write content, but the web was only one of many channels where content was being used. \u003c/p\u003e\n\n\n\n\u003cp\u003eNot only was the technology decoupled (PHP for the CMS, React Native for mobile apps, Python for Data warehousing and some legacy system I forget the name of for print), but the teams were also decoupled.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe only team that really needed to understand WordPress was the CMS team. The other teams were able to use WPGraphQL Schema Introspection to build tools for their teams using data from WordPress, without needing to understand anything about PHP or WordPress under the hood. \u003c/p\u003e\n\n\n\n\u003cp\u003eMuch like door and lock manufacturers don\u0026#8217;t need to be experts at what the other is building, WPGraphQL\u0026#8217;s schema served as the contract, enabling many different teams to use WordPress data when, and how, they needed. \u003c/p\u003e\n\n\n\n\u003cp\u003eWPGraphQL served as the contract between the CMS team and the other teams as well as WordPress the system and the other team\u0026#8217;s decoupled systems.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eWordPress contracts\u003c/h2\u003e\n\n\n\n\u003cp\u003eFor WordPress, one of the common contracts, or agreements established between multiple systems (such as plugins, themes, and WordPress core) comes in the form of registries.\u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress has registries for Post Types, Taxonomies, Settings, Meta and more. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u003ca href=\"https://developer.wordpress.org/reference/functions/register_post_type/\"\u003eregister_post_type\u003c/a\u003e function has more than 30 options that can be configured to define the contract between the Post Type existing and how WordPress core and decoupled systems (namely plugins and themes) should interact with it. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u003ca href=\"https://www.google.com/url?client=internal-element-cse\u0026amp;cx=012566942813864066925:bnbfebp99hs\u0026amp;q=https://developer.wordpress.org/reference/functions/register_taxonomy/\u0026amp;sa=U\u0026amp;ved=2ahUKEwi0o7DeqZXvAhXaHc0KHTbtBe0QFjAAegQIABAB\u0026amp;usg=AOvVaw2n2TUd-jKjirfxq2j5y_Wm\"\u003eregister_taxonomy\u003c/a\u003e, \u003ca href=\"https://developer.wordpress.org/reference/functions/register_meta/\"\u003eregister_meta\u003c/a\u003e, \u003ca href=\"https://developer.wordpress.org/reference/functions/register_setting/\"\u003eregister_setting\u003c/a\u003e, \u003ca href=\"https://developer.wordpress.org/reference/functions/register_sidebar/\"\u003eregister_sidebar\u003c/a\u003e and other \u003ca href=\"https://wordpress.org/search/register_?in=developer_documentation\"\u003eregister_*\u003c/a\u003e functions in WordPress serve a similar purpose. They allow for a contract to be established so that many different systems can work with WordPress in an agreed upon way.\u003c/p\u003e\n\n\n\n\u003cp\u003eThese registries serve as a contract between WordPress core and decoupled systems (themes and plugins) that can work with WordPress. Because these registries establish an agreement with how WordPress core will behave, plugins and themes can latch onto these registries and extend WordPress core in some powerful ways.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe decoupled (pluggable) architecture of WordPress is enabled by these contracts. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM.png\" alt=\"Image showing WordPress in the middle with the logos for ElasticPress, WordPress SEO by Yoast, WPGraphQL and Advanced Custom Fields around it.\" class=\"wp-image-8171\" width=\"600\" height=\"528\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM.png 857w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM-300x264.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.07.25-PM-768x676.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" /\u003e\u003cfigcaption\u003eWordPress registries enable plugins to iterate outside of WordPress core\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eRegistering a new post type to WordPress can get you a UI in the WordPress dashboard, but it can also get your content indexed to Elastic Search via \u003ca href=\"https://wordpress.org/plugins/elasticpress/\"\u003eElasticPress\u003c/a\u003e, powerful SEO tools from \u003ca href=\"https://wordpress.org/plugins/wordpress-seo/\"\u003eWordPress SEO\u003c/a\u003e, custom admin functionality from \u003ca href=\"https://wordpress.org/plugins/advanced-custom-fields/\"\u003eAdvanced Custom Fields\u003c/a\u003e, and API access via \u003ca href=\"https://wordpress.org/plugins/wp-graphql/\"\u003eWPGraphQL\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eIf the next release of WordPress started hiding the UI for all post types that were registered with \u003ccode\u003eshow_ui =\u0026gt; true\u003c/code\u003e, or stopped allowing plugins from reading the post type registry, there would likely be a bug (or hundreds) reported on \u003ca href=\"https://core.trac.wordpress.org/\"\u003eTrac\u003c/a\u003e, (and Twitter, and Slack, etc), as that would mean WordPress was breaking the established contract. \u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eThe client/server contract\u003c/h2\u003e\n\n\n\n\u003cp\u003eLike we discussed earlier, decoupled systems need some sort of shared agreement in order to work well together. It doesn\u0026#8217;t have to be a GraphQL API, but it has to be \u003cem\u003esomething.\u003c/em\u003e\u003cbr\u003e\u003cbr\u003eFor WordPress, this comes in the form of APIs. \u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress core has 2 built-in APIs that enable decoupled applications to interact with WordPress data, \u003ca href=\"https://codex.wordpress.org/XML-RPC_WordPress_API\"\u003eXML-RPC\u003c/a\u003e and the \u003ca href=\"https://developer.wordpress.org/rest-api/\"\u003eWP REST API\u003c/a\u003e. \u003c/p\u003e\n\n\n\n\u003cp\u003eAnd, of course, there\u0026#8217;s yours truly, \u003ca href=\"https://wordpress.org/plugins/wp-graphql/\"\u003eWPGraphQL\u003c/a\u003e, a free open-source WordPress plugin that provides an extendable GraphQL schema and API for any WordPress site.\u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-image size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM.png\" alt=\"Blocks representing REST, GraphQL and RPC API on top of a block representing the Authorization and Business logic layers of WordPress, and at the bottom is a block representing the Persistence Layer (MySQL).\" class=\"wp-image-8176\" width=\"537\" height=\"493\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM.png 592w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-4.18.39-PM-300x276.png 300w\" sizes=\"auto, (max-width: 537px) 100vw, 537px\" /\u003e\u003cfigcaption\u003eDiagram of the WordPress server + API setup\u003c/figcaption\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eIn order for decoupled applications, such as \u003ca href=\"https://www.gatsbyjs.com/plugins/gatsby-source-wordpress/\"\u003eGatsby\u003c/a\u003e, \u003ca href=\"https://github.com/WebDevStudios/nextjs-wordpress-starter\"\u003eNextJS\u003c/a\u003e, \u003ca href=\"https://frontity.org/\"\u003eFrontity\u003c/a\u003e, native mobile applications or others, to work with WordPress, the APIs must establish a contract that WordPress and the decoupled application can both work against.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eThe WP REST API provides a Schema\u003c/h3\u003e\n\n\n\n\u003cp\u003eThe WordPress REST API provides a Schema that acts as this contract. The Schema is introspect-able, allowing remote systems to see what\u0026#8217;s available before asking for the data. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis is a good thing! \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eBut the Schema is not enforced\u003c/h3\u003e\n\n\n\n\u003cp\u003eHowever, the WP REST API doesn\u0026#8217;t \u003cem\u003eenforce\u003c/em\u003e the Schema. \u003c/p\u003e\n\n\n\n\u003cp\u003eWordPress plugins that extend the WP REST API Schema can add fields to the API without defining what data will be returned in the REST API Schema. Or, they can register fields that return \u0026#8220;object\u0026#8221; as a wildcard catch-all. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis is a bad thing! \u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled teams and applications cannot reliably use the WordPress REST API if it doesn\u0026#8217;t enforce any type of contract. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eOptional Schema and wildcard return types\u003c/h4\u003e\n\n\n\n\u003cp\u003ePlugins such as the \u003ca href=\"https://wordpress.org/plugins/acf-to-rest-api/\"\u003eAdvanced Custom Fields to REST API\u003c/a\u003e add a single \u0026#8220;acf\u0026#8221; field to the REST endpoints and declare in the WP REST API that the field will return \u0026#8220;an object\u0026#8221;. \u003c/p\u003e\n\n\n\n\u003cp\u003eWe can see this if we introspect the WP REST API of a WordPress install with this plugin active: \u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-image size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"459\" height=\"456\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection.png\" alt=\"\" class=\"wp-image-8107\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection.png 459w, https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection-300x298.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/acf-to-rest-api-introspection-150x150.png 150w\" sizes=\"auto, (max-width: 459px) 100vw, 459px\" /\u003e\u003cfigcaption\u003eScreenshot showing the Introspection of the ACF to REST API Schema definition\u003c/figcaption\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eThis means that decoupled applications, and the teams building them, have no way to predict what data this field will ever return. This also means that even if a decoupled application does manage to get built, it could break at any time, because there\u0026#8217;s no contract agreed to between the client and the server. The WordPress server can return \u003cem\u003eanything\u003c/em\u003e at \u003cem\u003eanytime\u003c/em\u003e.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eUnpredictable data is frustrating for API consumers\u003c/h4\u003e\n\n\n\n\u003cp\u003eWith the field defined as \u0026#8220;object\u0026#8221; the data returned can be different from page to page, post to post, user to user, and so on. There\u0026#8217;s no predictable way decoupled application developers can prepare for the data the API will return. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis would be like me trying to purchase that door lock, but instead of the website showing me a list of door locks with specific colors to chose from, I was just given one \u0026#8220;product\u0026#8221; as the option to purchase.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u0026#8220;product\u0026#8221; might be a hat or some new sunglasses, or if I\u0026#8217;m really lucky, it might be a door lock. I don\u0026#8217;t have any way of knowing what the \u0026#8220;product\u0026#8221; is, until I receive it. \u003c/p\u003e\n\n\n\n\u003cp\u003eAs an e-commerce consumer, this is not helpful. \u003c/p\u003e\n\n\n\n\u003cp\u003eAnd as a decoupled application developer, this type of API is frustrating.\u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled systems don\u0026#8217;t work well if part of the equation is to \u0026#8220;just guess\u0026#8221;.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eGraphQL enforces Schema and Strong Types\u003c/h3\u003e\n\n\n\n\u003cp\u003eWPGraphQL, on the other hand, \u003cem\u003eenforces\u003c/em\u003e a strongly Typed Schema. There is no option to extend the WPGraphQL API without describing the type of data the API will return. Additionally, there is no \u0026#8220;wildcard\u0026#8221; type. \u003c/p\u003e\n\n\n\n\u003cp\u003eA plugin cannot register a field to the WPGraphQL Schema that returns a door lock on one request, and sunglasses or a hat on the next request.\u003c/p\u003e\n\n\n\n\u003cp\u003eTo extend WPGraphQL, plugins must register fields that declare a \u003cem\u003especific\u003c/em\u003e Type of data that will be returned. And this contract must be upheld. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis removes the \u0026#8220;just guess\u0026#8221; part of the equation. \u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled application developers always know what to expect.\u003c/p\u003e\n\n\n\n\u003cp\u003eMuch like I, as an e-commerce consumer, was able to browse the list of door locks that were possible to purchase on the online store, decoupled application developers can use a tool such as GraphiQL to browse the GraphQL Schema and see what Types and Fields are available to query from the GraphQL API. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe screenshot below shows GraphiQL being used to explore a GraphQL Schema. The screenshot shows the type named \u0026#8220;Post\u0026#8221; in the GraphQL Schema with a field named \u0026#8220;slug\u0026#8221; which declares that it will return a String.\u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-image size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"356\" height=\"176\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM.png\" alt=\"\" class=\"wp-image-8087\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM.png 356w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-03-at-12.19.17-AM-300x148.png 300w\" sizes=\"auto, (max-width: 356px) 100vw, 356px\" /\u003e\u003cfigcaption\u003eScreenshot of GraphiQL showing the \u0026#8220;slug\u0026#8221; field on the \u0026#8220;Post\u0026#8221; type.\u003c/figcaption\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eApplication developers can take the information they get from the Schema and construct queries that are now predictable.\u003c/p\u003e\n\n\n\n\u003cp\u003eAnd the GraphQL Schema serves as the contract between the server and the client application, ensuring that the server will return the data in the same shape the client was promised.\u003c/p\u003e\n\n\n\n\u003cp\u003eJust like I received the specific door lock matching the brand and color that I specified in my order, client applications can specify the Types and Fields they require with a GraphQL Query, and the response will match what was asked for. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn the example below, the GraphQL Query asks for a Post and the \u0026#8220;slug\u0026#8221; field, which we can see in the Schema that it will return a String. And in response to this query, the GraphQL server will provide just what was asked for. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe \u0026#8220;just guess\u0026#8221; part of the server/client equation is eliminated.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eExample GraphQL Query \u0026amp; Response\u003c/h3\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-graphql\"\u003e\u003ccode\u003equery {\n post( id: 1, idType: DATABASE_ID ) {\n slug\n }\n}\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-json\"\u003e\u003ccode\u003e{\n \"data\": {\n \"post\": {\n \"slug\": \"hello-world\",\n },\n },\n}\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"1010\" height=\"289\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM.png\" alt=\"\" class=\"wp-image-8147\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM.png 1010w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM-300x86.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-11.34.53-AM-768x220.png 768w\" sizes=\"auto, (max-width: 1010px) 100vw, 1010px\" /\u003e\u003cfigcaption\u003eScreenshot showing a GraphQL Query and Response in the GraphiQL IDE\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eThe Gutenberg block registry\u003c/h2\u003e\n\n\n\n\u003cp\u003eNow that we\u0026#8217;re on the same page about contracts between decoupled systems and how WPGraphQL provides a contract between the WordPress server and client applications, let\u0026#8217;s move on to discuss Gutenberg more specifically. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eEarly integration with WPGraphQL\u003c/h3\u003e\n\n\n\n\u003cp\u003eGutenberg as a concept was fascinating to me early on. Like many others, I saw the potential for this block-based editor to impact WordPress users and the WordPress ecosystem greatly, WPGraphQL included.\u003c/p\u003e\n\n\n\n\u003cp\u003eI explored exposing Gutenberg blocks as queryable data in WPGraphQL as far back as June 2017: \u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter\"\u003e\u003cdiv class=\"wp-block-embed__wrapper\"\u003e\n\u003cblockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eTurns out \u003ca href=\"https://twitter.com/wpgraphql?ref_src=twsrc%5Etfw\"\u003e@wpgraphql\u003c/a\u003e plays nice with the new Gutenberg editor. \u003ca href=\"https://twitter.com/hashtag/WordPress?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#WordPress\u003c/a\u003e \u003ca href=\"https://twitter.com/hashtag/graphql?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#graphql\u003c/a\u003e \u003ca href=\"https://t.co/0Vlav4w75k\"\u003epic.twitter.com/0Vlav4w75k\u003c/a\u003e\u003c/p\u003e\u0026mdash; GraphQL for WordPress (@wpgraphql) \u003ca href=\"https://twitter.com/wpgraphql/status/878364222253481984?ref_src=twsrc%5Etfw\"\u003eJune 23, 2017\u003c/a\u003e\u003c/blockquote\u003e\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n\u003c/div\u003e\u003c/figure\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eChallenges and the current state of Gutenberg\u003c/h3\u003e\n\n\n\n\u003cp\u003eWhile a basic initial integration was straightforward, I ran into roadblocks quickly. \u003c/p\u003e\n\n\n\n\u003cp\u003eGutenberg didn\u0026#8217;t have a server-side registry for blocks. At this time, all blocks in Gutenberg were fully registered in JavaScript, which is not executed or understood by the WordPress server. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis means that unlike Post Types, Taxonomies, Meta, Sidebars, Settings, and other constructs that make up WordPress, Gutenberg blocks don\u0026#8217;t adhere to any type of contract with the WordPress server. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis means that the WordPress server knows nothing about blocks. There are no agreements between Gutenberg blocks and other systems in WordPress, or systems trying to interact with WordPress via APIs. \u003c/p\u003e\n\n\n\n\u003cp\u003eBlocks were practically non-existent as far as the application layer of WordPress was concerned. \u003c/p\u003e\n\n\n\n\u003cp\u003eThere were no WP-CLI commands to create, update, delete or list blocks. No WP REST API Schema or endpoints for blocks. No XML-RPC methods for blocks. \u003cem\u003eAnd no way to expose blocks to WPGraphQL\u003c/em\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eWithout any kind of agreement between the WordPress server and the Gutenberg JavaScript application, the WordPress server can\u0026#8217;t interact with blocks in meaningful ways. \u003c/p\u003e\n\n\n\n\u003cp\u003eFor example, the WordPress server cannot validate user input on Gutenberg blocks. Data that users input into the fields in Gutenberg blocks is trusted without question and saved to the database without the server having final say. This is a dangerous precedent, especially as Gutenberg is moving outside of editing Post content and into other parts of full-site editing. As far as I know, the lack of block input validation by the WordPress server is still a problem today.\u003c/p\u003e\n\n\n\n\u003cp\u003eAnyway, without the WordPress server having any knowledge of blocks, WPGraphQL also could not provide a meaningful integration with Gutenberg. \u003c/p\u003e\n\n\n\n\u003cp\u003eI was sad, because I was optimistic that this integration could lead to some really great innovations for decoupled applications. \u003c/p\u003e\n\n\n\n\u003cp\u003eShortly after my tweet above and running into roadblocks, I raised these concerns with the Gutenberg team on Twitter and Slack. The Gutenberg team asked me to post my thoughts in a Gutenberg Github issue, \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/gutenberg/issues/2751#issuecomment-330930741\"\u003ewhich I did at length\u003c/a\u003e. While my comments received a lot of positive emoji reactions from the community. Unfortunately the issue has been closed with many of the concerns outstanding.\u003c/p\u003e\n\n\n\n\u003cp\u003eMonths later I also \u003ca href=\"https://make.wordpress.org/mobile/2018/03/21/gutenberg-on-mobile/#comment-19383\"\u003evoiced similar concerns\u003c/a\u003e on the Make WordPress post about Gutenberg and Mobile, pointing out that without a proper server registry and API, decoupled applications, such as the WordPress native mobile application, won\u0026#8217;t be able to support Custom Blocks, or even per-site adjustments to core blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eAs of today, my understanding is that the WordPress native mobile applications still do not support custom blocks or adjustments to core blocks, making the App nearly useless for sites that have adopted Gutenberg.\u003c/p\u003e\n\n\n\n\u003cp\u003eEven with the limitations of Gutenberg, the headless WordPress community has been determined to use Gutenberg with decoupled applications.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eThree approaches to using Gutenberg in decoupled applications, today\u003c/h2\u003e\n\n\n\n\u003cp\u003eBelow are some of the different approaches, including tradeoffs, that you can implement today to start using Gutenberg in decoupled applications.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eGutenberg blocks as HTML\u003c/h3\u003e\n\n\n\n\u003cp\u003eI believe the \u003cem\u003efastest\u003c/em\u003e way to get started using Gutenberg in decoupled applications today, is to query the \u0026#8220;content\u0026#8221; field from WPGraphQL (or the WP REST API, if it\u0026#8217;s still your flavor).\u003c/p\u003e\n\n\n\n\u003cp\u003eThis is the approach that \u003ca href=\"https://frontity.org/blog/connecting-gutenberg-and-frontity/\"\u003eFrontity is using\u003c/a\u003e.\u003cbr\u003e\u003cbr\u003eThis is also the approach I\u0026#8217;m using for WPGraphQL.com, which is in use on this very blog post you\u0026#8217;re reading right now. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis post is written in Gutenberg, queried by Gatsby using WPGraphQL, and rendered using React components!\u003cbr\u003e\u003cbr\u003eHere\u0026#8217;s how it works (and please don\u0026#8217;t judge my hacky JavaScript skills ????): \u003c/p\u003e\n\n\n\n\u003cul class=\"wp-block-list\"\u003e\u003cli\u003eThe GraphQL Query in Gatsby gets the content (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/master/src/templates/WpDocument.js#L86-L88\"\u003esee the code\u003c/a\u003e)\u003c/li\u003e\u003cli\u003eThe content is passed through a parser (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/master/src/templates/WpDocument.js#L56\"\u003esee the code\u003c/a\u003e)\u003c/li\u003e\u003cli\u003eThe parser converts standard HTML elements into the \u003ca href=\"https://chakra-ui.com/\"\u003eChakra UI\u003c/a\u003e equivalent to play nice with theming (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parse-html.js#L98-L103\"\u003esee the code\u003c/a\u003e)\u003c/li\u003e\u003cli\u003eThe parser also converts things like HTML for Twitter embeds, and `\u0026lt;code\u0026gt;` blocks into React components (\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parsed-components/index.js#L146-L167\"\u003esee the code\u003c/a\u003e)\u003cul\u003e\u003cli\u003e\u003ca href=\"https://github.com/wp-graphql/wpgraphql.com/blob/848e84251f0519a28c5a7046fef2334ea0565d33/src/components/parsed-components/index.js#L94-L101\"\u003eThis is how\u003c/a\u003e we get neat things like the Syntax highlighting and \u0026#8220;copy\u0026#8221; button on the code snippets\u003c/li\u003e\u003c/ul\u003e\u003c/li\u003e\u003c/ul\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoff: Lack of Introspection, unpredictable data\u003c/h4\u003e\n\n\n\n\u003cp\u003eWhile this is working for me and WPGraphQL.com, I can\u0026#8217;t recommend it for everyone.\u003c/p\u003e\n\n\n\n\u003cp\u003eUsing HTML as the API defeats much of the purpose of decoupled systems. In order to use the markup as an API, the developers of the decoupled application need to be able to predict all the markup that might come out of the editor. \u003c/p\u003e\n\n\n\n\u003cp\u003eQuerying HTML and trying to predict all the markup to parse is like me ordering \u0026#8220;product\u0026#8221; at the store. At any time I (or other users of WordPress) could add blocks with markup that my parser doesn\u0026#8217;t recognize and the consuming application might not work as intended. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\" id=\"block-4c1b3d70-cbce-4b4c-9d03-1019c7014449\"\u003eTradeoff: Missing data\u003c/h4\u003e\n\n\n\n\u003cp\u003eContent creators can modify attributes of blocks, and Gutenberg saves these attributes as HTML comments in the post_content. But when the content is prepared for public use in WordPress themes, the WP REST API or WPGraphQL, the raw block attributes are not available, so a parser like the one I described will not have all the block data to work with. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\" id=\"block-ebd15260-ea14-49b4-9d6f-c1e78cdcd1b3\"\u003eTradeoff: Undefined Types\u003c/h4\u003e\n\n\n\n\u003cp\u003eTo overcome the \u0026#8220;missing data\u0026#8221; issue, it\u0026#8217;s possible to pass attributes from Gutenberg blocks as HTML data-attributes in the \u003ca href=\"https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/creating-dynamic-blocks/\"\u003erender_callback\u003c/a\u003e for blocks, as a way to get Gutenberg attributes passed from the editor to the rendered HTML and available for a parser to use, but even doing this leads to client applications not knowing what to expect, and leads to undefined Types as all data-attributes are strings, so mapping data-attributes to something like a React or Vue component is difficult and fragile with this method.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eWhen to use\u003c/h4\u003e\n\n\n\n\u003cp\u003eThis approach works for me, because I personally control both sides of wpgraphql.com, what blocks are available in the WordPress install, what content is published, and the Gatsby consumer application that queries the content and renders the site. In the e-commerce analogy, I\u0026#8217;m both the person ordering the \u0026#8220;product\u0026#8221; \u003cem\u003eand\u003c/em\u003e the person fulfilling the order, so there are no surprises. I\u0026#8217;m not working with different teams, or even different team members, and I\u0026#8217;m the primary content creator.\u003c/p\u003e\n\n\n\n\u003cp\u003eFor projects that have multiple team members, multiple authors, multiple teams and/or multi-channel distribution of content, I would not recommend this approach. And multi-team, I would argue, includes the team that builds the project, \u003cem\u003eand\u003c/em\u003e the team that maintains it after it\u0026#8217;s live, which in many agencies are different teams. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eGutenberg Object Plugin\u003c/h3\u003e\n\n\n\n\u003cp\u003eIn late 2018, \u003ca href=\"https://twitter.com/royboy789\" data-type=\"URL\" data-id=\"https://twitter.com/royboy789\"\u003eRoy Sivan\u003c/a\u003e, a Senior JavaScript Engineer and recurring \u003ca href=\"https://twitter.com/search?q=%40royboy789%20happy%20birthday%20%40benUNC\u0026amp;src=typed_query\"\u003eHappy Birthday wisher to Ben Meredith\u003c/a\u003e, released a plugin that exposed Gutenberg blocks to the WP REST API: \u003c/p\u003e\n\n\n\n\u003cfigure class=\"wp-block-embed is-type-rich is-provider-twitter wp-block-embed-twitter\"\u003e\u003cdiv class=\"wp-block-embed__wrapper\"\u003e\n\u003cblockquote class=\"twitter-tweet\" data-width=\"550\" data-dnt=\"true\"\u003e\u003cp lang=\"en\" dir=\"ltr\"\u003eYup, a plugin I made that takes \u003ca href=\"https://twitter.com/hashtag/Gutenberg?src=hash\u0026amp;ref_src=twsrc%5Etfw\"\u003e#Gutenberg\u003c/a\u003e data and stores the data elegantly into the DB which is accessible via REST API directly or via normal /posts/ endpoint. \u003cbr\u003e\u003cbr\u003eBeen coming in handy for this headless React App. \u003ca href=\"https://t.co/16pAAZoM4g\"\u003ehttps://t.co/16pAAZoM4g\u003c/a\u003e\u003c/p\u003e\u0026mdash; 🟣☮ Roy (@royboy789) \u003ca href=\"https://twitter.com/royboy789/status/1047542119252938752?ref_src=twsrc%5Etfw\"\u003eOctober 3, 2018\u003c/a\u003e\u003c/blockquote\u003e\u003cscript async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"\u003e\u003c/script\u003e\n\u003c/div\u003e\u003c/figure\u003e\n\n\n\n\u003cp\u003eThis plugin exposes Gutenberg block data to the WP REST API so that data saved to pages can be consumed as JSON objects. \u003c/p\u003e\n\n\n\n\u003cp\u003eExposing Gutenberg data as JSON is what a lot of developers building decoupled applications want. They want to take the data in a structured form, and pass the data to React / Vue / Native components. This plugin gets things headed in the right direction! \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoff: Lack of Introspection, unpredictable data\u003c/h4\u003e\n\n\n\n\u003cp\u003eBut, because of the lack of a server-side registry for Gutenberg blocks, and the non-enforced Schema of the WP REST API, this \u003ca href=\"https://github.com/royboy789/gutenberg-object-plugin\"\u003eplugin\u003c/a\u003e also suffers from the \u0026#8220;just guess\u0026#8221; pattern for decoupled applications.\u003c/p\u003e\n\n\n\n\u003cp\u003eThis plugin is unable to register blocks or fields to the WP REST API, so inspecting the Schema leaves decoupled application developers guessing. \u003c/p\u003e\n\n\n\n\u003cp\u003eIf we Introspect the REST API Schema from this plugin and we can see that the Schema doesn\u0026#8217;t provide any information to the decoupled application developer about what to expect.\u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"457\" height=\"368\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM.png\" alt=\"\" class=\"wp-image-8134\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM.png 457w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.41.18-AM-300x242.png 300w\" sizes=\"auto, (max-width: 457px) 100vw, 457px\" /\u003e\u003cfigcaption\u003eScreenshot of the introspection of the Gutenberg Object Plugin REST endpoint\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eIt\u0026#8217;s like ordering a \u0026#8220;product\u0026#8221; from an e-commerce store. The endpoint can return anything at any time, and can change from page to page, request to request. \u003c/p\u003e\n\n\n\n\u003cp\u003eThere\u0026#8217;s no contract between the REST endpoints and the consumer application. There\u0026#8217;s no scalable way for decoupled application developers to know what type of data the endpoints will return, now or in the future.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoff: Only available in REST\u003c/h4\u003e\n\n\n\n\u003cp\u003eIf you\u0026#8217;re building headless applications using WPGraphQL, taking advantages of \u003ca href=\"https://www.wpgraphql.com/docs/wpgraphql-vs-wp-rest-api/\" data-type=\"docs\" data-id=\"864\"\u003efeatures that differentiate WPGraphQL from REST,\u003c/a\u003e you would not be able to use the GraphQL Objects plugin in your decoupled application without enduring additional pain points, in addition to the lack of introspection.\u003c/p\u003e\n\n\n\n\u003cp\u003eCaching clients such as \u003ca href=\"https://www.apollographql.com/docs/react/\"\u003eApollo\u003c/a\u003e would have to be customized to work with data from these endpoints, and still may not work well with the rest of the application that might be using GraphQL. Additionally, when using REST endpoints with related resources, it becomes the clients responsibility to determine how to map the various block endpoint data to the components that need the data. There\u0026#8217;s no concept of coupling \u003ca href=\"https://www.wpgraphql.com/docs/intro-to-graphql/#fragments\"\u003eGraphQL Query Fragments\u003c/a\u003e with Components, like you can do with GraphQL.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eWhen to use:\u003c/h4\u003e\n\n\n\n\u003cp\u003eAgain, if you are the developer controlling both sides, the WordPress server and the client application, this approach could work, at least while you\u0026#8217;re building the application and the capabilities are fresh in your mind. But in general, this approach can cause some pain points that that might be difficult to identify and fix when things go wrong. For example, 6 months down the road, even the person that built the application will likely forget all the details, and when there\u0026#8217;s a bug, and no contract between the applications to refer to, it can be hard to diagnose and fix.\u003c/p\u003e\n\n\n\n\u003cp\u003eEven when things break with GraphQL applications (and they do), the explicit nature of GraphQL Queries serve as a \u0026#8220;documentation of intent\u0026#8221; from the original application developer and can make it much easier for teams to diagnose, down to specific leaf fields, what is broken. \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eWPGraphQL for Gutenberg\u003c/h3\u003e\n\n\n\n\u003cp\u003eIn early 2019 \u003ca href=\"https://twitter.com/pristas_peter\"\u003ePeter Pristas\u003c/a\u003e introduced the \u003ca href=\"https://github.com/pristas-peter/wp-graphql-gutenberg\"\u003eWPGraphQL for Gutenberg\u003c/a\u003e plugin. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe intent of this plugin is to expose Gutenberg blocks to the WPGraphQL Schema, so that decoupled application developers could use tools such as GraphiQL to inspect what blocks were possible to be queried from the API, and compose GraphQL Queries asking for specific fields of specific blocks that they wanted to support. \u003c/p\u003e\n\n\n\n\u003cp\u003eNow, content creators can publish content with Gutenberg, and decoupled application developers can introspect the Schema and construct queries asking for the specific blocks and fields their application supports.\u003c/p\u003e\n\n\n\n\u003cp\u003eDecoupled application developers can move at their own pace, independent from the developers and content creators working on the CMS. The decoupled application can specify which blocks are supported, and ask for the exact fields they need. Much like an e-commerce consumer can specify the specific color door lock they want to order from the store! The Schema serves as the contract between the server and the client. Clients can predictably ask for what they want, and get just that in response. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eCreating a page\u003c/h4\u003e\n\n\n\n\u003cp\u003eContent creators can use Gutenberg to create pages. In the example blow, we see a page in Gutenberg with a Paragraph block and an Image block. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"468\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1024x468.png\" alt=\"Screenshot showing the Gutenberg editor with a paragraph and image block.\" class=\"wp-image-8128\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1024x468.png 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-300x137.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-768x351.png 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1536x702.png 1536w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM-1568x717.png 1568w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.02.38-PM.png 1918w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" /\u003e\u003cfigcaption\u003eScreenshot showing the Gutenberg editor with a paragraph and image block.\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eExploring the Schema\u003c/h4\u003e\n\n\n\n\u003cp\u003eWith the plugin installed and activated (for demo sake I have \u003ca href=\"https://github.com/wp-graphql/wp-graphql/releases/tag/v1.2.5\"\u003eWPGraphQL v1.2.5\u003c/a\u003e and \u003ca href=\"https://github.com/pristas-peter/wp-graphql-gutenberg/releases/tag/v0.3.8\"\u003eWPGraphQL for Gutenberg v0.3.8\u003c/a\u003e active), decoupled application developers can use GraphiQL to browse the Schema to see what Gutenberg Blocks are available to query and interact with. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex\"\u003e\n\u003cdiv class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"\u003e\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM.png\" alt=\"\" class=\"wp-image-8122\" width=\"356\" height=\"813\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM.png 356w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.39-PM-131x300.png 131w\" sizes=\"auto, (max-width: 356px) 100vw, 356px\" /\u003e\u003cfigcaption\u003eScreenshot of GraphiQL showing Gutenberg Blocks\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\u003c/div\u003e\n\n\n\n\u003cdiv class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\"\u003e\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large is-resized\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM.png\" alt=\"\" class=\"wp-image-8120\" width=\"362\" height=\"812\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM.png 361w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-12.25.44-PM-134x300.png 134w\" sizes=\"auto, (max-width: 362px) 100vw, 362px\" /\u003e\u003cfigcaption\u003eScreenshot of GraphiQL showing the CoreParagraphBlock and its fields\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003c/div\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eQuerying the blocks\u003c/h4\u003e\n\n\n\n\u003cp\u003eAnd using the Schema, developers can construct a query to ask for the blocks and fields that their application supports.\u003c/p\u003e\n\n\n\n\u003cp\u003e\u003cstrong\u003eHere\u0026#8217;s an example query:\u003c/strong\u003e\u003c/p\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-graphql\"\u003e\u003ccode\u003e{\n post(id: 6, idType: DATABASE_ID) {\n id\n databaseId\n title\n blocks {\n __typename\n name\n ... on CoreImageBlock {\n attributes {\n ... on CoreImageBlockAttributes {\n url\n alt\n caption\n }\n }\n }\n ... on CoreParagraphBlock {\n attributes {\n ... on CoreParagraphBlockAttributes {\n content\n }\n }\n }\n }\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cp\u003e\u003cstrong\u003eAnd the response:\u003c/strong\u003e\u003c/p\u003e\n\n\n\n\u003cp\u003eYou can see that the response includes the exact fields that were asked for. No surprises. \u003c/p\u003e\n\n\n\n\u003cpre class=\"wp-block-code lang-json\"\u003e\u003ccode\u003e{\n \"data\": {\n \"post\": {\n \"id\": \"cG9zdDo2\",\n \"databaseId\": 6,\n \"title\": \"Test Gutenberg Post\",\n \"blocks\": [\n {\n \"__typename\": \"CoreParagraphBlock\",\n \"name\": \"core/paragraph\",\n \"attributes\": {\n \"content\": \"This is a paragraph\"\n }\n },\n {\n \"__typename\": \"CoreImageBlock\",\n \"name\": \"core/image\",\n \"attributes\": {\n \"url\": \"http://wpgraphql.local/wp-content/uploads/2021/03/Screen-Shot-2021-03-04-at-12.11.53-PM-1024x490.png\",\n \"alt\": \"Jason Bahl, dressed in character as JamStackMullet with a Mullet wig and sunglasses, watches the WP Engine Decode conference\",\n \"caption\": \"Screenshot of the JamStackMullet watching WP Engine Decode conference\"\n }\n }\n }\u003c/code\u003e\u003c/pre\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"555\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-1024x555.png\" alt=\"\" class=\"wp-image-8132\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-1024x555.png 1024w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-300x163.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM-768x416.png 768w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-05-at-4.08.32-PM.png 1376w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" /\u003e\u003cfigcaption\u003eScreenshot of a query for a post and some blocks using WPGraphQL for Gutenberg.\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eGraphQL Schema as the contract\u003c/h4\u003e\n\n\n\n\u003cp\u003eHaving the GraphQL Schema serve as the contract between the client and server allows each part of the application to move forward at its own pace. There\u0026#8217;s now an agreement for how things will behave. If the contract is broken, for example, if the server changed the shape of one of the Types in the GraphQL Schema, it\u0026#8217;s easily identifiable and can be fixed quickly, because the client specified exactly what was needed from the server by way of a GraphQL Query. \u003c/p\u003e\n\n\n\n\u003cp\u003eThis removes the \u0026#8220;just guess\u0026#8221; pattern from decoupled application development with Gutenberg.\u003c/p\u003e\n\n\n\n\u003cp\u003eTeams that know nothing about WordPress can even make use of the data. For example, a data warehouse team, a native mobile team, a print team, etc. The GraphQL Schema and tooling such as GraphiQL frees up different teams to use the data in their applications how they want.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eClient in control\u003c/h4\u003e\n\n\n\n\u003cp\u003eWith clients querying Gutenberg blocks as data, this gives clients full control over the presentation of the blocks. Whether the blocks are used in a React or Vue website, or used for a Native iOS app that doesn\u0026#8217;t render HTML, or used to prepare a newspaper for print, the client gets to ask for the fields that it needs, and gets to decide what happens with the data. No unexpected changes from the server, the client is in control.\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradeoffs: Scaling issues\u003c/h4\u003e\n\n\n\n\u003cp\u003eWhile WPGraphQL for Gutenberg gets us much closer to being able to query Gutenberg blocks as data, it unfortunately has a dependency that makes it very difficult to scale, and it comes back, again, to the lack of a proper server side registry for blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eSince Gutenberg Blocks aren\u0026#8217;t registered on the server, WPGraphQL for Gutenberg has a settings page where users must click a button to \u0026#8220;Update the Block Registry\u0026#8221;. \u003c/p\u003e\n\n\n\n\u003cdiv class=\"wp-block-image\"\u003e\u003cfigure class=\"aligncenter size-large\"\u003e\u003cimg loading=\"lazy\" decoding=\"async\" width=\"848\" height=\"128\" src=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM.png\" alt=\"\" class=\"wp-image-8136\" srcset=\"https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM.png 848w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM-300x45.png 300w, https://content.wpgraphql.com/wp-content/uploads/2021/03/Screen-Shot-2021-03-08-at-8.59.25-AM-768x116.png 768w\" sizes=\"auto, (max-width: 848px) 100vw, 848px\" /\u003e\u003cfigcaption\u003eScreenshot of the WPGraphQL for Gutenberg settings page\u003c/figcaption\u003e\u003c/figure\u003e\u003c/div\u003e\n\n\n\n\u003cp\u003eClicking this button opens up Gutenberg in a hidden iFrame, executes the JavaScript to instantiate Gutenberg, gets the Block Registry from Gutenberg initialized in JavaScript, sends the list of registered blocks to the server and stores the registry in the options table of the WordPress database. The registered blocks that are stored in the database are then used to map to the GraphQL Schema.\u003c/p\u003e\n\n\n\n\u003cp\u003ePeter Pristas deservers an award, because this approach is a very creative solution to the frustrating problem of Gutenberg not respecting the WordPress server. \u003c/p\u003e\n\n\n\n\u003cp\u003eUnfortunately this solution doesn\u0026#8217;t scale well. \u003cbr\u003e\u003cbr\u003eSince Gutenberg blocks are registered in JavaScript, this means that the JavaScript to register any given block might be enqueued from WordPress on only specific pages, specific post types, or other unique individualized criteria. \u003c/p\u003e\n\n\n\n\u003cp\u003eThat means the JavaScript Block Registry for Page A and Page B might be different from each other, and maybe also different from the registry for Post Type C or Post Type D. So loading one page in an iframe to get the block registry might not get the full picture of what blocks are \u003cem\u003epossible\u003c/em\u003e to interact with in a decoupled application. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn order for the block registry that is generated from the iframe to be accurate, \u003cem\u003eevery\u003c/em\u003e page of every post type that Gutenberg is enabled on in the site would need to be loaded by iframe to account for cases where blocks were registered to specific contexts. Yikes!\u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eTradoffs: Schema design issues\u003c/h4\u003e\n\n\n\n\u003cp\u003eIn addition to the scaling issues, there are some concerns with some of the Schema design choices, and I\u0026#8217;ll even take the blame for some of this, as I had many conversations with Peter as he worked on the plugin, and he followed my lead with some of my also poor Schema design choices. \u003c/p\u003e\n\n\n\n\u003cp\u003eOne issue is infinite nesting. Gutenberg blocks, as previously discussed, can sometimes have nested inner blocks. In WPGraphQL for Gutenberg, querying inner blocks requires explicit queries and without knowing what level of depth inner blocks might reach, it\u0026#8217;s difficult to compose queries that properly return all inner blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eWPGraphQL used to expose hierarchical data in a similar way, but has since changed to expose hierarchical data, such as Nav Menu Items, in flat lists. This allows for nested data in any depth to be queried, and \u003ca href=\"https://www.wpgraphql.com/docs/menus/#hierarchical-data\"\u003ere-structured in a hierarchy on the client\u003c/a\u003e.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe unlimited depth issue is commonly reported for projects such as \u003ca href=\"https://github.com/gatsbyjs/gatsby-source-wordpress-experimental/issues?q=is%3Aissue+gutenberg+is%3Aclosed\"\u003eGatsby Source WordPress\u003c/a\u003e. \u003c/p\u003e\n\n\n\n\u003ch4 class=\"wp-block-heading\"\u003eWhen to use\u003c/h4\u003e\n\n\n\n\u003cp\u003eIf Gutenberg is a requirement for your headless project, this might be a good option, as it allows you to query Gutenberg blocks as structured data. You gain a lot of the predictability that you miss with the other options, and can benefit greatly from features of GraphQL such as \u003ca href=\"https://www.wpgraphql.com/docs/wpgraphql-vs-wp-rest-api/#batch-queries\"\u003eBatch Queries\u003c/a\u003e, \u003ca href=\"https://www.wpgraphql.com/docs/intro-to-graphql/#fragments\"\u003ecoupling Fragments with components\u003c/a\u003e, and more.\u003c/p\u003e\n\n\n\n\u003cp\u003eSo while WPGraphQL for Gutenberg is probably the closest option available for being able to predictably query Gutenberg blocks as data in decoupled applications, there are some serious questions in regards to production readiness, \u003cem\u003eespecially\u003c/em\u003e on larger projects, and you should consider these issues before choosing it for your next project.\u003c/p\u003e\n\n\n\n\u003cp\u003eTradeoffs in mind, agencies such as WebDevStudios are \u003ca href=\"https://webdevstudios.com/2021/03/09/next-js-headless-wordpress/\" target=\"_blank\" rel=\"noreferrer noopener\"\u003eusing this approach in production\u003c/a\u003e, even for large sites.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eProgress for the server side block registry\u003c/h2\u003e\n\n\n\n\u003cp\u003eIn 2020, some progress was made in regards to a server side registry for Gutenberg blocks. \u003c/p\u003e\n\n\n\n\u003cp\u003eWhile the \u003ca href=\"https://developer.wordpress.org/block-editor/tutorials/block-tutorial/writing-your-first-block-type/#enqueuing-block-scripts\"\u003eofficial Gutenberg documentation\u003c/a\u003e still shows developers how to create new blocks fully JavaScript with no server awareness, the core Gutenberg blocks have started transitioning to have \u003cem\u003esome\u003c/em\u003e data registered on the server.\u003c/p\u003e\n\n\n\n\u003cp\u003eYou can \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/WordPress/tree/master/wp-includes/blocks\"\u003esee here\u003c/a\u003e that (as of Gutenberg 5.6.2, released in February 2021) core Gutenberg blocks are now registered with JSON files that can be used by the PHP server as well as the JavaScript client. \u003c/p\u003e\n\n\n\n\u003cp\u003eThese JSON files are now used to expose blocks to the WP REST API.\u003c/p\u003e\n\n\n\n\u003cp\u003e This is progress! \u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eInner blocks, inner peace?\u003c/h3\u003e\n\n\n\n\u003cp\u003eUnfortunately it\u0026#8217;s not all the progress needed to have meaningful impact for decoupled applications to use Gutenberg. There\u0026#8217;s a lot of information that a decoupled application would need about blocks that is not described in the server registry. One example (of many) being inner blocks.\u003c/p\u003e\n\n\n\n\u003cp\u003eGutenberg has a concept called \u0026#8220;Inner Blocks\u0026#8221;, which is blocks that can have other blocks nested within. For example, a \u0026#8220;Column\u0026#8221; block can have other blocks nested within each column, while other blocks such as an Image block cannot have nested inner blocks.\u003c/p\u003e\n\n\n\n\u003cp\u003eThe bit of server side registry that is now available for core Gutenberg blocks doesn\u0026#8217;t declare this information. If we take a look at the Column block\u0026#8217;s \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/WordPress/blob/5.6.2/wp-includes/blocks/column/block.json\"\u003eblock.json\u003c/a\u003e file, we can see there\u0026#8217;s no mention of inner blocks being supported. Additionally, if. we look at the Image block\u0026#8217;s \u003ca href=\"https://github.com/nas/content/live/wpgqlcontent/WordPress/blob/5.6.2/wp-includes/blocks/image/block.json\"\u003eblock.json\u003c/a\u003e file, we don\u0026#8217;t see any mention of inner blocks \u003cem\u003enot\u003c/em\u003e being supported. \u003c/p\u003e\n\n\n\n\u003cp\u003eIn order for a decoupled application, such as the official WordPress iOS app, to know what blocks can or cannot have inner blocks, this information needs to be exposed to an API that the decoupled application can read. Without the server knowing about this information, decoupled applications cannot know this information either.\u003c/p\u003e\n\n\n\n\u003cp\u003eSo, while there\u0026#8217;s been a bit of a migration for the core WordPress blocks to have some server (and REST API) awareness, there\u0026#8217;s still a lot of missing information. Also the community of 3rd party block developers are still being directed to build blocks entirely in JavaScript, which means that all new blocks will have no server awareness until the server registry becomes more of a 1st-class citizen for Gutenberg.\u003c/p\u003e\n\n\n\n\u003ch2 class=\"wp-block-heading\"\u003eWhat\u0026#8217;s next?\u003c/h2\u003e\n\n\n\n\u003cp\u003eThe beginnings of a move toward a server-side registry gives hope, and gives a bit of a path toward blocks being properly introspect-able and useful by decoupled teams and applications.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eSpecification for Server Side Registering Blocks\u003c/h3\u003e\n\n\n\n\u003cp\u003eI believe that the step forward for Gutenberg + decoupled applications, is to come up with a specification for how Gutenberg blocks can be registered on the server to work properly with server APIs. \u003c/p\u003e\n\n\n\n\u003cp\u003eOnce a specification is discussed, vetted, tested and published, the WP REST API, WP CLI and WPGraphQL, and therefore decoupled applications such as the WordPress native mobile app, would all make use of the spec to be able to interact with Gutenberg blocks.\u003c/p\u003e\n\n\n\n\u003cp\u003e\u003cem\u003eI don\u0026#8217;t fully know what this spec needs to look like, but I believe it needs to exist in some form.\u003c/em\u003e\u003c/p\u003e\n\n\n\n\u003cp\u003eProjects such as \u003ca href=\"https://github.com/rtCamp/gutenberg-fields-middleware\"\u003eGutenberg Fields Middleware\u003c/a\u003e from \u003ca href=\"https://rtcamp.com/\"\u003ertCamp\u003c/a\u003e, \u003ca href=\"https://www.advancedcustomfields.com/resources/blocks/\"\u003eACF Blocks\u003c/a\u003e, and \u003ca href=\"https://wordpress.org/plugins/genesis-custom-blocks/\"\u003eGenesis Custom Blocks\u003c/a\u003e all take a server-first approach to creating new Gutenberg blocks, and I think there\u0026#8217;s a lot to learn from these projects. \u003c/p\u003e\n\n\n\n\u003cp\u003eThe blocks from these tools are created in a way that the WordPress server knows what blocks exist, what attributes and fields the blocks have, and the server can then pass the description of the blocks to the Gutenberg JavaScript application, which then renders the blocks for users to interact with. \u003c/p\u003e\n\n\n\n\u003cp\u003eSince the server provides the Gutenberg JavaScript application with the information needed to render the blocks to a content producer, this means the server can \u003cem\u003ealso\u003c/em\u003e provide the information to other clients, such as the native mobile WordPress app, or teams building decoupled front-ends with Gatsby, Gridsome or NextJS.\u003c/p\u003e\n\n\n\n\u003ch3 class=\"wp-block-heading\"\u003eThe future of decoupled Gutenberg\u003c/h3\u003e\n\n\n\n\u003cp\u003eI believe that with a proper specification for registering blocks on the server, Gutenberg can enable some incredibly powerful integrations across the web.\u003c/p\u003e\n\n\n\n\u003cp\u003eMy thoughts are that we can arrive at a specification for registering blocks that can enable block developers to provide pleasant editing experiences, while providing decoupled application developers with the ability to Introspect the GraphQL API, predictably write GraphQL Queries (and Mutations) to interact with blocks, and get predictable, strongly typed results that can be used in decoupled applications.\u003c/p\u003e\n\n\n\n\u003cp\u003eIn an effort to start discussing what the future of a Gutenberg Block Server Registry Specification like this might look like, I\u0026#8217;ve opened the following Github issue: \u003ca rel=\"noreferrer noopener\" href=\"https://github.com/wp-graphql/wp-graphql/issues/1764\" target=\"_blank\"\u003ehttps://github.com/wp-graphql/wp-graphql/issues/1764\u003c/a\u003e\u003c/p\u003e\n\n\n\n\u003cp\u003eIf this topic interests you, and you\u0026#8217;d like to be involved in discussing what such a specification might look like, please contribute ideas to that issue. Additionally, you can \u003ca href=\"https://join.slack.com/t/wp-graphql/shared_invite/zt-3vloo60z-PpJV2PFIwEathWDOxCTTLA\"\u003eJoin the WPGraphQL community on Slack\u003c/a\u003e community, and visit the \u003ccode\u003e#gutenberg\u003c/code\u003e channel and discuss in there.\u003c/p\u003e\n","author":{"__typename":"NodeWithAuthorToUserConnectionEdge","node":{"__typename":"User","name":"Jason Bahl","uri":"/author/jasonbahl/","avatar":{"__typename":"Avatar","url":"https://secure.gravatar.com/avatar/94bf4ea789246f76c48bcf8509bcf01e?s=96\u0026d=mm\u0026r=g"}}},"categories":{"__typename":"PostToCategoryConnection","nodes":[{"__ref":"Category:dGVybTo3Njk0Nzk="},{"__ref":"Category:dGVybTo3Njk0NzQ="}]}},"ROOT_QUERY":{"__typename":"RootQuery","nodeByUri({\"uri\":\"/2021/03/09/gutenberg-and-decoupled-applications\"})":{"__ref":"Post:cG9zdDo4MDU4"},"post({\"id\":\"/2021/03/09/gutenberg-and-decoupled-applications/\",\"idType\":\"URI\"})":{"__ref":"Post:cG9zdDo4MDU4"},"menu({\"id\":\"Primary Nav\",\"idType\":\"NAME\"})":{"__ref":"Menu:dGVybTozMA=="}},"Category:dGVybTo3Njk0Nzk=":{"__typename":"Category","id":"dGVybTo3Njk0Nzk=","name":"Gutenberg","uri":"/category/gutenberg/"},"Category:dGVybTo3Njk0NzQ=":{"__typename":"Category","id":"dGVybTo3Njk0NzQ=","name":"WordPress","uri":"/category/wordpress/"},"MenuItem:cG9zdDoyNjE3":{"__typename":"MenuItem","id":"cG9zdDoyNjE3","label":"Docs","description":"Learn how to use WPGraphQL to build headless apps","url":"/docs/introduction/","target":null,"path":"/docs/introduction/","parentId":null,"cssClasses":["icon-BookOpenIcon"]},"MenuItem:cG9zdDo4NDI1":{"__typename":"MenuItem","id":"cG9zdDo4NDI1","label":"Getting Started","description":"Get up and running with WPGraphQL.","url":"/docs/introduction/","target":null,"path":"/docs/introduction/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-BookOpenIcon"]},"MenuItem:cG9zdDo4NDI0":{"__typename":"MenuItem","id":"cG9zdDo4NDI0","label":"Beginner Guides","description":"Learn the basics of GraphQL and using it with WordPress","url":"/docs/intro-to-graphql/","target":null,"path":"/docs/intro-to-graphql/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-BookOpenIcon"]},"MenuItem:cG9zdDo4NDI2":{"__typename":"MenuItem","id":"cG9zdDo4NDI2","label":"Using WPGraphQL","description":"Learn the details of using WPGraphQL","url":"/docs/posts-and-pages/","target":null,"path":"/docs/posts-and-pages/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-ChartBarIcon"]},"MenuItem:cG9zdDo4NDI3":{"__typename":"MenuItem","id":"cG9zdDo4NDI3","label":"Advanced Concepts","description":"Dig deeper into WPGraphQL’s features","url":"/docs/wpgraphql-concepts/","target":null,"path":"/docs/wpgraphql-concepts/","parentId":"cG9zdDoyNjE3","cssClasses":["icon-ShieldCheckIcon"]},"MenuItem:cG9zdDoyNjI0":{"__typename":"MenuItem","id":"cG9zdDoyNjI0","label":"Developer Reference","description":"Learn how to extend WPGraphQL on the server","url":"/developer-reference/","target":null,"path":"/developer-reference/","parentId":null,"cssClasses":["icon-Bars4Icon"]},"MenuItem:cG9zdDo4NDM3":{"__typename":"MenuItem","id":"cG9zdDo4NDM3","label":"Recipes","description":"Helpful Snippets to help developers extend or modify WPGraphQL","url":"/recipes","target":null,"path":"/recipes","parentId":"cG9zdDoyNjI0","cssClasses":["icon-CommandLineIcon"]},"MenuItem:cG9zdDo4NDQw":{"__typename":"MenuItem","id":"cG9zdDo4NDQw","label":"Functions","description":"Helpful functions for developers interacting with WPGraphQL","url":"/functions","target":null,"path":"/functions","parentId":"cG9zdDoyNjI0","cssClasses":["icon-CodeBracketIcon"]},"MenuItem:cG9zdDo4NDM4":{"__typename":"MenuItem","id":"cG9zdDo4NDM4","label":"Actions","description":"WordPress actions you can use to hook into WPGraphQL","url":"/actions","target":null,"path":"/actions","parentId":"cG9zdDoyNjI0","cssClasses":["icon-BoltIcon"]},"MenuItem:cG9zdDo4NDM5":{"__typename":"MenuItem","id":"cG9zdDo4NDM5","label":"Filters","description":"WordPress filters you can use to modify WPGraphQL","url":"/filters","target":null,"path":"/filters","parentId":"cG9zdDoyNjI0","cssClasses":["icon-FunnelIcon"]},"MenuItem:cG9zdDoyNjIw":{"__typename":"MenuItem","id":"cG9zdDoyNjIw","label":"Extensions","description":"Find extensions that add functionality to WPGrapQL","url":"/extensions","target":null,"path":"/extensions","parentId":null,"cssClasses":["icon-PuzzlePieceIcon"]},"MenuItem:cG9zdDo4MzMzNzM=":{"__typename":"MenuItem","id":"cG9zdDo4MzMzNzM=","label":"Blog","description":null,"url":"/blog","target":null,"path":"/blog","parentId":null,"cssClasses":[]},"MenuItem:cG9zdDo4MzMyMzA=":{"__typename":"MenuItem","id":"cG9zdDo4MzMyMzA=","label":"REPL","description":"WPGraphQL REPL","url":"https://repl.wpgraphql.com/","target":"_blank","path":"https://repl.wpgraphql.com/","parentId":null,"cssClasses":[]},"Menu:dGVybTozMA==":{"__typename":"Menu","id":"dGVybTozMA==","name":"Primary Nav","menuItems({\"first\":100})":{"__typename":"MenuToMenuItemConnection","nodes":[{"__ref":"MenuItem:cG9zdDoyNjE3"},{"__ref":"MenuItem:cG9zdDo4NDI1"},{"__ref":"MenuItem:cG9zdDo4NDI0"},{"__ref":"MenuItem:cG9zdDo4NDI2"},{"__ref":"MenuItem:cG9zdDo4NDI3"},{"__ref":"MenuItem:cG9zdDoyNjI0"},{"__ref":"MenuItem:cG9zdDo4NDM3"},{"__ref":"MenuItem:cG9zdDo4NDQw"},{"__ref":"MenuItem:cG9zdDo4NDM4"},{"__ref":"MenuItem:cG9zdDo4NDM5"},{"__ref":"MenuItem:cG9zdDoyNjIw"},{"__ref":"MenuItem:cG9zdDo4MzMzNzM="},{"__ref":"MenuItem:cG9zdDo4MzMyMzA="}]}}}},"__N_SSG":true},"page":"/[...wordpressNode]","query":{"wordpressNode":["2021","03","09","gutenberg-and-decoupled-applications"]},"buildId":"O4Tz2qRxFYEkYRCQePyAA","isFallback":false,"gsp":true,"scriptLoader":[]}</script><script nomodule="" src="/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js"></script><script src="/_next/static/chunks/webpack-aaf2b736bc265890.js" async=""></script><script src="/_next/static/chunks/framework-02398e00071ab346.js" async=""></script><script src="/_next/static/chunks/main-c66e5e2c08594bca.js" async=""></script><script src="/_next/static/chunks/pages/_app-807050be1a0fea29.js" async=""></script><script src="/_next/static/chunks/pages/%5B...wordpressNode%5D-8553865619f9b237.js" async=""></script><script src="/_next/static/O4Tz2qRxFYEkYRCQePyAA/_buildManifest.js" async=""></script><script src="/_next/static/O4Tz2qRxFYEkYRCQePyAA/_ssgManifest.js" async=""></script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10