<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Apollo GraphQL</title>
    <description>The latest articles on Forem by Apollo GraphQL (@apollographql).</description>
    <link>https://forem.com/apollographql</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1737%2F8e4fbb8b-f979-4740-8a27-a23451270d0f.jpg</url>
      <title>Forem: Apollo GraphQL</title>
      <link>https://forem.com/apollographql</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/apollographql"/>
    <language>en</language>
    <item>
      <title>Meet the Builders: Highlights from the MCP Server Builder Meetup</title>
      <dc:creator>Amanda</dc:creator>
      <pubDate>Tue, 08 Jul 2025 16:33:39 +0000</pubDate>
      <link>https://forem.com/apollographql/meet-the-builders-highlights-from-the-mcp-server-builder-meetup-5821</link>
      <guid>https://forem.com/apollographql/meet-the-builders-highlights-from-the-mcp-server-builder-meetup-5821</guid>
      <description>&lt;p&gt;On June 18th, we hosted our very first MCP Server Builder Meetup in San Francisco, bringing together engineers, tinkerers, and early adopters to explore the future of building AI-native developer experiences with Model Context Protocol (MCP).&lt;/p&gt;

&lt;p&gt;This was more than just a showcase of cool demos. It marked the launch of a new community of developers building for and with LLMs using MCP servers. The energy in the room made one thing clear: this community is ready to build what's next.&lt;/p&gt;

&lt;p&gt;Keep reading for a recap of all the demos with links to the full sessions.  Our next events in the series are on July 29th in NYC and July 31st in San Francisco.  We hope to see you there. RSVP and subscribe in our &lt;a href="https://lu.ma/mcp-server" rel="noopener noreferrer"&gt;Luma calendar&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jordan Bergero (Block): Building Reliable MCP Servers with Goose &amp;amp; MCP Tool Layering in Square MCP Server
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhztmsfjb35nqhkd5p1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhztmsfjb35nqhkd5p1f.png" alt="Jordan bergero of block" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jordan shared how his team turned an internal hack week project into a production-grade MCP server at Square, which now powers real developer workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Highlights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduced Goose: an open source, LLM-agnostic tool for running and testing MCP servers locally in a GUI or terminal experience.&lt;/li&gt;
&lt;li&gt;Explained Square’s "layered approach" (discover → plan → execute) to reduce API confusion. This structured layering helps make results predictable and reproducible.&lt;/li&gt;
&lt;li&gt;Goose read a raw .txt file of invoice notes, parsed out customer and payment data, called multiple Square API endpoints (customer create, order create, invoice publish), and emailed real invoice all from a single prompt.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; An impressive real-world example of turning API chaos into structured tool behavior. &lt;/p&gt;

&lt;p&gt;Check out Jordan’s &lt;a href="https://youtu.be/tA0-xf85FuM?feature=shared" rel="noopener noreferrer"&gt;full session on YouTube&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/jordan-bergero" rel="noopener noreferrer"&gt;connect with them on LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Melissa Herrera (Langflow): Langflow as a Visual MCP Client and Server
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31ew0wxgb9iusldb7btr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31ew0wxgb9iusldb7btr.png" alt="Melissa Hererra" width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
Melissa brought the energy and delivered a lightning-fast tour of Langflow, a visual IDE for building agent workflows with drag-and-drop ease.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Highlights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MCP Client &amp;amp; Server: Langflow can act as both an MCP client (calling tools via agent workflows) and a server (exposing your flows as tools), letting you chain, compose, and republish.&lt;/li&gt;
&lt;li&gt;Multi-agent architecture: She showcased a resume enhancer app that parsed a resume, queried live job market data via Tavily, and returned improvements all orchestrated across multiple agents.&lt;/li&gt;
&lt;li&gt;Tool Reuse: Demonstrated turning any Langflow component into a reusable tool and exporting it as part of a server bundle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; For devs or visual thinkers, Langflow is a developer-friendly launchpad for building composable, LLM-native tools.&lt;/p&gt;

&lt;p&gt;Check out Melissa's &lt;a href="https://youtu.be/EOXPaLF8_gw?feature=shared" rel="noopener noreferrer"&gt;full session on YouTube&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/herrera-melissa" rel="noopener noreferrer"&gt;connect with them on LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lizzie Siegle (Cloudflare): Podcast Generator with Workers + MCP
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwp.apollographql.com%2Fwp-content%2Fuploads%2F2025%2F07%2FScreenshot-2025-07-01-at-3.15.52%25E2%2580%25AFPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwp.apollographql.com%2Fwp-content%2Fuploads%2F2025%2F07%2FScreenshot-2025-07-01-at-3.15.52%25E2%2580%25AFPM.png" alt="Lizzie Siegle" width="800" height="446"&gt;&lt;/a&gt;&lt;br&gt;
Lizzie brought some joy (and a surprise) with her talk on building and deploying fun, voice-powered MCP apps on Cloudflare Workers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Highlights&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless podcast generator: Combined Claude, Workers AI, and a Cloudflare D1 SQL database to build a podcast generator that outputs both audio and script content.&lt;/li&gt;
&lt;li&gt;End-to-end deployment: Demonstrated how to go from zero to a deployed MCP server using Cloudflare’s click-to-deploy button. Most code was auto-generated, including durable object support.&lt;/li&gt;
&lt;li&gt;Tool listing &amp;amp; persistence: Saved generated podcast metadata and audio URLs to SQL, and exposed a tool to query prior results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Cloudflare’s infra stack is well-suited for lightweight AI applications. If you want a fast way to host, persist, and serve LLM workflows globally, this is a great blueprint.&lt;/p&gt;

&lt;p&gt;Check out Lizzie's &lt;a href="https://www.youtube.com/watch?v=vjgHr5temTM&amp;amp;list=PLpi1lPB6opQyLjI99abvDXZ-OsWbO4Yt4&amp;amp;index=4" rel="noopener noreferrer"&gt;full session on YouTube&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/elsiegle/" rel="noopener noreferrer"&gt;connect with them on LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tobin South (WorkOS): Securing MCP Servers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwp.apollographql.com%2Fwp-content%2Fuploads%2F2025%2F07%2FScreenshot-2025-07-01-at-3.17.38%25E2%2580%25AFPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwp.apollographql.com%2Fwp-content%2Fuploads%2F2025%2F07%2FScreenshot-2025-07-01-at-3.17.38%25E2%2580%25AFPM.png" alt="Tobin South" width="800" height="448"&gt;&lt;/a&gt;&lt;br&gt;
Tobin delivered a high-impact talk focused on the security foundations of MCP infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Highlights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highlighted common authentication pitfalls in community-deployed MCP servers and the growing need for OAuth and SSO.&lt;/li&gt;
&lt;li&gt;Gave a crash course in OAuth and SSO for MCP, including how to support dynamic client registration properly.&lt;/li&gt;
&lt;li&gt;Demoed a secure, OAuth-powered MCP server for ordering custom swag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; If you’re shipping MCP servers to users or enterprises, this talk is your must-watch.&lt;/p&gt;

&lt;p&gt;Check out Tobins &lt;a href="https://youtu.be/Zk3V0QE9Uho?feature=shared" rel="noopener noreferrer"&gt;full session on YouTube&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/tobinsouth" rel="noopener noreferrer"&gt;connect with them on LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Michael Watson (Apollo): Token-Efficient MCP Servers with GraphQL
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwp.apollographql.com%2Fwp-content%2Fuploads%2F2025%2F07%2FScreenshot-2025-07-01-at-3.23.40%25E2%2580%25AFPM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwp.apollographql.com%2Fwp-content%2Fuploads%2F2025%2F07%2FScreenshot-2025-07-01-at-3.23.40%25E2%2580%25AFPM.png" alt="Michael Watson" width="800" height="446"&gt;&lt;/a&gt;&lt;br&gt;
Watson closed the night with a deep dive into how token efficiency and schema-first tooling can make LLM interactions smarter and cheaper.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Highlights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token bloat audit: Analyzed GitHub's MCP server responses which produced 8.5k+ tokens per tool call caused by duplicate and irrelevant fields.&lt;/li&gt;
&lt;li&gt;Selective GraphQL queries: Used GraphQL selection sets to omit unneeded data, reducing token usage by 75% to about 2,000 tokens.&lt;/li&gt;
&lt;li&gt;Hot-reloadable tools: Showed how the &lt;a href="https://www.apollographql.com/docs/apollo-mcp-server" rel="noopener noreferrer"&gt;Apollo MCP server&lt;/a&gt; can load tools directly from .graphql files with no build step needed.&lt;/li&gt;
&lt;li&gt;Live demo: Used Goose to introspect GitHub’s GraphQL schema and auto-generate a working tool in minutes, then hot-loaded it into the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; GraphQL-first design gives you clean, typed, and cost-efficient tooling which is a superpower for scaling MCP use.&lt;/p&gt;

&lt;p&gt;Check out Watson's &lt;a href="https://youtu.be/_7J1A4IXh-s?feature=shared" rel="noopener noreferrer"&gt;full session on YouTube&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/michael-watson-%F0%9F%96%A5-85844442" rel="noopener noreferrer"&gt;connect with them on LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next: Join the MCP Server Builder Community
&lt;/h2&gt;

&lt;p&gt;This was just the beginning. Our mission is to support and grow a community of developers building smarter MCP tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out our MCP Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apollo's own &lt;a href="https://github.com/apollographql/apollo-mcp-server" rel="noopener noreferrer"&gt;MCP server is open source&lt;/a&gt; and we have a &lt;a href="https://www.apollographql.com/tutorials/intro-mcp-graphql" rel="noopener noreferrer"&gt;full tutorial on Odyssey&lt;/a&gt; that will get you up and running building your own tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Join future events&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We host meetups regularly. Check out our &lt;a href="https://lu.ma/mcp-server" rel="noopener noreferrer"&gt;Luma event page&lt;/a&gt; to RSVP for the next one in SF, NYC, or virtually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get involved&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Got something to show? Built a server? Join our talks or share in the &lt;a href="https://community.apollographql.com/c/mcp-server/41" rel="noopener noreferrer"&gt;Apollo Community&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>graphql</category>
      <category>community</category>
    </item>
    <item>
      <title>From REST to GraphQL in minutes with prebuilt Connectors</title>
      <dc:creator>Amanda</dc:creator>
      <pubDate>Fri, 13 Jun 2025 15:25:32 +0000</pubDate>
      <link>https://forem.com/apollographql/from-rest-to-graphql-in-minutes-with-prebuilt-connectors-nkf</link>
      <guid>https://forem.com/apollographql/from-rest-to-graphql-in-minutes-with-prebuilt-connectors-nkf</guid>
      <description>&lt;p&gt;We’ve all been there: vague API docs, an outdated OpenAPI spec, and a half-buried list of endpoints that leave you guessing. With prebuilt &lt;a href="https://www.apollographql.com/docs/graphos/connectors" rel="noopener noreferrer"&gt;REST Connectors&lt;/a&gt;, you skip the guesswork. Just download the schema files, run your graph locally, and start querying live data with GraphQL.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll use the &lt;a href="https://thespacedevs.com/llapi" rel="noopener noreferrer"&gt;Space Devs Launch Library 2&lt;/a&gt; in the &lt;a href="https://github.com/apollographql/connectors-community/tree/main" rel="noopener noreferrer"&gt;Connectors Community repo&lt;/a&gt; as an example to show how easy it is to integrate a REST API into your graph in minutes and how you can tailor it to meet your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;-Create an &lt;a href="https://studio.apollographql.com/?_gl=1*1i71c12*_gcl_aw*R0NMLjE3NDk4MjU4MTguQ2owS0NRandtS19DQmhDRUFSSXNBTUt3Y0Q3alNWdTlxbVUwV2NwSmhYbThWR2pWM2V0TERaWFN2Z25HeHBTYVdtLS1aTFd0S25YRmwzUWFBc0J3RUFMd193Y0I.*_gcl_au*NjA0MzcxODg1LjE3NDU4NzE2ODA." rel="noopener noreferrer"&gt;Apollo Studio&lt;/a&gt; account. This allows you to create and manage your graph, providing the necessary credentials, APOLLO_KEY and APOLLO_GRAPH_REF (more on those later…) which the Apollo Router uses to fetch the supergraph schema and run locally.&lt;br&gt;
-&lt;a href="https://www.apollographql.com/docs/rover/getting-started#installation-methods" rel="noopener noreferrer"&gt;Install&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/rover/configuring" rel="noopener noreferrer"&gt;authenticate&lt;/a&gt; Rover CLI which we will use for configuring our graph and running Apollo Router locally.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;In your working directory, initialize your schema with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rover init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select the option to create a new graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ogqiqgrv2t7gwv702z7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ogqiqgrv2t7gwv702z7.png" alt="image of the terminal with create a new graph selected" width="800" height="280"&gt;&lt;/a&gt;&lt;br&gt;
Then “Start a graph with one or more REST APIs”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepfta3xr068a6ykj9e5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepfta3xr068a6ykj9e5a.png" alt="inage of the terminal with selecti option: create a new graph" width="800" height="302"&gt;&lt;/a&gt;&lt;br&gt;
Next you will name your graph and Rover will generate you a new ID for your graph and APOLLO_KEY.  Once your APOLLO_KEY and GRAPH_REF are generated,you can start the router with the command provided, but I would recommend putting these values in an VSCode &amp;gt; settings.json. The configuration below will ensure these environment variables are loaded into any new VSCode terminal windows you open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;{&lt;/span&gt;

 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;terminal.integrated.profiles.osx"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;

   &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graphos"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;

     &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zsh"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;

     &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-l"&lt;/span&gt;&lt;span class="pi"&gt;],&lt;/span&gt;

     &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;env"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;

       &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APOLLO_KEY"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_KEY&amp;gt;"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;

       &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;APOLLO_GRAPH_REF"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;GRAPH_REF&amp;gt;"&lt;/span&gt;

     &lt;span class="pi"&gt;}&lt;/span&gt;

   &lt;span class="pi"&gt;}&lt;/span&gt;

 &lt;span class="pi"&gt;},&lt;/span&gt;

 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;terminal.integrated.defaultProfile.osx"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graphos"&lt;/span&gt;

&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rover generated a supergraph.yaml file for you. You can delete this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using thespacedevs prebuilt connector
&lt;/h2&gt;

&lt;p&gt;Next, navigate to &lt;a href="https://github.com/apollographql/connectors-community/tree/main/connectors/thespacedevs" rel="noopener noreferrer"&gt;thespacedevs folder&lt;/a&gt; in the Connectors Community repo. This Connector is an implementation of the &lt;a href="https://thespacedevs.com/llapi" rel="noopener noreferrer"&gt;Space Devs Launch Library 2&lt;/a&gt;.  Download all of the graphql files here and the supergraph.yaml.  You can add these to the root of your project folder. Rover generates test schema files and a supergraph. Anything that is duplicated or you do not need can be removed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy28ybqbvql9kjd3lveqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy28ybqbvql9kjd3lveqs.png" alt="Image of the space devs repo in github with files surrounded by a red box" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, open your terminal and run the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rover dev --supergraph-config supergraph.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Your graph is live at &lt;a href="http://localhost:4000" rel="noopener noreferrer"&gt;http://localhost:4000&lt;/a&gt;. Pop it open in your browser to explore the queries in Apollo Sandbox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0d3tdi2i33u9iaoziuhk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0d3tdi2i33u9iaoziuhk.gif" alt="gif of launches query executing" width="640" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying a prebuilt connector
&lt;/h2&gt;

&lt;p&gt;You can use a prebuilt Connector as is or modify it for your application.  For example, I’d like to add more information about what a valid search string looks like for upcoming launches.&lt;/p&gt;

&lt;p&gt;In launches.graphql find the upcoming launches query and place this comment above it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Fetches a list of upcoming launches by agency name. 
Try Starlink, Nasa, or Exa for example.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="nf"&gt;upcomingLaunches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;LaunchConnection&lt;/span&gt;

   &lt;span class="nd"&gt;@connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;

     &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llv2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

     &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

       &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/launches/upcoming/?search={$args.search}&amp;amp;limit={$args.limit}&amp;amp;offset={$args.offset}&amp;amp;format=json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;rest&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your file and you will see your new comment as context in the query builder.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fequwlkkqemkd22nvojrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fequwlkkqemkd22nvojrp.png" alt="Sandbox with new description added under the upcoming launches query" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;Once you have the prebuilt Connector ready for your use case now you are ready to integrate it to a client.  To continue exploring where to use this Connector, take a look at &lt;a href="https://www.apollographql.com/blog/simplify-your-rest-api-logic-in-react-with-connectors-for-rest-apis-and-graphql" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt; to learn how to replace traditional REST API calls in a React and Next.js application. Or write your very first MCP tool using the &lt;a href="https://www.apollographql.com/docs/apollo-mcp-server" rel="noopener noreferrer"&gt;Apollo MCP server&lt;/a&gt;. There’s even a &lt;a href="https://github.com/apollographql/apollo-mcp-server/tree/main/graphql/TheSpaceDevs" rel="noopener noreferrer"&gt;ready to go example&lt;/a&gt; using The Space Devs Api for MCP you can use.&lt;/p&gt;

&lt;p&gt;There are many prebuilt Connectors ready to use or extend including &lt;a href="https://github.com/apollographql/connectors-community/tree/main/connectors/stripe" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt;, &lt;a href="https://github.com/apollographql/connectors-community/tree/main/connectors/openai" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, &lt;a href="https://github.com/apollographql/connectors-community/tree/main/connectors/aws" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;, and more.  Motivated to build your own Connectors to share with the community? We welcome contributions! Learn how to submit your Connector in the &lt;a href="https://github.com/apollographql/connectors-community" rel="noopener noreferrer"&gt;Connectors Community repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>restapi</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Simplify Your REST API Logic in React with Connectors for REST APIs and GraphQL</title>
      <dc:creator>Amanda</dc:creator>
      <pubDate>Thu, 29 May 2025 20:10:27 +0000</pubDate>
      <link>https://forem.com/apollographql/simplify-your-rest-api-logic-in-react-with-connectors-for-rest-apis-and-graphql-5ab7</link>
      <guid>https://forem.com/apollographql/simplify-your-rest-api-logic-in-react-with-connectors-for-rest-apis-and-graphql-5ab7</guid>
      <description>&lt;p&gt;If you’ve built a React or Next.js app that talks to multiple REST APIs, you’ve probably got a file like actions.ts set up as a central spot for fetch calls to public services like the USGS Earthquake API or Nominatim’s reverse geocoder. It works, but the code often ends up repetitive, brittle, and difficult to maintain or scale when different endpoints need to talk to each other.&lt;/p&gt;

&lt;p&gt;In this post, we’re going to take that same setup and clean it up with a GraphQL layer powered by &lt;a href="https://www.apollographql.com/docs/graphos/connectors" rel="noopener noreferrer"&gt;Apollo Connectors&lt;/a&gt;. Instead of orchestrating data in actions.ts, we’ll define a GraphQL schema that does the work for us. Using a declarative configuration, we’ll unify earthquake and location data in a single query with no need for custom resolvers or custom backend logic. The goal: to make your frontend simpler and your data fetching smarter, without giving up the REST services and patterns you already know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create an &lt;a href="https://studio.apollographql.com/" rel="noopener noreferrer"&gt;Apollo Studio&lt;/a&gt; account. This allows you to create and manage your graph, providing the necessary credentials APOLLO_KEY and APOLLO_GRAPH_REF (more on those later…), which the Apollo Router uses to fetch the supergraph schema and run locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.apollographql.com/docs/rover/getting-started#installation-methods" rel="noopener noreferrer"&gt;Install&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/rover/configuring" rel="noopener noreferrer"&gt;authenticate&lt;/a&gt; Rover CLI, which we will use for configuring our graph and running Apollo Router locally.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;In the directory you want to host your schema in run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rover init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts in the CLI to create a new project.&lt;/p&gt;

&lt;p&gt;Rover will generate a command for you that contains your APOLLO_KEY and GRAPH_REF.  You can start the Router with the command provided, but I would recommend putting these values in an VSCode &amp;gt; settings.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"terminal.integrated.profiles.osx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"graphos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zsh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-l"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"APOLLO_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR KEY&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"APOLLO_GRAPH_REF"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;YOUR GRAPH REF&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"terminal.integrated.defaultProfile.osx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"graphos"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next create a file at the root called router.yaml and place this code in it.  This is to override CORS errors while working in dev. &lt;a href="https://www.apollographql.com/docs/graphos/routing/configuration/yaml#cors" rel="noopener noreferrer"&gt;This is not for use in production.&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;allow_any_origin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you haven’t already open your project in VSCode or your IDE of choice and from the terminal run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;rover dev --router-config router.yaml --supergraph-config supergraph.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the Schema
&lt;/h2&gt;

&lt;p&gt;Create a new file called earthquake.graphql and paste the following code.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If you prefer, &lt;a href="https://github.com/apollographql/connectors-community/blob/main/connectors/usgs/earthquakes-nominatum/earthquake-simple.graphql" rel="noopener noreferrer"&gt;the completed schema can be found here&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://specs.apollo.dev/federation/v2.10&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# Enable this schema to use Apollo Federation features
&lt;/span&gt;&lt;span class="nd"&gt;@link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="c1"&gt;# Enable this schema to use Apollo Connectors
&lt;/span&gt;   &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://specs.apollo.dev/connect/v0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@connect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usgs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://earthquake.usgs.gov/fdsnws/event/1/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="n"&gt;EarthquakeProperties&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="n"&gt;mag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Float&lt;/span&gt;
 &lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;
 &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Float&lt;/span&gt;
 &lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Float&lt;/span&gt;
 &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;
 &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="n"&gt;EarthquakeGeometry&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="c1"&gt;#this returns 3 values - lon,lat,depth in km
&lt;/span&gt; &lt;span class="n"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Earthquake&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
 &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EarthquakeProperties&lt;/span&gt;
 &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EarthquakeGeometry&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;recentEarthquakes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="n"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="n"&gt;maxRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Int&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Earthquake&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="nd"&gt;@connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usgs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
     &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query?format=geojson&amp;amp;latitude={$args.lat}&amp;amp;longitude={$args.lon}&amp;amp;maxradiuskm={$args.maxRadius}&amp;amp;limit={$args.limit}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
       $.features
      {
       properties{
         mag
         place
         time
         updated
         url
         title
       }
       geometry{
        coordinates
       }
       id
     }

     &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two directives following @ link are required at the top of any file to enable Connectors and federation. The @ source directive is used to point to our base URL for USGS earthquakes.&lt;/p&gt;

&lt;p&gt;Underneath this there are three types: Earthquake, EarthquakeProperties, and EarthquakeGeometry. Here we are defining what we want to make available in our schema. You can build this however you want and include as much of the REST API as is valuable for your application. Here you will also define all the types and what values are nullable.&lt;/p&gt;

&lt;p&gt;Finally you will see the query type. In this schema we only have one query, but you can build out as many as you need. For example, while this calls recent earthquakes and takes in four parameters, you may also want to include a query for retrieving one earthquake by Id.  What you design in this schema is dependent upon the needs of your application and frontend team. Inside this query, you will see the @connect directive which is where you declare what populates this query and how it will return in the selection set below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a second REST API
&lt;/h2&gt;

&lt;p&gt;Next, we want to add extended location details for each earthquake using Nominatim. To do this, add in another source directive below the USGS one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://nominatim.openstreetmap.org/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
     &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;testing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nominatim requires user-agent headers but the value passed can be anything you want.  &lt;/p&gt;

&lt;p&gt;Next, in your Earthquake type add a new field “display_name” and the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Earthquake&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;
 &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EarthquakeProperties&lt;/span&gt;
 &lt;span class="n"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EarthquakeGeometry&lt;/span&gt;
 &lt;span class="n"&gt;display_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;
 &lt;span class="nd"&gt;@connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
   &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reverse?lat={$this.geometry.coordinates-&amp;gt;slice(1,2)-&amp;gt;first}&amp;amp;lon={$this.geometry.coordinates-&amp;gt;first}&amp;amp;format=json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="n"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
   $.display_name
   &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we use a Connector for REST to declare that display_name comes from Nominatum. Later in the React app, we’ll cover how you can alias this to what you are using on your frontend. You can also alias here in the schema if you choose. The $this key references the parent Earthquake object.&lt;/p&gt;

&lt;p&gt;Your schema is ready, the last thing we need to do before testing it is to update your supergraph.yaml to point to your file. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;subgraphs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;earthquake&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;routing_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:4000&lt;/span&gt;
   &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;earthquake.graphql&lt;/span&gt;
&lt;span class="na"&gt;federation_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;=2.10.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To learn about configuring your Router, you can &lt;a href="https://www.apollographql.com/docs/rover/commands/supergraphs#yaml-configuration-file" rel="noopener noreferrer"&gt;head over to the documentation&lt;/a&gt; to see more options especially relevant once you are ready to go to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the Query
&lt;/h2&gt;

&lt;p&gt;Head to &lt;a href="http://localhost:4000" rel="noopener noreferrer"&gt;http://localhost:4000&lt;/a&gt; to create and test the query for your app.&lt;/p&gt;

&lt;p&gt;Once you have confirmed that your graph is working as expected, it’s time to move to the React app to see what we need to modify. Leave your local Router running as you will need it to be able to test your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying the React App
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/AmandaApollo/earthquake-finder" rel="noopener noreferrer"&gt;Clone the repo&lt;/a&gt; and install the dependencies, run the project. &lt;/p&gt;

&lt;p&gt;Open actions.ts so we can investigate the API calls.&lt;/p&gt;

&lt;p&gt;There are two functions here, one to call USGS and a second to then call Nominatim on each returned value. There is also some code here to create the necessary object for our frontend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;searchEarthquakes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;maxRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Earthquake&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Build USGS Earthquake API URL&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&amp;amp;latitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;longitude=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;maxradius=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;maxRadius&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;limit=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


   &lt;span class="c1"&gt;// Fetch earthquake data&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="c1"&gt;// rest of code omitted here. . . . .&lt;/span&gt;


   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;earthquakesWithLocation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error fetching earthquake data:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Integrating your GraphQL query doesn’t have to be complicated. If you’re comfortable writing a query and calling fetch, you already know 90% of what you need. Head back to the GraphQL sandbox and click the 3 dots to the right of your query. Then select copy to cURL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figehva5qrqot9wl517ig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figehva5qrqot9wl517ig.png" alt="Apollo Sandbox with copy to curl button highlighted" width="800" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: If you would rather follow along with the final version, &lt;a href="https://github.com/AmandaApollo/earthquake-finder/tree/graphql-version" rel="noopener noreferrer"&gt;take a look at this branch.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In actions.ts inside the try/catch block at the top, paste your curl.  This is a little difficult to read so if you are in VSCode, this is a good place to use copilot to make this more readable.  The end result should look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    query RecentEarthquakes($limit: Int!, $lat: String!, $lon: String!, $maxRadius: Int!) {
      recentEarthquakes(limit: $limit, lat: $lat, lon: $lon, maxRadius: $maxRadius) {
        id
        locationDetails: display_name
        properties {
          mag
          place
          time
          updated
          url
          title
        }
        geometry {
          coordinates
        }
      }
    }
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;maxRadius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;lon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;


  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4000/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`GraphQL API error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;earthquakes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentEarthquakes&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;earthquakes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;earthquakes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you have your query, the parameters needed from the frontend, and a fetch call to your graph. Notice we are providing an alias to display_name to locationDetails to match our frontends shape.&lt;/p&gt;

&lt;p&gt;Let’s clean up the old code.&lt;/p&gt;

&lt;p&gt;In the try/catch block you can remove the rest of the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Build USGS Earthquake API URL&lt;/span&gt;
   &lt;span class="c1"&gt;// const url = `https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&amp;amp;latitude=${latitude}&amp;amp;longitude=${longitude}&amp;amp;maxradius=${maxRadius}&amp;amp;limit=${limit}`;&lt;/span&gt;


   &lt;span class="c1"&gt;// // Fetch earthquake data&lt;/span&gt;
   &lt;span class="c1"&gt;// const response = await fetch(url);&lt;/span&gt;


   &lt;span class="c1"&gt;// if (!response.ok) {&lt;/span&gt;
   &lt;span class="c1"&gt;//   throw new Error(`USGS API error: ${response.statusText}`);&lt;/span&gt;
   &lt;span class="c1"&gt;// }&lt;/span&gt;


   &lt;span class="c1"&gt;// const data = await response.json();&lt;/span&gt;
   &lt;span class="c1"&gt;// const earthquakes = data.features as Earthquake[];&lt;/span&gt;


   &lt;span class="c1"&gt;// Get location details for each earthquake&lt;/span&gt;
   &lt;span class="c1"&gt;// const earthquakesWithLocation = await Promise.all(&lt;/span&gt;
   &lt;span class="c1"&gt;//   earthquakes.map(async (quake) =&amp;gt; {&lt;/span&gt;
   &lt;span class="c1"&gt;//     try {&lt;/span&gt;
   &lt;span class="c1"&gt;//       // Get location details from Nominatim&lt;/span&gt;
   &lt;span class="c1"&gt;//       const locationDetails = await getLocationDetails(&lt;/span&gt;
   &lt;span class="c1"&gt;//         quake.geometry.coordinates[1], // latitude&lt;/span&gt;
   &lt;span class="c1"&gt;//         quake.geometry.coordinates[0] // longitude&lt;/span&gt;
   &lt;span class="c1"&gt;//       );&lt;/span&gt;


   &lt;span class="c1"&gt;//       // Add location details to earthquake properties&lt;/span&gt;
   &lt;span class="c1"&gt;//       return {&lt;/span&gt;
   &lt;span class="c1"&gt;//         ...quake,&lt;/span&gt;
   &lt;span class="c1"&gt;//         locationDetails: locationDetails ?? undefined,&lt;/span&gt;
   &lt;span class="c1"&gt;//       };&lt;/span&gt;
   &lt;span class="c1"&gt;//     } catch (error) {&lt;/span&gt;
   &lt;span class="c1"&gt;//       console.error(&lt;/span&gt;
   &lt;span class="c1"&gt;//         `Error getting location details for earthquake ${quake.id}:`,&lt;/span&gt;
   &lt;span class="c1"&gt;//         error&lt;/span&gt;
   &lt;span class="c1"&gt;//       );&lt;/span&gt;
   &lt;span class="c1"&gt;//       return quake;&lt;/span&gt;
   &lt;span class="c1"&gt;//     }&lt;/span&gt;
   &lt;span class="c1"&gt;//   })&lt;/span&gt;
   &lt;span class="c1"&gt;// );&lt;/span&gt;


   &lt;span class="c1"&gt;// Log the first earthquake with location details for debugging&lt;/span&gt;
 &lt;span class="c1"&gt;//   if (earthquakesWithLocation.length &amp;gt; 0) {&lt;/span&gt;
 &lt;span class="c1"&gt;//     console.log(&lt;/span&gt;
 &lt;span class="c1"&gt;//       "First earthquake with location details:",&lt;/span&gt;
 &lt;span class="c1"&gt;//       JSON.stringify(&lt;/span&gt;
 &lt;span class="c1"&gt;//         {&lt;/span&gt;
 &lt;span class="c1"&gt;//           id: earthquakesWithLocation[0].id,&lt;/span&gt;
 &lt;span class="c1"&gt;//           hasLocationDetails:&lt;/span&gt;
 &lt;span class="c1"&gt;//             !!earthquakesWithLocation[0].locationDetails,&lt;/span&gt;
 &lt;span class="c1"&gt;//           locationDetails:&lt;/span&gt;
 &lt;span class="c1"&gt;//             earthquakesWithLocation[0].locationDetails,&lt;/span&gt;
 &lt;span class="c1"&gt;//         },&lt;/span&gt;
 &lt;span class="c1"&gt;//         null,&lt;/span&gt;
 &lt;span class="c1"&gt;//         2&lt;/span&gt;
 &lt;span class="c1"&gt;//       )&lt;/span&gt;
 &lt;span class="c1"&gt;//     );&lt;/span&gt;
 &lt;span class="c1"&gt;//   }&lt;/span&gt;


 &lt;span class="c1"&gt;//   return earthquakesWithLocation;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also remove the entire function call for getLocationDetails.  We are now calling exactly what we need in one GraphQL query so we do not need this second function or API call here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLocationDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="c1"&gt;//   latitude: number,&lt;/span&gt;
&lt;span class="c1"&gt;//   longitude: number&lt;/span&gt;
&lt;span class="c1"&gt;// ): Promise&amp;lt;string | null&amp;gt; {&lt;/span&gt;
&lt;span class="c1"&gt;//   try {&lt;/span&gt;
&lt;span class="c1"&gt;//     // Add a small delay to avoid rate limiting&lt;/span&gt;
&lt;span class="c1"&gt;//     await new Promise((resolve) =&amp;gt; setTimeout(resolve, 100));&lt;/span&gt;


&lt;span class="c1"&gt;//     const url = `https://nominatim.openstreetmap.org/reverse?format=json&amp;amp;lat=${latitude}&amp;amp;lon=${longitude}`;&lt;/span&gt;


&lt;span class="c1"&gt;//     const response = await fetch(url, {&lt;/span&gt;
&lt;span class="c1"&gt;//       headers: {&lt;/span&gt;
&lt;span class="c1"&gt;//         "User-Agent": "EarthquakeSearchApp/1.0",&lt;/span&gt;
&lt;span class="c1"&gt;//       },&lt;/span&gt;
&lt;span class="c1"&gt;//       // Ensure we don't cache the response&lt;/span&gt;
&lt;span class="c1"&gt;//       cache: "no-store",&lt;/span&gt;
&lt;span class="c1"&gt;//     });&lt;/span&gt;


&lt;span class="c1"&gt;//     if (!response.ok) {&lt;/span&gt;
&lt;span class="c1"&gt;//       console.error(&lt;/span&gt;
&lt;span class="c1"&gt;//         `Nominatim API error: ${response.status} ${response.statusText}`&lt;/span&gt;
&lt;span class="c1"&gt;//       );&lt;/span&gt;
&lt;span class="c1"&gt;//       return null;&lt;/span&gt;
&lt;span class="c1"&gt;//     }&lt;/span&gt;


&lt;span class="c1"&gt;//     const data = await response.json();&lt;/span&gt;


&lt;span class="c1"&gt;//     // Log the response for debugging&lt;/span&gt;
&lt;span class="c1"&gt;//     console.log(&lt;/span&gt;
&lt;span class="c1"&gt;//       `Location details for ${latitude},${longitude}:`,&lt;/span&gt;
&lt;span class="c1"&gt;//       JSON.stringify({ display_name: data.display_name }, null, 2)&lt;/span&gt;
&lt;span class="c1"&gt;//     );&lt;/span&gt;


&lt;span class="c1"&gt;//     return data.display_name ?? null;&lt;/span&gt;
&lt;span class="c1"&gt;//   } catch (error) {&lt;/span&gt;
&lt;span class="c1"&gt;//     console.error("Error getting location details:", error);&lt;/span&gt;
&lt;span class="c1"&gt;//     return null;&lt;/span&gt;
&lt;span class="c1"&gt;//   }&lt;/span&gt;
&lt;span class="c1"&gt;//}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By modifying this file to use our new GraphQL query, we eliminated over 50 lines of redundant code without changing any UI logic. It’s a reminder that simplicity scales and a practical example of how to keep your GraphQL integration dead simple.&lt;/p&gt;

&lt;p&gt;Another interesting thing to notice here is that searchByPlace is still intact and using REST.  Using Graphql is not all or nothing, you can adopt it in your applications when it makes sense and use it alongside your other REST API calls. This allows you to adopt Graphql slowly without breaking other parts of your application which is especially important if you work with multiple teams.&lt;/p&gt;

&lt;p&gt;Save your files and navigate to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to see your updates in action.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihvmex5gbtt32j791rcp.gif" class="article-body-image-wrapper"&gt;&lt;img width="560" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihvmex5gbtt32j791rcp.gif" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;While this example uses Apollo Router and Connectors for REST APIs to keep things simple and local, in a production app you’d typically pair this setup with Apollo Client on the frontend. Apollo Client handles caching, state management, and reactive updates, all the things you’d expect in a mature GraphQL application.&lt;/p&gt;

&lt;p&gt;But the key point here is GraphQL doesn’t have to be all-or-nothing or hard to adopt. By adding a lightweight GraphQL layer over your existing REST services using Connectors, you can reduce boilerplate, simplify frontend data fetching, and set yourself up for more scalable, maintainable code. And when you’re ready, tools like Apollo Client make it easy to fully integrate this into your application architecture.&lt;/p&gt;

&lt;p&gt;To get started building your first Apollo Connector for REST APIs today, check out &lt;a href="https://www.apollographql.com/docs/graphos/connectors" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt; or get started with &lt;a href="https://github.com/apollographql/connectors-community" rel="noopener noreferrer"&gt;pre-built connectors&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>graphql</category>
      <category>api</category>
    </item>
    <item>
      <title>GraphQL is for Backend Engineers</title>
      <dc:creator>Dylan Anthony</dc:creator>
      <pubDate>Mon, 05 Feb 2024 22:22:42 +0000</pubDate>
      <link>https://forem.com/apollographql/graphql-is-for-backend-engineers-3ih</link>
      <guid>https://forem.com/apollographql/graphql-is-for-backend-engineers-3ih</guid>
      <description>&lt;p&gt;Most articles explaining the benefits of GraphQL focus on advantages for the frontend: things like preventing overfetching, reducing round trips, and iterating faster. But GraphQL provides just as many advantages for backend developers, which is why I choose it by default for new APIs and why you should consider it, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improved communication
&lt;/h2&gt;

&lt;p&gt;The goal of building any API is to &lt;a href="https://www.postman.com/state-of-api/api-first-strategies/#measuring-success-of-public-apis" rel="noopener noreferrer"&gt;enable someone to use it&lt;/a&gt;, which requires excellent communication between API producers and consumers. However, the state of the art for REST APIs doesn’t quite live up to this expectation.&lt;/p&gt;

&lt;p&gt;On the backend, developers either need to manually document the entire API or rely on auto-generation tools that don’t fully meet their needs. Consumers face the same choice, write code by hand or workaround the bugs in their SDK generator (stated, lovingly, as the maintainer of &lt;a href="https://github.com/openapi-generators/openapi-python-client" rel="noopener noreferrer"&gt;an OpenAPI client generator&lt;/a&gt;). On top of this, these solutions result in inconsistent understandings of the API. Reproducing errors becomes time-consuming and frustrating, which feels like a battle instead of a collaboration. What we need is a shared language to describe how the API works—one that doesn’t add unnecessary layers of abstraction or manual work.&lt;/p&gt;

&lt;p&gt;Meet GraphQL, where every team uses the same schema definition language (SDL) to talk about the API. SDL is designed for developers, omitting extraneous details while describing all the essential bits. This serves both as your documentation and a universal language to communicate between teams.&lt;/p&gt;

&lt;p&gt;On the backend, developers can write code in their language of choice and have the SDL created for them. Alternatively, they can write the SDL by hand, and generate the required code. Either way, the documentation is always kept in sync with the implementation.&lt;/p&gt;

&lt;p&gt;Consumers use this same SDL directly, so there’s no need to maintain a separate documentation website. Instead, they can get information, auto-complete names, and see potential mistakes right in their IDEs. The same request they write in their code can be run by itself for easier debugging, which makes sending snippets to the backend team much more straightforward. With clearer communication, there will be fewer open issues and less wasted time tracking down minor bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less bikeshedding
&lt;/h2&gt;

&lt;p&gt;Bikeshedding is when you spend time discussing decisions that don’t have a significant impact, and it happens quite a bit with RESTful APIs. To name a few, &lt;em&gt;I&lt;/em&gt; have been in bikeshedding meetings about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Which verb to use? &lt;code&gt;PUT&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; could have a lot of overlap. Maybe you *should*use &lt;code&gt;GET&lt;/code&gt;, but the fact that it can’t contain a body is too limiting for the amount of filters you need. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Should this be a query parameter, path parameter, form data, or JSON body? For path parameters, how should we serialize arrays? What about objects?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do we completely normalize endpoints to reduce duplication, even though that causes slower client performance? Or include the kitchen sink even though that causes slower individual endpoint performance? Maybe a compromise with shallow nested objects containing only the most common parameters or a parameter that allows selecting fields, emulating GraphQL without the type safety.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which HTTP status code is the right one for each response?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Should relationships be indicated with IDs or URIs?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hopefully, these discussions will lead to a comprehensive style guide, but the meetings will continue even with those guides. While there will always be &lt;em&gt;some&lt;/em&gt; debate around things like naming, GraphQL eliminates entire categories of potential style issues (including all those above) by being &lt;em&gt;simpler&lt;/em&gt;. You can spend less time on the little details and more time solving more interesting problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce tech debt
&lt;/h2&gt;

&lt;p&gt;Imagine you have a &lt;code&gt;/v1/data&lt;/code&gt; endpoint called by all your frontend teams: web, iOS, and Android. You decide that the date field should return UTC instead of localized time, so how do you make that breaking change? It usually looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a new &lt;code&gt;/v2/data&lt;/code&gt; endpoint.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inform all the client teams that this new endpoint exists and that they should migrate by some date.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Track metrics on &lt;code&gt;/v1/data&lt;/code&gt; and send reminders to consumers who haven’t updated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The deprecation date comes and goes, and you’re still receiving calls to the old endpoint.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You either pull the plug and start breaking things or, more likely, continue to support the &lt;code&gt;/v1/data&lt;/code&gt; endpoint indefinitely, creating tech debt with no apparent payoff date.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let’s play through the same scenario with GraphQL. First, you add &lt;code&gt;@deprecated(reason: “use utcDate”)&lt;/code&gt; to your old &lt;code&gt;date&lt;/code&gt; field and add the new &lt;code&gt;utcDate&lt;/code&gt; field. API consumers immediately start seeing warnings in their IDEs and potentially CI/CD about this deprecation since most GraphQL tooling supports it.&lt;/p&gt;

&lt;p&gt;As it turns out, the iOS and Android teams weren’t even using the old &lt;code&gt;date&lt;/code&gt; field, so no changes are necessary for them!&lt;/p&gt;

&lt;p&gt;Someone on the web team notices some squiggly lines in their editor and decides to fix them, following the instructions left in the &lt;code&gt;reason&lt;/code&gt; field. Your backend metrics tell you that the date field is no longer in use, and you’re safe to remove it!&lt;/p&gt;

&lt;p&gt;Creating less churn and an easier upgrade path for clients makes them more likely to upgrade. If they upgrade faster, you can remove that stale old code faster, too!&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL is for you too
&lt;/h2&gt;

&lt;p&gt;One of the main features of GraphQL is its developer experience. By creating a shared language between teams, reducing complexity, and providing a smoother upgrade path, GraphQL makes building and maintaining APIs more delightful. That’s why, even if your frontend engineers aren’t pushing for it yet, you should start evaluating GraphQL for your tech stack. If you have any questions or disagree with anything I’ve said above, let me know on our &lt;a href="http://discord.gg/NUaGwceDhX" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;! If you want to be notified of future posts, there’s also a channel for that.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Secure GraphQL Microservices</title>
      <dc:creator>Dylan Anthony</dc:creator>
      <pubDate>Wed, 02 Aug 2023 18:17:49 +0000</pubDate>
      <link>https://forem.com/apollographql/secure-graphql-microservices-4pc2</link>
      <guid>https://forem.com/apollographql/secure-graphql-microservices-4pc2</guid>
      <description>&lt;p&gt;Federation unlocks superpowers for our queries, enabling us to split up business logic and improve performance with features like &lt;code&gt;@defer&lt;/code&gt;. However, these same powers can be abused if placed in the wrong hands, so it’s essential to limit who has access to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The threats
&lt;/h2&gt;

&lt;p&gt;Many coordination features of a federated graph rely on an important assumption: &lt;strong&gt;clients always query your router—never individual subgraphs&lt;/strong&gt;. If this assumption is violated and clients query your subgraphs directly, you might expose data and capabilities that you &lt;em&gt;shouldn’t&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;@inaccessible&lt;/code&gt; directive enables subgraphs to define schema fields that &lt;em&gt;other&lt;/em&gt; subgraphs might need, but that the router &lt;em&gt;shouldn’t&lt;/em&gt; expose to clients. If clients can query a subgraph directly, those queries &lt;em&gt;can&lt;/em&gt; include &lt;code&gt;@inaccessible&lt;/code&gt; fields. And even if you don’t have any sensitive  &lt;code&gt;@inaccessible&lt;/code&gt; fields in your graph today, you might in the future!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;@requires&lt;/code&gt; directive enables a subgraph to define schema fields that depend on entity fields from &lt;em&gt;other&lt;/em&gt; subgraphs. Subgraphs rely on the router to resolve this dependency, ensuring that trusted data is provided. But if clients have direct access to subgraph entity resolvers, that data can &lt;em&gt;no longer&lt;/em&gt; be trusted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;@override&lt;/code&gt; directive enables a subgraph to take responsibility for a schema field away from &lt;em&gt;another&lt;/em&gt; subgraph. With direct subgraph access, clients can still query the &lt;em&gt;original&lt;/em&gt; subgraph field, which can result in inconsistent and unexpected behavior.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is just a small selection of the available features today, and more will be added over time. The key takeaway is that federation is a microservice architecture that expects a &lt;em&gt;single&lt;/em&gt; entry point through a controlled router. If subgraphs can be accessed directly, it’s only a matter of time before something unexpected happens, and that unexpected event could be a security breach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protecting your subgraphs
&lt;/h2&gt;

&lt;p&gt;Exposing subgraphs publicly opens the door to several attacks. To mitigate them, we prevent anything but our routers from accessing our subgraphs. First, we can protect our subgraphs at the application level by adding an extra layer of authorization on top of whatever user auth may be in place. We can configure our routers to send a header containing a shared secret to each of our subgraphs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;subgraphs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;orders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;insert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Router-Authorization"&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${env.ORDERS_SUBGRAPH_SECRET}"&lt;/span&gt;
    &lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;insert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Router-Authorization"&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${env.USERS_SUBGRAPH_SECRET}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we use a unique secret per subgraph, following a security best practice called “the principle of least privilege”. If there’s no reason for the &lt;code&gt;orders&lt;/code&gt; subgraph to have direct access to the &lt;code&gt;users&lt;/code&gt; subgraph, then we should not give it the ability to do so (by sending it valid tokens for another subgraph).&lt;/p&gt;

&lt;p&gt;These values come from environment variables, so how you set them will depend on where you’re running your routers. Most secret managers have a way to inject values via an environment variable. For example, in GraphOS, you can &lt;a href="https://www.apollographql.com/docs/graphos/routing/cloud-configuration#managing-secrets" rel="noopener noreferrer"&gt;set secrets via the UI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How this looks on the subgraph side depends on your implementation. Here’s an example using FastAPI in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_router_security&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Awaitable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;router_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ROUTER_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;router_secret&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Router-Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;router_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNAUTHORIZED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’re setting up the expected token as an environment variable called &lt;code&gt;ROUTER_SECRET&lt;/code&gt; in our subgraph. This value would be injected via the secret manager for your hosting platform. If the secret is not set (such as in local development), we turn off this enforcement; you may want to add a more sophisticated method of ensuring this is never turned off in production. If the secret &lt;em&gt;is&lt;/em&gt; set, we ensure every request has a matching &lt;code&gt;Router-Authorization&lt;/code&gt; header. Requests without a valid header will get a bare response with a &lt;code&gt;401 Unauthorized&lt;/code&gt; code—we don’t send any additional information that clients don’t need, and unauthenticated requesters don’t even need to know that this is a GraphQL server.&lt;/p&gt;

&lt;p&gt;We have examples for many languages and frameworks in our &lt;a href="https://www.apollographql.com/docs/graphos/graphs/creating-a-subgraph#starting-from-a-template" rel="noopener noreferrer"&gt;subgraph templates&lt;/a&gt;, so check them out for more inspiration!&lt;/p&gt;

&lt;h3&gt;
  
  
  Extra credit: network protections
&lt;/h3&gt;

&lt;p&gt;As an additional step, depending on your hosting environment, you may be able to use network-level protections to prevent any incoming connections to your subgraphs except your routers. This is typically done via firewall rules (such as access control lists). Not all platforms or architectures will have this option, but if you do, we recommend adding it as a “defense in depth”. &lt;/p&gt;

&lt;h2&gt;
  
  
  Ruling out alternatives
&lt;/h2&gt;

&lt;p&gt;There are a few other solutions to this problem that might &lt;em&gt;seem&lt;/em&gt; viable but can cause issues. Let’s go over them!&lt;/p&gt;

&lt;p&gt;First, you might be tempted to apply router authorization checks only for federation-specific subgraph fields (&lt;code&gt;_service&lt;/code&gt; and &lt;code&gt;_entities&lt;/code&gt; ), but there are problems with this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Newer versions of federation might add &lt;em&gt;more&lt;/em&gt; subgraph fields in the future, which would introduce new vulnerabilities that special-case checks don’t handle.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This approach doesn’t solve the problem of reaching &lt;code&gt;@inaccessible&lt;/code&gt; or &lt;code&gt;@requries&lt;/code&gt; fields via a top-level &lt;code&gt;Query&lt;/code&gt;field (a standard, non-federated query).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Second, you might be tempted to enforce router authorization per field, maybe even automatically for any field that implements a federated directive. However, federation enables any single subgraph to influence the behavior of the entire supergraph. For example, if &lt;code&gt;@inaccessible&lt;/code&gt; is applied to a field in &lt;em&gt;any&lt;/em&gt; subgraph, it’s expected to be enforced on &lt;em&gt;every&lt;/em&gt; subgraph. This means that per-field, per-subgraph enforcement is fundamentally inconsistent and can lead to unexpected data leakage.&lt;/p&gt;

&lt;p&gt;Finally, you might consider disabling introspection as a solution—if attackers can’t discover the hidden fields, they can’t use them, right? This is called “security by obscurity” and is widely considered not security at all. A sufficiently determined attacker &lt;em&gt;will&lt;/em&gt; eventually guess the names of fields that you don’t want them to find.&lt;/p&gt;

&lt;p&gt;The only consistent approach to preventing undesired access to subgraph capabilities is to disable access to subgraphs &lt;em&gt;entirely&lt;/em&gt; to any client besides the router.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get secure
&lt;/h2&gt;

&lt;p&gt;Now that you know the dangers of exposing subgraphs publicly and the best approach to mitigating the threat, there’s nothing left to do but start implementing! Start securing your subgraphs immediately.&lt;/p&gt;

&lt;p&gt;Have any questions, comments, or concerns about this post? I’d love to hear about it in our &lt;a href="https://discord.com/invite/graphos" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt;! That’s also where you’ll be notified of upcoming security-related live streams.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>api</category>
      <category>security</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Performance impacts of Apollo Router customizations</title>
      <dc:creator>Lucas Leadbetter</dc:creator>
      <pubDate>Mon, 17 Jul 2023 20:14:24 +0000</pubDate>
      <link>https://forem.com/apollographql/performance-impacts-of-apollo-router-customizations-pjh</link>
      <guid>https://forem.com/apollographql/performance-impacts-of-apollo-router-customizations-pjh</guid>
      <description>&lt;p&gt;The Apollo Router features three customization options out of the box to adjust behavior: &lt;a href="https://www.apollographql.com/docs/router/configuration/overview"&gt;configuration options&lt;/a&gt;, &lt;a href="https://www.apollographql.com/docs/router/customizations/rhai"&gt;Rhai scripting&lt;/a&gt;, and &lt;a href="https://www.apollographql.com/docs/router/customizations/coprocessor"&gt;external coprocessing&lt;/a&gt;. We refer to these as extensibility options.&lt;/p&gt;

&lt;p&gt;Extensibility of the Router is one of its primary strengths and is a critical feature for many organizations large and small. Understanding the performance implications of these options is important as the Router is designed for performance and serves as the entry point to your supergraph. We’re excited to share results with you covering these extensibility options, as well as a public GitHub repository where you can test them yourself. &lt;/p&gt;

&lt;p&gt;Before diving into the methodology and detailed results, the high-level results are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you’re able to choose, configuration options perform better than Rhai scripts, and Rhai scripts perform better than external coprocessing. &lt;/li&gt;
&lt;li&gt;When you must use an external coprocessor for more complex logic, we see about 350μs overhead. &lt;/li&gt;
&lt;li&gt;The choice of programming language, complexity of task, network proximity, type of host machine, and other factors will have varying impacts on the real-world performance of your Router and coprocessor, so ensure you consider the best tool for the job, so to speak, and keep the coprocessor as close as possible to the Router. Ideally this would be within the same Pod (if using Kubernetes) or similar for other orchestration systems. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/apollosolutions/router-extensibility-load-testing"&gt;The GitHub repository&lt;/a&gt; includes more information about the results, including specific numbers, as well as the ability to run these tests yourself. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Methodology&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As with any test, determining how to best isolate the variable you want is critical to ensure you’re not introducing other factors into the results. For the tests we ran, we wanted to isolate the overhead for each extensibility option, not the performance impact of each test we ran.&lt;/p&gt;

&lt;p&gt;To help isolate overhead latency, we ensured that we pre-warmed the query plan cache to remove the initial overhead of query planning to the first request (which may have skewed results by adding a request with a higher maximum latency value). We do this by running curl commands after the Router restart with both of the test queries. &lt;/p&gt;

&lt;p&gt;We also limited the resources for each running Docker image to use only 1 CPU core and 1GB of RAM as a way to ensure a consistent result from run to run. While this may have impacted some programming language implementations, we wanted to give a level playing field for each test. &lt;/p&gt;

&lt;p&gt;With that in mind, we designed three different tests for the extensibility options. This allowed us to show various overheads, as well as showing the impact of not only the complexity, but potential impact of the coprocessor stages. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting a static subgraph header, which is supported by all three extensibility options. This was the simplest example available, and helped test the overhead of a SubgraphRequest coprocessor stage. Since this logic is run per-subgraph on both the coprocessor and in Rhai, we wanted to understand the overhead of multiple invocations. &lt;/li&gt;
&lt;li&gt;Setting 10 GUID headers on a response, which is supported by only Rhai and external coprocessing. This test was designed to have slightly more complex logic, while also showing something that wasn’t possible with a basic configuration. &lt;/li&gt;
&lt;li&gt;Lastly, we tested setting the client awareness headers using claims within a JSON Web Token (JWT) which can only be done in a coprocessor as of writing. This was the most complex logic we tested, and was a way to show how complexity could affect results as well as being an example of something only possible with external coprocessing. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these tests were run against a baseline Router (that is, a basic Router without any configured extensibility options), and a Router with the respective extensibility options configured with coprocessors being implemented in Node.js, Python, Go, Java, and C#. Between each run, we restart the Router and pre-warm the query plan cache. &lt;/p&gt;

&lt;p&gt;For every test, we utilized &lt;a href="https://github.com/apollosolutions/router-extensibility-load-testing/tree/main/loadtester"&gt;a custom version&lt;/a&gt; of &lt;a href="https://github.com/tsenart/vegeta"&gt;Vegeta&lt;/a&gt;, a Go-based load testing utility. Our version handles GraphQL errors within the payload and treats them as non-2XX results for the purposes of reporting failures appropriately. Each run was set to 100 requests per second for 60 seconds to adequately load-test the system and provide enough data points for reference. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Results&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Diving into the results, we saw that most workloads benefit by using configuration options over Rhai scripts, and Rhai scripts are better than external coprocessing in terms of overhead. &lt;/p&gt;

&lt;p&gt;Rhai performance was excellent across the board, with latency overheads in p95s being around 100μs. This overhead makes it an excellent choice for most customization needs, as it tends to cover most use-cases that can’t traditionally be done with the basic configuration options. &lt;/p&gt;

&lt;p&gt;External coprocessing overhead was still excellent at 350μs overhead per stage average across all tests and languages, so for those that need more complex logic (such as the client awareness test showed), it can still be an excellent fit with great performance. With that in mind, there are additional elements here that are worth diving into. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your runtime/programming language matters, and will affect results depending on the type of customizations you need. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each language has its strengths and weaknesses, so ensure you pick the right tool for the job when deciding what to use for your coprocessor.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Even more important than runtime, your networking setup is critical to ensuring optimal performance. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since external coprocessing communicates over HTTP, reducing the latency between your Apollo Router and external coprocessor will ensure optimal performance and reduce overhead. If you are using Kubernetes, for example, running the coprocessor as a container alongside the Router in the same Pod can ensure network proximity. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure you need to use SubgraphRequest/Response stages before using them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The coprocessor overhead is per stage, so if you run a query that hits 5 subgraphs, you’ll end up with 5 invocations of the SubgraphRequest/Response stages for a coprocessor. This overhead can add up quickly, so it is advisable to validate your need to use this stage before utilizing it. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Per-Test Results&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Looking at each test individually, we can start to make sense of some of the above information, as well as see the percentage differences from the baseline.&lt;/p&gt;

&lt;p&gt;The numbers used were from the most recent test, running on Windows using WSL2, but your latency numbers may vary depending on resources available, CPU, OS, and more. The takeaways themselves should remain the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UKRVJln1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j7bwvr75w4nbbn33e2er.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UKRVJln1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j7bwvr75w4nbbn33e2er.png" alt="Results for setting a static subgraph header" title="Results for setting a static subgraph header" width="600" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the static subgraph header test, we added a "coprocessor" header with the value of the type of extensibility (e.g. rhai for Rhai scripts). This leveraged the SubgraphRequest stage for a coprocessor, and we aimed to test the overhead of making multiple requests. For this test, we averaged 1.5 subgraphs per request.&lt;/p&gt;

&lt;p&gt;We can start to see that we are running into minute variance issues. In the above chart, we show that both Python and the config option are faster than the baseline- but the raw data shows that they are only 40μs and 10μs difference, respectively. Regardless, the actual overhead was measured around 330μs averaged across all coprocessor types. For specific details on each option, see the&lt;a href="https://github.com/apollosolutions/router-extensibility-load-testing/tree/main#static-subgraph-header"&gt; full results in the GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jNMmLM_D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0lrx5n9n2ta7m4qg42fb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jNMmLM_D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0lrx5n9n2ta7m4qg42fb.png" alt="Results for adding 10 GUIDs to a header in the response" title="Results for adding 10 GUIDs to a header in the response" width="600" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the GUID response headers test, we wanted to add a level of logic that the configuration couldn't provide, as well as test the impact of a very basic looping logic to add dynamically generated GUIDs to the Router's response. To do so, we leveraged the RouterResponse stage for a coprocessor.&lt;/p&gt;

&lt;p&gt;Breaking down the numbers, we see clearly that Rhai is far more performant- only 11% higher p95 latency than baseline, or only 10μs in our tests versus the minimum 17% change when using a Go-based coprocessor, and the others being higher.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0tdNwbwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7plondl84yp2uwzxzj1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0tdNwbwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7plondl84yp2uwzxzj1j.png" alt="Results for identifying clients based on JWT claims" title="Results for identifying clients based on JWT claims" width="600" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For our last test, we wanted to test something logically complex as well as something only a coprocessor could do. This test decoded a JWT using an HMAC256 secret, and then used the claims to populate the &lt;code&gt;apollographql-client-name&lt;/code&gt; and &lt;code&gt;apollographql-client-version&lt;/code&gt; headers &lt;a href="https://www.apollographql.com/docs/router/managed-federation/client-awareness/"&gt;to enable client awareness&lt;/a&gt; for visibility in Apollo Studio.&lt;/p&gt;

&lt;p&gt;This test shows how, while the HTTP overhead itself is minimal, the actual logic is far more important. Introducing complex logic can affect performance in a meaningful way, so using other extensibility options whenever possible will help reduce the overall overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Prefer Router configuration options over other customization options whenever possible.&lt;/li&gt;
&lt;li&gt;External coprocessors add, on average, only 350μs of overhead plus the overhead of your logic. This number can change depending on other factors, identified above.&lt;/li&gt;
&lt;li&gt;Keep coprocessors as close as possible to the Router to ensure the overhead doesn't become too burdensome.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;View the full results and implementation on the &lt;a href="https://github.com/apollosolutions/router-extensibility-load-testing"&gt;public GitHub page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>performance</category>
      <category>loadtest</category>
      <category>api</category>
    </item>
    <item>
      <title>Don’t Overreact! Introducing Apollo Client’s new @nonreactive directive and useFragment hook</title>
      <dc:creator>Alessia Bellisario</dc:creator>
      <pubDate>Thu, 15 Jun 2023 21:23:12 +0000</pubDate>
      <link>https://forem.com/apollographql/dont-overreact-introducing-apollo-clients-new-nonreactive-directive-and-usefragment-hook-1ipn</link>
      <guid>https://forem.com/apollographql/dont-overreact-introducing-apollo-clients-new-nonreactive-directive-and-usefragment-hook-1ipn</guid>
      <description>&lt;p&gt;Efficient React apps are selective in their re-rendering, using a variety of techniques (such as &lt;strong&gt;&lt;a href="https://react.dev/reference/react/memo"&gt;&lt;code&gt;memo&lt;/code&gt;&lt;/a&gt;)&lt;/strong&gt; to skip re-rendering unchanged components. With the beta release of Apollo Client 3.8, we’re excited to spotlight a couple of new techniques to help you re-render exactly the components that you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;&lt;a href="https://www.apollographql.com/docs/react/data/directives/#nonreactive"&gt;&lt;code&gt;@nonreactive&lt;/code&gt; directive&lt;/a&gt;&lt;/strong&gt;: mark GraphQL query fields and fragments that shouldn’t trigger a re-render if their cached value changes.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;&lt;a href="https://www.apollographql.com/docs/react/api/react/hooks-experimental/#usefragment"&gt;&lt;code&gt;useFragment&lt;/code&gt; hook&lt;/a&gt;&lt;/strong&gt;: create a live binding into the Apollo Client cache for a GraphQL fragment. This allows Apollo Client to broadcast specific fragment results to individual React components so child components can re-render without needing to re-render the parent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at how we can use these (both individually and in combination) to keep our app snappy!&lt;/p&gt;

&lt;h2&gt;
  
  
  (Re-)Rendering Lists
&lt;/h2&gt;

&lt;p&gt;You might have found yourself reaching for &lt;code&gt;memo&lt;/code&gt; while building an app with Apollo Client that renders a &lt;strong&gt;list&lt;/strong&gt;. Consider the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserFragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  fragment UserFragment on User {
    name
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ALL_USERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query AllUsers {
    users {
      id
      ...UserFragment
    }
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;UserComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;UserList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ALL_USERS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserComponent&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;InMemoryCache&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fragments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;createFragmentRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
      &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;UserFragment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    `&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ApolloProvider&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserList&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ApolloProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;By registering our UserFragment with Apollo Client’s InMemoryCache via createFragmentRegistry, we can reference it by name inside our queries (as we do here with the ...UserFragment spread in ALL_USERS) without interpolating it directly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this example, the &lt;code&gt;UserList&lt;/code&gt; component fetches a list of users with Apollo Client’s &lt;code&gt;useQuery&lt;/code&gt; hook, then maps over the result to render a &lt;code&gt;UserComponent&lt;/code&gt; for each user in the list.&lt;/p&gt;

&lt;p&gt;But what happens when a user record is updated in the Apollo Client cache? Because these cached fields are watched by our &lt;code&gt;useQuery&lt;/code&gt; invocation, the hook re-runs. This re-renders &lt;code&gt;UserList&lt;/code&gt; with the latest values, which in turn recursively re-renders every child component (by default). In other words, if a single user in our list is updated, the vast majority of our &lt;code&gt;UserComponent&lt;/code&gt;s will re-render even though they haven’t changed!&lt;/p&gt;

&lt;p&gt;These unnecessary renders often go unnoticed by end users (and sometimes developers), but recursively re-running application and library code can add up to cause noticeable dips in performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  To &lt;code&gt;memo&lt;/code&gt; or not to &lt;code&gt;memo&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;The React docs provide excellent guidance for this question in the deep dive section &lt;a href="https://react.dev/reference/react/memo#should-you-add-memo-everywhere"&gt;“Should you add memo everywhere?”&lt;/a&gt; Like many optimizations, &lt;code&gt;memo&lt;/code&gt; has a cost: the additional step of comparing old and new props on every render. Although that cost is often outweighed by the benefits, it’s still a cost you should consider.&lt;/p&gt;

&lt;p&gt;I encourage you to read that section in its entirety, but here are two criteria from it that can help us determine whether we should optimize with &lt;code&gt;memo&lt;/code&gt; (emphasis mine):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does our component &lt;strong&gt;re-render often&lt;/strong&gt; with &lt;strong&gt;the same exact props&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;And is its &lt;strong&gt;re-rendering logic expensive&lt;/strong&gt;, causing &lt;strong&gt;perceptible lag&lt;/strong&gt; when it re-renders?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take a look at an example that extends the &lt;code&gt;UserList&lt;/code&gt; code above.&lt;/p&gt;

&lt;p&gt;When editing a single user and writing to Apollo Client’s &lt;code&gt;InMemoryCache&lt;/code&gt; on every keystroke, we see that the parent component re-renders, along with all of the 2,000 &lt;code&gt;UserComponent&lt;/code&gt;s in our list. Typing in the input feels sluggish 🐌&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x6BsIGep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/td25x1esg8qsdeyqwbnh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x6BsIGep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/td25x1esg8qsdeyqwbnh.gif" alt="A list of users rendered in a bare bones React app: each row renders an input with the user name pre-populated when the edit button is clicked. Typing in the input is slow and janky." width="636" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, this is a contrived example: there are other ways we might write this code in a real app to work around the problem: using &lt;a href="https://www.apollographql.com/docs/react/pagination/overview/"&gt;pagination&lt;/a&gt;, debouncing cache writes so we aren’t updating the user list on every keystroke, or writing to the cache only after the user presses “Done editing”.&lt;/p&gt;

&lt;p&gt;But there are reasons we shouldn’t be satisfied with these “fixes”. First, although rendering fewer users and/or performing fewer cache writes would improve perceived performance, we’d still be re-rendering &lt;strong&gt;parent&lt;/strong&gt; and &lt;strong&gt;child&lt;/strong&gt; components unnecessarily every time a user updates. Reducing the number of renders is a good place to start, but the dynamic nature of lists—iterating over N users, where N is usually known only at runtime—can make this tricky.&lt;/p&gt;

&lt;p&gt;Maybe at first we don’t think we’re handling enough data to really need pagination, only to later notice performance has degraded when our &lt;code&gt;ALL_USERS&lt;/code&gt; query returns more users than expected (been there 🙋🏻‍♀️). Or maybe our &lt;code&gt;UserComponent&lt;/code&gt; starts out with a simple implementation that does minimal work on each render, but as our app grows it either becomes more complex or renders more complex children of its own.&lt;/p&gt;

&lt;p&gt;I hope by now I’ve convinced you this is a problem worth solving! But is &lt;code&gt;memo&lt;/code&gt; the answer?&lt;/p&gt;

&lt;p&gt;Using the criteria from the React docs, our &lt;code&gt;UserComponent&lt;/code&gt; &lt;strong&gt;is a good candidate for memoization&lt;/strong&gt;: all other users in the list are re-rendering with the same exact props ✅, and each component is simulating expensive rendering logic, causing the DOM to perceptibly lag when applying updates to 2,000+ nodes on every keystroke ✅.&lt;/p&gt;

&lt;p&gt;Sure enough, after wrapping &lt;code&gt;UserComponent&lt;/code&gt; in &lt;code&gt;memo&lt;/code&gt;, we can see the lag disappear:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FR0kGxYD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no0ikc9clso58q119j94.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FR0kGxYD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no0ikc9clso58q119j94.gif" alt="The same bare bones React app, but now it's wrapping each UserComponent in React.memo. The jank is gone, but we can still see that the parent component is re-rendering on every keystroke, just the unchanged children are no longer re-rendering." width="636" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fun fact: even though &lt;a href="https://react.dev/reference/react/memo#my-component-rerenders-when-a-prop-is-an-object-or-array"&gt;objects, arrays and functions normally need to be memoized/cached when passing them as props&lt;/a&gt; to memoized React components (so they pass React’s Object.is same value equality check when determining whether the child needs to re-render), we don’t have to memoize the user objects we’re passing into our UserComponents. This is because Apollo Client’s normalized cache has already done the work to ensure the user objects we get from the cache are referentially stable 🎉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Rendering &lt;code&gt;memo&lt;/code&gt; unnecessary
&lt;/h2&gt;

&lt;p&gt;The lag is gone, but our work isn’t done. Notice that the &lt;em&gt;parent component is still re-rendering on every keystroke&lt;/em&gt;, because our &lt;code&gt;useQuery&lt;/code&gt; call is watching for changes on all &lt;code&gt;users&lt;/code&gt;. &lt;strong&gt;We’re still re-rendering the parent, but now React is able to skip re-rendering all unchanged child components.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Re-rendering the parent has its own drawbacks: in a larger application, we’d also have to remember to wrap every other child of &lt;code&gt;UserList&lt;/code&gt; in &lt;code&gt;memo&lt;/code&gt; when needed (see the criteria above, rinse and repeat). And &lt;code&gt;memo&lt;/code&gt;‘s comparison of all of those props still comes with a cost—one we’d like to avoid altogether.&lt;/p&gt;

&lt;p&gt;Our Apollo Client team saw an opportunity here. We wondered: what if a parent component could ignore updates to certain fields on the selection set it’s watching in the cache? And what if each child component could react to updates for a single cache entity, without being prompted to re-render by its parent?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;@nonreactive&lt;/code&gt; and &lt;code&gt;useFragment&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Together, this new directive and hook unlock a pattern for rendering lists that selectively re-render individual list items by default—&lt;strong&gt;no &lt;code&gt;memo&lt;/code&gt; required&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can apply the &lt;code&gt;@nonreactive&lt;/code&gt; directive to fields or fragments on the parent component’s query, and you can use it with or without Apollo Client’s React bindings (for example, in queries passed to &lt;code&gt;client.watchQuery&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In our demo app, the &lt;code&gt;AllUsers&lt;/code&gt; query becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AllUsers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;UserFragment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;@nonreactive&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: The parent component &lt;em&gt;should&lt;/em&gt; receive updates for the &lt;code&gt;id&lt;/code&gt; field (or whatever the equivalent cache key is for an entity), so we intentionally &lt;em&gt;don’t&lt;/em&gt; apply &lt;code&gt;@nonreactive&lt;/code&gt; to the &lt;code&gt;id&lt;/code&gt; field here. This ensures that the parent &lt;strong&gt;does&lt;/strong&gt; update when items are added or removed from the list.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, when cache updates arrive and Apollo Client is comparing the previous result with incoming data for a selection set to determine whether to update, it can avoid comparing fields that exist on subtrees of the &lt;code&gt;@nonreactive&lt;/code&gt; fragment altogether. Less work for Apollo Client, and less work for React as a result.&lt;/p&gt;

&lt;p&gt;Instead of passing each user object into &lt;code&gt;UserComponent&lt;/code&gt; and wrapping it with &lt;code&gt;memo&lt;/code&gt;, child components can use the &lt;code&gt;id&lt;/code&gt; and fragment document to receive cache updates directly for each user in the list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;UserComponent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useFragment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserFragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any component watching for changes for a specific user via &lt;code&gt;useFragment&lt;/code&gt; will re-render on its own when data changes. It no longer needs to wait for a parent to trigger a re-render by passing it fresh data via props!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2TLygPWV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agdqm2bkb9ylgirxve5m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2TLygPWV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agdqm2bkb9ylgirxve5m.gif" alt="The same bare bones React app, but now using @nonreactive and useFragment: only the child component being edited re-renders - not the parent or any siblings - and the app is smooth and performant." width="636" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, our parent component &lt;strong&gt;no longer re-renders at all when updating users&lt;/strong&gt;—only when users are &lt;strong&gt;added&lt;/strong&gt; or &lt;strong&gt;removed&lt;/strong&gt;. This gives us predictable behavior &lt;em&gt;and&lt;/em&gt; performance, whether our list is 10, 100, or 1,000 users long! And we achieved all of this without &lt;code&gt;memo&lt;/code&gt;. (You definitely &lt;em&gt;should&lt;/em&gt; still use &lt;code&gt;memo&lt;/code&gt; as needed to solve other performance issues, per the guidance in the docs!)&lt;/p&gt;

&lt;p&gt;View the full CodeSandbox demo &lt;a href="https://codesandbox.io/s/github/alessbell/nonreactive"&gt;here&lt;/a&gt;. You can try out both &lt;code&gt;@nonreactive&lt;/code&gt; and &lt;code&gt;useFragment&lt;/code&gt; in our latest 3.8 beta via &lt;code&gt;npm i @apollo/client@beta&lt;/code&gt; and view the documentation for both &lt;a href="https://www.apollographql.com/docs/react/data/directives/#nonreactive"&gt;&lt;code&gt;@nonreactive&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/react/api/react/hooks-experimental/#usefragment"&gt;&lt;code&gt;useFragment&lt;/code&gt;&lt;/a&gt; for more information. We’d love to hear what you think!&lt;/p&gt;

</description>
      <category>react</category>
      <category>graphql</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Favorite Resources for Learning GraphQL</title>
      <dc:creator>Ceora Ford</dc:creator>
      <pubDate>Thu, 17 Mar 2022 18:25:11 +0000</pubDate>
      <link>https://forem.com/apollographql/my-favorite-resources-for-learning-graphql-30gc</link>
      <guid>https://forem.com/apollographql/my-favorite-resources-for-learning-graphql-30gc</guid>
      <description>&lt;p&gt;I recently held a Twitter Space where I discussed how I learned GraphQL during my first few months as a developer advocate here at Apollo. I went over the strategies I used which can be really helpful regardless of what language, framework, or tool you’re learning on the job. If you're interested in checking out the recording, I’ll leave a link at the end of this article.&lt;/p&gt;

&lt;p&gt;For now, I want to focus more on what I used to learn GraphQL. So here’s a quick list of my favorite resources for learning GraphQL. This list includes courses, videos, tutorials, and more. So no matter your preferred medium for educational content, you’ll find something that fits your style.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;a href="https://www.youtube.com/watch?v=dwLI70eP3mw" rel="noopener noreferrer"&gt;What is GraphQL YouTube Video&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If you don’t know much about GraphQL or what problems it solves, this is a great video to start with. It covers the fundamentals of how GraphQL works and how developers benefit from using it.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/dwLI70eP3mw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  2.  &lt;a href="https://odyssey.apollographql.com/" rel="noopener noreferrer"&gt;GraphQL Odyssey Course&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is the main course I relied on when I was first learning GraphQL. This is free and it includes tons of great information and interactive coding exercises. And once you finish all 5 modules, you get a certificate!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;a href="https://www.moonhighway.com/live-events" rel="noopener noreferrer"&gt;GraphQL Is for Everyone Workshop&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I was able to attend this workshop shortly after joining Apollo and it was amazing! This workshop is led by &lt;a href="https://twitter.com/eveporcello" rel="noopener noreferrer"&gt;Eve Porcello&lt;/a&gt; and I love the way she teaches. All the information is delivered in a fun and approachable style. This is a great workshop to attend especially if you’re completely new to GraphQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;a href="https://graphqlworkshop.com/" rel="noopener noreferrer"&gt;GraphQL Email Newsletter&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This email newsletter is also created by Eve Porcello. It’s an easy, low-stakes way to learn more about GraphQL. I’m a huge fan of Eve’s teaching style and this email course is just as great as everything else she creates.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;a href="https://www.apollographql.com/docs/tutorial/introduction/" rel="noopener noreferrer"&gt;Full Stack GraphQL Tutorial&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This Full Stack GraphQL tutorial is a great way to start building with GraphQL. I’m a huge proponent of project-based learning. But it can be intimidating to start a project with a new language all on your own. So this is a great way to have a guided learning experience as you begin working with GraphQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. &lt;a href="https://camiinthisthang.hashnode.dev/everything-i-learned-in-my-1st-year-as-a-swe-graphql" rel="noopener noreferrer"&gt;Everything I Learned in My 1st Year as a SWE: GraphQL Article&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I really enjoyed this article because hearing the perspective of another GraphQL newbie really resonated with me. It was nice to read about parts of &lt;a href="https://twitter.com/camiinthisthang" rel="noopener noreferrer"&gt;Camila’s&lt;/a&gt; learning journey that mirrored my own. It also helped me to recognize areas of confusion that I, as a developer advocate and GraphQL educator, could help fill.  &lt;/p&gt;

&lt;h2&gt;
  
  
  7. &lt;a href="https://dev.to/shrutikapoor08/getting-started-with-graphql-emo"&gt;Getting Started with GraphQL Article&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is an article that’s quite similar to this one. It includes an explanation of what GraphQL is and additional resources for learning. &lt;a href="https://twitter.com/shrutikapoor08" rel="noopener noreferrer"&gt;Shruti&lt;/a&gt; is a pillar in the GraphQL community so I’m sure you’ll find something useful in that article.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. &lt;a href="https://www.apollographql.com/docs/resources/graphql-glossary/" rel="noopener noreferrer"&gt;GraphQL Glossary&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I have to include this glossary because it was so helpful for me! I used it as a handy reference along with my courses and tutorials so I could easily check my understanding of unfamiliar terminology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you have any recommendations for GraphQL learning resources, please feel free to leave them in the comments below. Like I said, these are resources that I personally used and enjoyed. I’m sure that there are so many more out there.&lt;/p&gt;

&lt;p&gt;If you’re interested in actual learning tips, you can listen to the &lt;a href="https://twitter.com/ceeoreo_/status/1502008332923588610?s=20&amp;amp;t=DaI73vvdiPjKtR83WbEjbQ" rel="noopener noreferrer"&gt;recording of the Twitter Space&lt;/a&gt;. I plan on doing more Spaces about GraphQL, developer advocacy, and working at Apollo. So let me know if you have any suggestions. Thanks for reading💜&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Behind the Scenes of Our "What is GraphQL" Video</title>
      <dc:creator>Ceora Ford</dc:creator>
      <pubDate>Tue, 08 Mar 2022 16:01:18 +0000</pubDate>
      <link>https://forem.com/apollographql/behind-the-scenes-of-our-what-is-graphql-video-10hi</link>
      <guid>https://forem.com/apollographql/behind-the-scenes-of-our-what-is-graphql-video-10hi</guid>
      <description>&lt;p&gt;Everyone has their own learning style. Some people prefer to read educational content. There are some developers who like to skip the reading and go straight to building and breaking things. Lots of people really enjoy video content. Then there are people like me who switch between all learning styles and content forms depending on the subject.&lt;/p&gt;

&lt;p&gt;The developer experience team at Apollo realized we wanted to focus more on creating video content. It’s our job to create educational content that will appeal to developers and we want that to include videos. We recently released our first of many YouTube videos called What is GraphQL.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/dwLI70eP3mw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It took a lot of hard work from the whole team. I wanted to talk about the process we went through to create and release this video. Our process is going to change and evolve over time. We’ll probably look at this first behind-the-scenes view and marvel at how much we’ve improved! &lt;/p&gt;

&lt;p&gt;So let’s pull the curtain back a bit and go over how we went about creating this video!&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming up with ideas
&lt;/h2&gt;

&lt;p&gt;Of course, everyone on the team had a ton of ideas for our videos. GraphQL is such a broad topic and there are so many creative things you can do with videos. To narrow down our focus, we had to think about a few things.&lt;/p&gt;

&lt;p&gt;Videos can be hard to update and edit post-release. Apollo and GraphQL (and just tech in general) are constantly changing. It’s inevitable that some content will become deprecated in the long run. So for our more polished, high-effort videos, we decided to stick with concepts that are relatively constant and stable. &lt;/p&gt;

&lt;p&gt;What is GraphQL fits this really well! The fundamental features of GraphQL probably won't change any time soon. Plus, what better way to kick off our new video initiative than starting from the very foundation of everything!&lt;/p&gt;

&lt;p&gt;After deciding on what our first video would be about, we went on to write out our script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the script
&lt;/h2&gt;

&lt;p&gt;Scripting is not easy. It reminds me of what it’s like to write a talk or an article. Coming up with the script and storyline for the video was a painstaking process. We wanted to be sure that our video was technically sound and worded &lt;em&gt;just&lt;/em&gt; right. So we edited and edited and edited some more.&lt;/p&gt;

&lt;p&gt;Of course, at one point and time, we had to stop ourselves. We weren’t aiming for perfection. We wanted something that would be really good and useful to the community. So after we finalized the script,  our video team came up with some creative concepts to help us really get our ideas across.&lt;/p&gt;

&lt;p&gt;Next, we assigned lines to different team members. We wanted this video to reflect the people who make up the developer experience team at Apollo. If you’ve watched the video, you probably noticed how we switch up between narrators to do this. All of this was written into the script and gave each of us a chance to rehearse our individual lines so we could be prepared for the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filming our video
&lt;/h2&gt;

&lt;p&gt;Now onto filming. We got our fancy cameras and fancy lighting and started filming. And then we were done! &lt;/p&gt;

&lt;p&gt;If you’ve ever filmed any kind of scripted video content, you know it wasn’t that simple. We had to learn how to properly set up all our fancy video equipment. After we figured that out, we had to get everything in just the right spot. This took a lot of adjusting. We had to make sure our microphones, lights, and camera angles were in the right place to give us the results we wanted.&lt;/p&gt;

&lt;p&gt;Now we press record... and suddenly I can’t remember any of my lines. Remembering what I had to say was much harder than I expected. Once I could finally remember without messing up, I had to say my lines in a bunch of different ways to convey the tone we were going for. I had to make sure to look directly at the camera and smile.&lt;/p&gt;

&lt;p&gt;We eventually got enough good takes to wrap up filming. Each of my team members who are featured in the video went through a similar process. Once each of us finished recording, our video team expertly stitched everything together, adding in amazing graphics and effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;At the end of this process, we were able to release our video to the world! And the results have been amazing!! I'm incredibly proud of our team and I'm glad to see our hard work paying off.&lt;/p&gt;

&lt;p&gt;Now we’re working on our next video. We’re already planning ahead for future concepts by gathering ideas and writing scripts. We would love to hear from you if you have any suggestions! Feel free to leave them in the comments below.&lt;/p&gt;

&lt;p&gt;Keep an eye out for our next video and in (the most non-cliche way possible) subscribe to the &lt;a href="https://www.youtube.com/channel/UC0pEW_GOrMJ23l8QcrGdKSw" rel="noopener noreferrer"&gt;Apollo GraphQL YouTube Channel&lt;/a&gt; so you'll know when it drops!! Thanks for reading.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>graphql</category>
    </item>
    <item>
      <title>⚡ Add a GraphQL Server to a RESTful Express.js API in 2 Minutes</title>
      <dc:creator>Khalil Stemmler</dc:creator>
      <pubDate>Thu, 09 Jan 2020 18:47:04 +0000</pubDate>
      <link>https://forem.com/apollographql/add-a-graphql-server-to-a-restful-express-js-api-in-2-minutes-4gdb</link>
      <guid>https://forem.com/apollographql/add-a-graphql-server-to-a-restful-express-js-api-in-2-minutes-4gdb</guid>
      <description>&lt;h3&gt;
  
  
  ⚡ Add a GraphQL Server to a RESTful Express.js API in 2 Minutes
&lt;/h3&gt;

&lt;p&gt;You can get a lot done in 2 minutes, like microwaving popcorn, sending a text message, eating a cupcake, and &lt;strong&gt;hooking up a GraphQL server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yup. If you have an old Express.js RESTful API lying around or you're interested in incrementally adopting GraphQL, we only need 2 minutes to hook it up with a fresh new GraphQL Server.&lt;/p&gt;

&lt;p&gt;Ready? Set. Go!&lt;/p&gt;

&lt;p&gt;Let's say that your server looked something like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;apiRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Existing routes for our Express.js app&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiRouter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[App]: Listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the root of your project, &lt;code&gt;npm install&lt;/code&gt; &lt;a href="https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-express" rel="noopener noreferrer"&gt;apollo-server-express&lt;/a&gt; as a dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install apollo-server-express --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to where your Express app is defined and import &lt;code&gt;ApolloServer&lt;/code&gt; and &lt;code&gt;gql&lt;/code&gt; from &lt;code&gt;apollo-server-express&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApolloServer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apollo-server-express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create an instance of an &lt;code&gt;ApolloServer&lt;/code&gt; with the &lt;em&gt;simplest possible&lt;/em&gt; GraphQL &lt;strong&gt;type definitions&lt;/strong&gt; and &lt;strong&gt;resolvers&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;typeDefs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
    type Query {
      hello: String
    }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, use &lt;code&gt;ApolloServer&lt;/code&gt;'s &lt;a href="https://www.apollographql.com/docs/apollo-server/api/apollo-server/?utm_source=devto&amp;amp;utm_medium=blog_post&amp;amp;utm_campaign=add_graphl_server_express_2_mins#apolloserverapplymiddleware" rel="noopener noreferrer"&gt;applyMiddleware&lt;/a&gt; method to pass in our Express.js server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom. That's it!&lt;/p&gt;

&lt;p&gt;Your code should look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v1Router&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./api/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApolloServer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apollo-server-express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloServer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;typeDefs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
    type Query {
      hello: String
    }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;resolvers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v1Router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[App]: Listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you navigate to &lt;code&gt;localhost:5000/graphql&lt;/code&gt;, you should be able to see your GraphQL schema in the GraphQL playground.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwd4tiobfydytzdtamlef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwd4tiobfydytzdtamlef.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If you want to change the URL that the GraphQL endpoint sits at from &lt;code&gt;/graphql&lt;/code&gt; to something else, you can pass in a &lt;code&gt;path&lt;/code&gt; option to &lt;code&gt;server.applyMiddleware()&lt;/code&gt; with the URL you want, like &lt;code&gt;path: '/specialUrl'&lt;/code&gt;. Check out the &lt;a href="https://www.apollographql.com/docs/apollo-server/api/apollo-server/?utm_source=devto&amp;amp;utm_medium=blog_post&amp;amp;utm_campaign=add_graphl_server_express_2_mins#apolloserverapplymiddleware" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for full API usage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How simple was that? Is your popcorn finished? 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here's what we did.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;code&gt;apollo-server-express&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;new ApolloServer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Connect your GraphQL Server with &lt;code&gt;server.applyMiddleware&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I personally really love the fact that Apollo Server is non-intrusive and can be tacked on any project as an alternative way to communicate between services and applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;If you're new to Apollo and GraphQL, a great way to learn is to actually build something in real life. For that reason, I highly recommend checking out the &lt;a href="https://www.apollographql.com/docs/tutorial/introduction?utm_source=devto&amp;amp;utm_medium=blog_post&amp;amp;utm_campaign=add_graphl_server_express_2_mins" rel="noopener noreferrer"&gt;Apollo Fullstack Tutorial (you can also learn in TypeScript now 🔥)&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm &lt;a href="https://twitter.com/stemmlerjs" rel="noopener noreferrer"&gt;Khalil Stemmler&lt;/a&gt;, a Developer Advocate at Apollo GraphQL. I teach advanced TypeScript, GraphQL, and Node.js best practices for large-scale applications. Feel free to ping me on &lt;a href="https://twitter.com/stemmlerjs" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; if you need help with anything Apollo, TypeScript, or architecture-related. Cheers 🤠&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>graphql</category>
      <category>expressjs</category>
      <category>node</category>
      <category>restfulapi</category>
    </item>
  </channel>
</rss>
