{"id":31881,"date":"2025-05-12T20:19:26","date_gmt":"2025-05-13T01:19:26","guid":{"rendered":"https:\/\/wpengine.com\/builders\/?p=31881"},"modified":"2025-05-13T11:35:11","modified_gmt":"2025-05-13T16:35:11","slug":"sveltekit-wordpress-routing-and-graphql","status":"publish","type":"post","link":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/","title":{"rendered":"SvelteKit + WordPress: Routing and GraphQL"},"content":{"rendered":"\n<p>Svelte is my favorite framework, and today we\u2019re exploring using SvelteKit to handle routing and data fetching for headless WordPress!<\/p>\n\n\n\n<p>This article is part of a series exploring implementing the <a href=\"https:\/\/developer.wordpress.org\/themes\/basics\/template-hierarchy\/\" target=\"_blank\" rel=\"noreferrer noopener\">WordPress template hierarchy<\/a> in various frameworks. In the <a href=\"https:\/\/wpengine.com\/builders\/astro-wordpress-routing-and-graphql\/\" target=\"_blank\" rel=\"noreferrer noopener\">Astro + WordPress article<\/a>, we covered many foundational topics. I recommend giving that a quick read before proceeding, especially if you\u2019re not familiar with WordPress, its template hierarchy, or how to implement the template hierarchy in a JavaScript framework.&nbsp;<\/p>\n\n\n\n<p>For a working example of what we discuss here, check out the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/tree\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\" target=\"_blank\" rel=\"noreferrer noopener\">wpengine\/hwptoolkit<\/a> repo.<\/p>\n\n\n\n<div class=\"wp-block-group has-polar-background-color has-background is-layout-flow wp-container-core-group-is-layout-7a03825d wp-block-group-is-layout-flow\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--40)\">\n<p class=\"has-large-font-size\"><strong>Table of Contents<\/strong><\/p>\n\n\n\n<ul id=\"Prerequisites\" class=\"wp-block-list\">\n<li><a href=\"#routing\">Routing<\/a><\/li>\n\n\n\n<li><a href=\"#template-hierarchy-in-sveltekit\">Template Hierarchy in SvelteKit<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#catch-all-route\">Catch-All Route<\/a><\/li>\n\n\n\n<li><a href=\"#seed-query\">Seed Query<\/a><\/li>\n\n\n\n<li><a href=\"#calculating-possible-templates\">Calculating the Possible Templates<\/a><\/li>\n\n\n\n<li><a href=\"#creating-available-templates\">Creating Available Templates<\/a><\/li>\n\n\n\n<li><a href=\"#choosing-a-template\">Choosing A Template<\/a><\/li>\n\n\n\n<li><a href=\"#putting-it-all-together\">Putting it All Together<\/a><\/li>\n\n\n\n<li><a href=\"#loading-the-template\">Loading the Template<\/a><\/li>\n\n\n\n<li><a href=\"#rendering-the-template\">Rendering the Template<\/a><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#querying-data\">Querying Data<\/a>\n<ul class=\"wp-block-list\">\n<li><a href=\"#defining-queries\">Defining Queries<\/a><\/li>\n\n\n\n<li><a href=\"#executing-queries\">Executing Queries<\/a><\/li>\n\n\n\n<li><a href=\"#component-queries\">Component Queries<\/a><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><a href=\"#wrapping-up\">Wrapping up<\/a><\/li>\n<\/ul>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"routing\">Routing<\/h2>\n\n\n\n<p>In the <a href=\"https:\/\/wpengine.com\/builders\/astro-wordpress-routing-and-graphql\/#basics-of-the-template-hierarchy\" target=\"_blank\" rel=\"noreferrer noopener\">article on Astro<\/a>, we discussed 4 major steps in the template hierarchy that must be recreated for a front-end framework. URI =&gt; Data =&gt; Template =&gt; Render: Data + Template.&nbsp;<\/p>\n\n\n\n<p>In Astro, we did this on the server, leveraging a catch-all route. Then, we used Astro\u2019s rewrites to switch to a different route under the hood that acted as our template. The rewrites were important to allow us to keep bundling working correctly. Given that SvelteKit rehydrates and does client-side routing by default, this will be even more important here!&nbsp;<\/p>\n\n\n\n<p>Svelte does have a mechanism for rewrites, or as they call them, <a href=\"https:\/\/svelte.dev\/docs\/kit\/hooks#Universal-hooks-reroute\" target=\"_blank\" rel=\"noreferrer noopener\">reroutes<\/a>. Compared to <a href=\"https:\/\/docs.astro.build\/en\/guides\/routing\/#rewrites\" target=\"_blank\" rel=\"noreferrer noopener\"><code>Astro.rewrite()<\/code><\/a>, this is a very different system. Reroutes are basically dedicated middleware that is executed on every request to the server, rather than a simple function that can be called.&nbsp;<\/p>\n\n\n\n<p>I decided to see how it would work, but I didn\u2019t like it. The implementation had some significant edge cases, which ultimately made it too cumbersome.&nbsp;<\/p>\n\n\n\n<p>Unlike Astro, because reroutes are a <a href=\"https:\/\/svelte.dev\/docs\/kit\/hooks\" target=\"_blank\" rel=\"noreferrer noopener\">SvelteKit Hook<\/a>, any code in this hook runs on every request; it happens before SvelteKit&#8217;s file-system routing. This is important because it means all routes get handled by the <code>reroute<\/code> hooks, not just the ones that make it to a <a href=\"https:\/\/svelte.dev\/docs\/kit\/advanced-routing#Rest-parameters\" target=\"_blank\" rel=\"noreferrer noopener\">catch-all route<\/a>.&nbsp;<\/p>\n\n\n\n<p>The difference being, if I add a route in the file system for content not driven by WordPress, Astro seamlessly handles that before the catch-all route (i.e., file-system router =&gt; catch-all rewrite =&gt; template). In SvelteKit, that same route would still get passed to the reroute hook (i.e., reroute hook =&gt; template). The file-system router has been fully circumvented. Additionally, this means we\u2019re making a request to WordPress on every request to the server. To avoid both these issues, we have to add logic to specifically include and exclude certain routes from being sent to WordPress or avoid the file-system router. In the end, it felt like I was having to invent a new router, on top of SvelteKit.<\/p>\n\n\n\n<p>While we can use a catch-all route in the Svelte router, we\u2019re back to being worried about how our JavaScript will bundle. But SvelteKit has a whole data loading system for routes that Astro doesn\u2019t have. Some quick searching led me to a pretty cool solution.&nbsp;<\/p>\n\n\n\n<p>Usually, frameworks only support returning serializable data from data loading functions like the Next.js <a href=\"https:\/\/nextjs.org\/docs\/pages\/building-your-application\/data-fetching\/get-server-side-props\" target=\"_blank\" rel=\"noreferrer noopener\"><code>getServerSideProps<\/code><\/a> function. This is because the framework has to turn this data into JSON to send it to a browser. SvelteKit offers this same functionality with its server <a href=\"https:\/\/svelte.dev\/docs\/kit\/load\" target=\"_blank\" rel=\"noreferrer noopener\">load function<\/a> in <code>+page.server.js<\/code>.&nbsp;<\/p>\n\n\n\n<p>However, SvelteKit also has a <a href=\"https:\/\/svelte.dev\/docs\/kit\/load#Universal-vs-server\" target=\"_blank\" rel=\"noreferrer noopener\">universal<\/a> load function in <code>+page.js<\/code>! This function is executed both on the server and on the client, so it can support returning non-serializable data\u2014data like a Svelte component!<\/p>\n\n\n\n<p>These load functions support asynchronous calls and thus also support dynamically importing Svelte components with the ESM <code>import()<\/code> <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/import\" target=\"_blank\" rel=\"noreferrer noopener\">syntax<\/a>. We can combine this to make seed queries, load the appropriate template, and render a page.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"template-hierarchy-in-sveltekit\">Template Hierarchy in SvelteKit<\/h2>\n\n\n\n<p>Let\u2019s put this all together in SvelteKit. The steps are:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Get the URI<\/li>\n\n\n\n<li>Determine the template\n<ol class=\"wp-block-list\">\n<li>Making a \u201cseed query\u201d to WordPress to fetch template data<\/li>\n\n\n\n<li>Collecting available templates for rendering<\/li>\n\n\n\n<li>Calculating possible templates the data could use<\/li>\n\n\n\n<li>Figuring out which of the available templates to use based on the prioritized order of most to least specific possible templates<\/li>\n\n\n\n<li>Import the template<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>Fetch more data from WordPress to actually render the template<\/li>\n\n\n\n<li>Merge the selected template and data for rendering<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"catch-all-route\">Catch-All Route<\/h3>\n\n\n\n<p>To get the full URI, we\u2019ll use the SvelteKit file-system router and a catch-all route: <code>src\/routes\/[...uri]\/<\/code>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Note<\/strong>: SvelteKit creates routes based on folder names, not file names, we\u2019ll create specific files here soon.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"seed-query\">Seed Query<\/h3>\n\n\n\n<p>To run the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/lib\/seedQuery.js#L3-L80\" target=\"_blank\" rel=\"noreferrer noopener\">seed query<\/a> in SvelteKit, we\u2019ll use the server load function in <code>+page.server.js<\/code>. This data is serializable. In apps that you build, you can either choose to use the server load function as demonstrated here or the universal load function, depending on the type of page and the type of data that it requires.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"calculating-possible-templates\">Calculating the Possible Templates<\/h3>\n\n\n\n<p>Our app will use <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/lib\/templates.ts#L30-L166\" target=\"_blank\" rel=\"noreferrer noopener\">a function we built for Faust<\/a> to take the data from the seed query and generate a list of possible templates, sorted from most specific to least specific. For example, the templates for a page could look like this: <code>[page-sample-page, page-2, page, singular, index]<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"creating-available-templates\">Creating Available Templates<\/h3>\n\n\n\n<p>Because we\u2019re using dynamic imports to import our WordPress templates, they don\u2019t have to be dedicated routes. However, we do need a single location where they all exist so we can easily import them programmatically. We will use a <code>wp-templates<\/code> directory with our templates inside, like this:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>src\n<\/span><\/span><span class='shcb-loc'><span>  \u21b3 wp-templates\/\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0  \u21b3\u00a0index.svelte\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u21b3\u00a0home.svelte\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u21b3\u00a0archive.svelte\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u21b3\u00a0single.svelte\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>Like with Astro, we can read from the file system to determine which templates are available. This is a bit easier than registering them manually.<\/p>\n\n\n\n<p>Because we\u2019re reading from the file system, this work must be done on the server. The code could be handled directly in our server load function. Alternatively, to use the universal load function, you\u2019d have to create an API endpoint.&nbsp;<\/p>\n\n\n\n<p>Svelte has another superpower: <a href=\"https:\/\/svelte.dev\/docs\/kit\/load#Making-fetch-requests\" target=\"_blank\" rel=\"noreferrer noopener\">a special built-in version of <code>fetch<\/code><\/a>. This is available in load functions and optimizes our request. In the case of server load functions, it doesn\u2019t make an HTTP request but directly executes the JS, meaning we don\u2019t have the overhead of a network request.&nbsp;<\/p>\n\n\n\n<p>This is handy because it allows us to get the available templates from <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/routes\/api\/templates\/%2Bserver.ts\" target=\"_blank\" rel=\"noreferrer noopener\">an endpoint<\/a>. Then, regardless of whether I choose to fetch that data in the universal (<code>+page.ts<\/code>) or server (<code>+page.server.ts<\/code>) load function, it will always work optimally.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"choosing-a-template\">Choosing a template<\/h3>\n\n\n\n<p>We now have a list of possible templates and a list of available templates. Based on the prioritized list of possible templates, we can determine which of the available templates to use.&nbsp;<\/p>\n\n\n\n<p>A <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/lib\/templates.ts#L168-L184\" target=\"_blank\" rel=\"noreferrer noopener\">quick bit of JavaScript<\/a> can compare the list of possible templates (single-post-sample-post, single-post, single, singular, index) to the list of available templates (archive, home, archive, single) and the first match is our template. In this case, <code>single<\/code> is the winner!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"putting-it-all-together\">Putting it all together<\/h3>\n\n\n\n<p>Now that we\u2019ve built all the pieces, we can put this into a <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/lib\/templateHierarchy.ts#L18-L77\" target=\"_blank\" rel=\"noreferrer noopener\">single function<\/a> that takes a URI and returns the template. The <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/routes\/%5B...uri%5D\/%2Bpage.server.ts#L4-L18\" target=\"_blank\" rel=\"noreferrer noopener\">server load function<\/a> for our catch-all route now looks something like this:<\/p>\n\n\n<pre class=\"wp-block-code language-js\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ routes\/&#91;...uri]\/+page.server.js<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> load = <span class=\"hljs-keyword\">async<\/span> (event) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-attr\">params<\/span>: { uri },\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0fetch,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0} = event;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> workingUri = uri || <span class=\"hljs-string\">\"\/\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> templateData = <span class=\"hljs-keyword\">await<\/span> uriToTemplate({ <span class=\"hljs-attr\">uri<\/span>: workingUri, fetch });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-attr\">uri<\/span>: workingUri,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0templateData,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0};\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"loading-the-template\">Loading the template<\/h3>\n\n\n\n<p>We have all the data, now we need to use that data to load our template in the universal load function.<\/p>\n\n\n\n<p>The data returned from a server load function is always resolved before the universal load function is executed and passed to it.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Additionally, because we\u2019re returning data from the universal load function, we\u2019ll lose any data we returned from the server load function. If we want data from both, we must return the server data from the universal function.&nbsp;<\/p>\n\n\n\n<p>Our <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/routes\/%5B...uri%5D\/%2Bpage.ts#L10-L24\" target=\"_blank\" rel=\"noreferrer noopener\">universal load function<\/a> now looks something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ routes\/&#91;...uri]\/+page.js<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> load = <span class=\"hljs-keyword\">async<\/span> ({ data }) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> template = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">import<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-string\">`$wp-templates\/<span class=\"hljs-subst\">${dataToPass.templateData.template?.id}<\/span>.svelte`<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0...data,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-attr\">template<\/span>: template.default,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0};\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"rendering-the-template\">Rendering the template<\/h3>\n\n\n\n<p>Okay, so our load functions are doing their jobs. The final step is to render our template, which is passed to the page route.<\/p>\n\n\n\n<p>Svelte makes this easy in the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/routes\/%5B...uri%5D\/%2Bpage.svelte\" target=\"_blank\" rel=\"noreferrer noopener\">route template<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>\/\/ routes\/&#91;...uri]\/+page.svelte\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"actionscript\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"actionscript\">\u00a0<span class=\"hljs-keyword\">const<\/span> { data } = $props();<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"actionscript\"><\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">data.template<\/span> \/&gt;<\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"querying-data\">Querying Data&nbsp;<\/h2>\n\n\n\n<p>The template hierarchy has now been implemented, and WordPress is routing! Now we need to get some data for our routes.&nbsp;<\/p>\n\n\n\n<p>We could query data from within our Svelte components. However, these queries will be executed on the client and not prerendered at build or on the server.&nbsp;<\/p>\n\n\n\n<p>This is preferably handled in one of our load functions. Either the server or the universal load functions may be used. There is a slight difference in how and when this data is queried.&nbsp;<\/p>\n\n\n\n<p>The most important thing to note is that the universal load function will execute once on the server and again on the client (albeit <a href=\"https:\/\/svelte.dev\/docs\/kit\/load#Making-fetch-requests\" target=\"_blank\" rel=\"noreferrer noopener\">without the need for a redundant network request<\/a>) for server-rendered pages. However, it will only be executed on the client for client-side navigation.<\/p>\n\n\n\n<p>The server load function is only executed on the server, and the response is delivered via JSON to the client for hydration. Client-side navigation means a new JSON payload is delivered.&nbsp;<\/p>\n\n\n\n<p>Depending on the caching headers you opt to set, code complexity, etc., either of these may have performance implications or benefits. In this example, I\u2019m going to fetch this data from the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/routes\/%5B...uri%5D\/%2Bpage.ts#L17\" target=\"_blank\" rel=\"noreferrer noopener\">universal load function<\/a>.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"defining-queries\">Defining Queries<\/h3>\n\n\n\n<p>The first step is creating a place to define queries that need to be executed for a given template. Since we\u2019re already dynamically importing templates, importing the needed queries along with them seems logical. This way, all GraphQL, HTML, JS, and CSS can live within one file.<\/p>\n\n\n\n<p>SvelteKit allows us to export things from the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/wp-templates\/single.svelte#L1-L67\" target=\"_blank\" rel=\"noreferrer noopener\">module-level script tags<\/a> in a component. i.e.;<\/p>\n\n\n<pre class=\"wp-block-code language-svelte\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">module<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>In this script tag, we can export anything we like, just like in regular JS. For our purposes, we need to export an array of objects with at least a query and variables.&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">module<\/span>&gt;<\/span><span class=\"javascript\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">import<\/span> { gql } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$lib\/client\"<\/span>;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> queries = &#91;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0{<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">query<\/span>: gql<span class=\"hljs-string\">`<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/...<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">\u00a0\u00a0\u00a0\u00a0\u00a0`<\/span>,<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">variables<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">event<\/span>) =&gt;<\/span> ({ <span class=\"hljs-attr\">uri<\/span>: event.params.uri }),<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\">\u00a0\u00a0\u00a0},<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\">\u00a0];<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\"><\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>To make things fancier, we\u2019ll want to pass 2 more optional fields, `stream` and `fetchAll`.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">module<\/span>&gt;<\/span><span class=\"javascript\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">import<\/span> { gql } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$lib\/client\"<\/span>;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> queries = &#91;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0{<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">stream<\/span>: <span class=\"hljs-literal\">false<\/span>,<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">query<\/span>: gql<span class=\"hljs-string\">`<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0query ArchiveTemplateNodeQuery($uri: String!) {<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  # My Query \u00a0<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">       }<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">\u00a0\u00a0\u00a0\u00a0\u00a0`<\/span>,<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">variables<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">event<\/span>) =&gt;<\/span> ({ <span class=\"hljs-attr\">uri<\/span>: event.params.uri }),<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\">\u00a0\u00a0\u00a0},<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\">\u00a0];<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\"><\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p><a href=\"https:\/\/svelte.dev\/docs\/kit\/load#Streaming-with-promises\" target=\"_blank\" rel=\"noreferrer noopener\">SvelteKit supports streaming<\/a> data to the browser using promises. This means page load won\u2019t be delayed waiting for that data to load. This <code>stream<\/code> boolean will be <code>false<\/code> by default, but if we do set it to <code>true<\/code>, then the load function will return the promise instead of the resolved data! This is great for non-critical data.<\/p>\n\n\n\n<p>The <code>fetchAll<\/code> field will take a function that receives data from the GraphQL client requesting our query. It will then return an instance of <code>pageInfo<\/code> from a paginated GraphQL request.&nbsp;<\/p>\n\n\n\n<p>In my case, I want to be able to fetch all menu items. I could make this work without pagination by setting my <code>first: $first<\/code> variable in a query quite high. However, this is not recommended. Not only is it fragile\u2014should you ever go over the given estimate, your menus will stop working\u2014but it also reduces the cacheability of your GraphQL requests. Smaller requests will always cache better because if one page of responses gets invalidated, the remaining pages will still be cached responses.<\/p>\n\n\n\n<p>Now that we\u2019ve defined our GraphQL queries, we\u2019ll want to implement a way to execute these queries!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"executing-queries\">Executing Queries<\/h3>\n\n\n\n<p>In our <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/routes\/%5B...uri%5D\/%2Bpage.ts#L17\" target=\"_blank\" rel=\"noreferrer noopener\">universal load function<\/a>, we\u2019re already handling the import of our wp-templates. The Svelte component itself is the <code>default<\/code> export on the imported module. We can access <code>queries<\/code> as a named export.<\/p>\n\n\n\n<p>We will now take the array of queries and pass it to a function that handles executing all the queries with their given config and variables. This will look something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { fetchQueries } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$lib\/queryHandler\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> load = <span class=\"hljs-keyword\">async<\/span> (event) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> { data } = event;\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> template = <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">import<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-string\">`$wp\/<span class=\"hljs-subst\">${data.templateData.template.id}<\/span>.svelte`<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> queryResults = <span class=\"hljs-keyword\">await<\/span> fetchQueries({ <span class=\"hljs-attr\">queries<\/span>: template.queries, event });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0...data,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-attr\">template<\/span>: template.default,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-attr\">graphqlData<\/span>: queryResults,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0};\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This function will correctly fetch all available data for paginated queries or streaming back queries we want streamed!<br><\/p>\n\n\n\n<p>This data is returned in an object with a key matching the name of or GraphQL query. For example, if my query is <code>query furtherReading($uri: String!) {...}<\/code> our <code>graphqlData<\/code> object will be:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>{\n<\/span><\/span><span class='shcb-loc'><span>  furtherReading: {\n<\/span><\/span><span class='shcb-loc'><span>    name: furtherReading,\n<\/span><\/span><span class='shcb-loc'><span>    variables: { uri: \u201courUri\u201d },\n<\/span><\/span><span class='shcb-loc'><span>    response: { \u2026 },\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p>If you streamed a response, then <code>response<\/code> will be a promise you have to use Svelte\u2019s <a href=\"https:\/\/svelte.dev\/docs\/svelte\/await\" target=\"_blank\" rel=\"noreferrer noopener\"><code>{#await}<\/code> syntax<\/a> with.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"component-queries\">Component Queries<\/h3>\n\n\n\n<p>For this example, I implemented a similar system for components. The distinction is that components may be used on one or more templates. I used a <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/components\/Nav.svelte#L1-L27\" target=\"_blank\" rel=\"noreferrer noopener\">component-level query<\/a> in my <code>Nav.svelte<\/code> to populate my navigation menu.&nbsp;<\/p>\n\n\n\n<p>I opted to export a single query from a component, e.g.:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>\/\/ src\/components\/Nav.svelte\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">module<\/span>&gt;<\/span><span class=\"javascript\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">import<\/span> { gql } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$lib\/client\"<\/span>;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> query = {<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">query<\/span>: gql<span class=\"hljs-string\">`<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/...<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-string\">\u00a0\u00a0\u00a0\u00a0\u00a0`<\/span>,<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">variables<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">event<\/span>) =&gt;<\/span> ({ <span class=\"hljs-attr\">uri<\/span>: event.params.uri }),<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\">\u00a0\u00a0\u00a0};<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><span class=\"hljs-function\">\u00a0<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><br><\/p>\n\n\n\n<p>This query can now be passed to any WordPress template that needs it executed. For the nav, I opted to execute this in the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/routes\/%5B...uri%5D\/%2Blayout.ts\">layout load function<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ src\/routes\/&#91;...]\/+layout.js<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { fetchQueries } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$lib\/queryHandler\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { query <span class=\"hljs-keyword\">as<\/span> NavQuery } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$components\/Nav.svelte\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> load = <span class=\"hljs-keyword\">async<\/span> (event) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">const<\/span> queryResults = <span class=\"hljs-keyword\">await<\/span> fetchQueries({ <span class=\"hljs-attr\">queries<\/span>: &#91;NavQuery], event });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0<span class=\"hljs-keyword\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0<span class=\"hljs-attr\">layoutData<\/span>: queryResults,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0};\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>To avoid passing this data down through several layers of components as props to get it to the nav component, we can access it directly from the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/components\/Nav.svelte#L32-L38\" target=\"_blank\" rel=\"noreferrer noopener\">page store in the nav component<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code language-svelte\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span>&gt;<\/span><span class=\"javascript\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">import<\/span> { flatListToHierarchical } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$lib\/wpgraphql\"<\/span>;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">import<\/span> { page } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"$app\/state\"<\/span>;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0<span class=\"hljs-keyword\">const<\/span> menu = $derived(<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0flatListToHierarchical(<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0\u00a0\u00a0page.data.layoutData.headerNavQuery.response.data.menu.menuItems.nodes<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0\u00a0\u00a0)<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\">\u00a0);<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"javascript\"><\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">nav<\/span>&gt;<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">\u00a0\u00a0\u00a0{#each menu as item (item.id)}<\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">NavStructure<\/span> <span class=\"hljs-attr\">navItem<\/span>=<span class=\"hljs-string\">{item}<\/span> \/&gt;<\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">\u00a0\u00a0\u00a0{\/each}<\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">nav<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"wrapping-up\">Wrapping up<\/h2>\n\n\n\n<p>Et. Voila! We have implemented the template hierarchy and data fetching for our templates! There are some edge cases, but we have a solid foundation to work from!<\/p>\n\n\n\n<p>SvelteKit\u2019s load functions provide us with a unique set of tools. While the specifics of implementing the template hierarchy varied from Astro, the general steps remained the same!&nbsp;<\/p>\n\n\n\n<p>Personally, I feel like there are a few extra steps required, especially when fetching data in SvelteKit. Astro templates not being rendered in the client simplifies things significantly. I had less to think about and fewer tradeoffs to consider.&nbsp;<\/p>\n\n\n\n<p>That said, SvelteKit still feels far simpler than most Next.js\/React applications I\u2019ve built. The routing and data loading can be complicated, or I can opt to keep it simple. As the developer, I feel like I have more levers at my disposal to control the exact outcome, which makes SvelteKit quite powerful but comes with tradeoffs.&nbsp;<\/p>\n\n\n\n<p>I have a side project I\u2019ll likely be using Svelte + Astro on soon! What do you think of this approach? What\u2019s your favorite framework to use with headless WordPress?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Svelte is my favorite framework, and today we\u2019re exploring using SvelteKit to handle routing and data fetching for headless WordPress! This article is part of a series exploring implementing the [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_EventAllDay":false,"_EventTimezone":"","_EventStartDate":"","_EventEndDate":"","_EventStartDateUTC":"","_EventEndDateUTC":"","_EventShowMap":false,"_EventShowMapLink":false,"_EventURL":"","_EventCost":"","_EventCostDescription":"","_EventCurrencySymbol":"","_EventCurrencyCode":"","_EventCurrencyPosition":"","_EventDateTimeSeparator":"","_EventTimeRangeSeparator":"","_EventOrganizerID":[],"_EventVenueID":[],"_OrganizerEmail":"","_OrganizerPhone":"","_OrganizerWebsite":"","_VenueAddress":"","_VenueCity":"","_VenueCountry":"","_VenueProvince":"","_VenueState":"","_VenueZip":"","_VenuePhone":"","_VenueURL":"","_VenueStateProvince":"","_VenueLat":"","_VenueLng":"","_VenueShowMap":false,"_VenueShowMapLink":false,"footnotes":""},"categories":[23],"tags":[51,26],"class_list":["post-31881","post","type-post","status-publish","format-standard","hentry","category-headless","tag-sveltekit","tag-wpgraphql"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>SvelteKit + WordPress: Routing and GraphQL - Builders<\/title>\n<meta name=\"description\" content=\"Are you interested in building a headless WordPress site using SvelteKit? SvelteKit makes for an amazing developer experience with headless WordPress. Learn how we handled routing and data fetching using WPGraphQL!\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"SvelteKit + WordPress: Routing and GraphQL\" \/>\n<meta property=\"og:description\" content=\"Are you interested in building a headless WordPress site using SvelteKit? SvelteKit makes for an amazing developer experience with headless WordPress. Learn how we handled routing and data fetching using WPGraphQL!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/\" \/>\n<meta property=\"og:site_name\" content=\"Builders\" \/>\n<meta property=\"article:published_time\" content=\"2025-05-13T01:19:26+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-05-13T16:35:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/05\/WPEBuildersOpenSourceProjects.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Alex Moon\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@moon_meister\" \/>\n<meta name=\"twitter:site\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alex Moon\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/\"},\"author\":{\"name\":\"Alex Moon\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/868e9d9f8b846000f45cc7a25f46255a\"},\"headline\":\"SvelteKit + WordPress: Routing and GraphQL\",\"datePublished\":\"2025-05-13T01:19:26+00:00\",\"dateModified\":\"2025-05-13T16:35:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/\"},\"wordCount\":2258,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"keywords\":[\"SvelteKit\",\"WPGraphQL\"],\"articleSection\":[\"Headless\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/\",\"name\":\"SvelteKit + WordPress: Routing and GraphQL - Builders\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\"},\"datePublished\":\"2025-05-13T01:19:26+00:00\",\"dateModified\":\"2025-05-13T16:35:11+00:00\",\"description\":\"Are you interested in building a headless WordPress site using SvelteKit? SvelteKit makes for an amazing developer experience with headless WordPress. Learn how we handled routing and data fetching using WPGraphQL!\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/sveltekit-wordpress-routing-and-graphql\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"SvelteKit + WordPress: Routing and GraphQL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"name\":\"Builders\",\"description\":\"Reimagining the way we build with WordPress.\",\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\",\"name\":\"WP Engine\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"contentUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"width\":348,\"height\":68,\"caption\":\"WP Engine\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/wpebuilders\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCh1WuL54XFb9ZI6m6goFv1g\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/868e9d9f8b846000f45cc7a25f46255a\",\"name\":\"Alex Moon\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e751c6c4033ab32c97e38f1c218b0f11070378a7c7b797539c497ca289b4f205?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e751c6c4033ab32c97e38f1c218b0f11070378a7c7b797539c497ca289b4f205?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e751c6c4033ab32c97e38f1c218b0f11070378a7c7b797539c497ca289b4f205?s=96&d=mm&r=g\",\"caption\":\"Alex Moon\"},\"description\":\"Alex Moon is a Developer Advocate at WP Engine. He's been working in Open Source for over a decade and headless WordPress since the early days of WP GraphQL. When he's not writing code he's often found in the mountains or sailing the seas. Follow him on X or Bluesky for more headless WordPress goodness!\",\"sameAs\":[\"https:\\\/\\\/gurugoes.net\",\"https:\\\/\\\/x.com\\\/moon_meister\"],\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/author\\\/alexmoon-2-2-2-2-2-2-2-2-2-2-2-2-2-2\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"SvelteKit + WordPress: Routing and GraphQL - Builders","description":"Are you interested in building a headless WordPress site using SvelteKit? SvelteKit makes for an amazing developer experience with headless WordPress. Learn how we handled routing and data fetching using WPGraphQL!","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/","og_locale":"en_US","og_type":"article","og_title":"SvelteKit + WordPress: Routing and GraphQL","og_description":"Are you interested in building a headless WordPress site using SvelteKit? SvelteKit makes for an amazing developer experience with headless WordPress. Learn how we handled routing and data fetching using WPGraphQL!","og_url":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/","og_site_name":"Builders","article_published_time":"2025-05-13T01:19:26+00:00","article_modified_time":"2025-05-13T16:35:11+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/05\/WPEBuildersOpenSourceProjects.png","type":"image\/png"}],"author":"Alex Moon","twitter_card":"summary_large_image","twitter_creator":"@moon_meister","twitter_site":"@wpebuilders","twitter_misc":{"Written by":"Alex Moon","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/#article","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/"},"author":{"name":"Alex Moon","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/868e9d9f8b846000f45cc7a25f46255a"},"headline":"SvelteKit + WordPress: Routing and GraphQL","datePublished":"2025-05-13T01:19:26+00:00","dateModified":"2025-05-13T16:35:11+00:00","mainEntityOfPage":{"@id":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/"},"wordCount":2258,"commentCount":0,"publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"keywords":["SvelteKit","WPGraphQL"],"articleSection":["Headless"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/","url":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/","name":"SvelteKit + WordPress: Routing and GraphQL - Builders","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/#website"},"datePublished":"2025-05-13T01:19:26+00:00","dateModified":"2025-05-13T16:35:11+00:00","description":"Are you interested in building a headless WordPress site using SvelteKit? SvelteKit makes for an amazing developer experience with headless WordPress. Learn how we handled routing and data fetching using WPGraphQL!","breadcrumb":{"@id":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wpengine.com\/builders\/"},{"@type":"ListItem","position":2,"name":"SvelteKit + WordPress: Routing and GraphQL"}]},{"@type":"WebSite","@id":"https:\/\/wpengine.com\/builders\/#website","url":"https:\/\/wpengine.com\/builders\/","name":"Builders","description":"Reimagining the way we build with WordPress.","publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wpengine.com\/builders\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/wpengine.com\/builders\/#organization","name":"WP Engine","url":"https:\/\/wpengine.com\/builders\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/","url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","contentUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","width":348,"height":68,"caption":"WP Engine"},"image":{"@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/wpebuilders","https:\/\/www.youtube.com\/channel\/UCh1WuL54XFb9ZI6m6goFv1g"]},{"@type":"Person","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/868e9d9f8b846000f45cc7a25f46255a","name":"Alex Moon","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/e751c6c4033ab32c97e38f1c218b0f11070378a7c7b797539c497ca289b4f205?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/e751c6c4033ab32c97e38f1c218b0f11070378a7c7b797539c497ca289b4f205?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e751c6c4033ab32c97e38f1c218b0f11070378a7c7b797539c497ca289b4f205?s=96&d=mm&r=g","caption":"Alex Moon"},"description":"Alex Moon is a Developer Advocate at WP Engine. He's been working in Open Source for over a decade and headless WordPress since the early days of WP GraphQL. When he's not writing code he's often found in the mountains or sailing the seas. Follow him on X or Bluesky for more headless WordPress goodness!","sameAs":["https:\/\/gurugoes.net","https:\/\/x.com\/moon_meister"],"url":"https:\/\/wpengine.com\/builders\/author\/alexmoon-2-2-2-2-2-2-2-2-2-2-2-2-2-2\/"}]}},"_links":{"self":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31881","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/comments?post=31881"}],"version-history":[{"count":0,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31881\/revisions"}],"wp:attachment":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/media?parent=31881"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/categories?post=31881"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/tags?post=31881"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}