--- title: Fetch third-party API data with Hydrogen description: Learn how to fetch and render data from third-party APIs with Hydrogen. source_url: html: >- https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party md: >- https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md --- ExpandOn this page * [What you'll build](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#what-youll-build) * [Step 1: Create a new third-party API client](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#step-1-create-a-new-third-party-api-client) * [Step 2: Create the API client and pass to the Remix context](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#step-2-create-the-api-client-and-pass-to-the-remix-context) * [Step 3: Query and render the list of entries](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#step-3-query-and-render-the-list-of-entries) * [Next steps](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#next-steps) # Fetch third-party API data with Hydrogen Hydrogen includes a built-in client and utilities for [fetching data](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching) with Shopify's Storefront API and Customer Account API. If you need to access data from third-party sources, then you can re-use these utilities and design patterns. By consistently using the same methods for data fetching regardless of the source, your app logic is simpler to understand, and your app will be more performant. *** ## What you'll build ![Hydrogen app displaying data from a third-party API](https://shopify.dev/assets/assets/images/custom-storefronts/hydrogen/hydrogen-3p-api-example-D8ETupZ0.png) In this guide, you'll use Hydrogen's built-in utilities to query the [GraphQL Rick and Morty API](https://rickandmortyapi.com/documentation/#graphql) and display a list of characters. This simplified example shows how to re-use Hydrogen tools to create a new API client, add it to the Remix context, and query your data from any route. *** ## Step 1: Create a new third-party API client The following example re-uses existing Hydrogen utilities to create an API client that handles caching with the same tooling and method that Hydrogen uses for Shopify API queries. This keeps data fetching and caching behaviors consistent across your app. ## Create a third-party API client ## /app/lib/createRickAndMortyClient.server.js ```js import {createWithCache, CacheLong} from '@shopify/hydrogen'; export function createRickAndMortyClient({ cache, waitUntil, request, }) { const withCache = createWithCache({cache, waitUntil, request}); async function query( query, options = {variables: {}, cacheStrategy: CacheLong()}, ) { const result = await withCache.fetch( 'https://rickandmortyapi.com/graphql', { method: 'POST', headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ query, variables: options.variables, }), }, { cacheKey: ['r&m', query, JSON.stringify(options.variables)], cacheStrategy: options.cacheStrategy, shouldCacheResponse: (body) => body.error == null || body.error.length === 0, }, ); return result.data; } return {query}; } ``` ```ts import {createWithCache, CacheLong, type CachingStrategy} from '@shopify/hydrogen'; export function createRickAndMortyClient({ cache, waitUntil, request, }: { cache: Cache; waitUntil: ExecutionContext['waitUntil']; request: Request; }) { const withCache = createWithCache({cache, waitUntil, request}); async function query( query: `#graphql:rickAndMorty${string}`, options: { variables?: object; cacheStrategy?: CachingStrategy; } = {variables: {}, cacheStrategy: CacheLong()}, ) { const result = await withCache.fetch<{data: T; error: string}>( 'https://rickandmortyapi.com/graphql', { method: 'POST', headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ query, variables: options.variables, }), }, { cacheKey: ['r&m', query, JSON.stringify(options.variables)], cacheStrategy: options.cacheStrategy, shouldCacheResponse: (body: {data: T; error: string}) => body.error == null || body.error.length === 0, }, ); return result.data; } return {query}; } ``` ##### JavaScript ``` import {createWithCache, CacheLong} from '@shopify/hydrogen'; export function createRickAndMortyClient({ cache, waitUntil, request, }) { const withCache = createWithCache({cache, waitUntil, request}); async function query( query, options = {variables: {}, cacheStrategy: CacheLong()}, ) { const result = await withCache.fetch( 'https://rickandmortyapi.com/graphql', { method: 'POST', headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ query, variables: options.variables, }), }, { cacheKey: ['r&m', query, JSON.stringify(options.variables)], cacheStrategy: options.cacheStrategy, shouldCacheResponse: (body) => body.error == null || body.error.length === 0, }, ); return result.data; } return {query}; } ``` ##### TypeScript ``` import {createWithCache, CacheLong, type CachingStrategy} from '@shopify/hydrogen'; export function createRickAndMortyClient({ cache, waitUntil, request, }: { cache: Cache; waitUntil: ExecutionContext['waitUntil']; request: Request; }) { const withCache = createWithCache({cache, waitUntil, request}); async function query( query: `#graphql:rickAndMorty${string}`, options: { variables?: object; cacheStrategy?: CachingStrategy; } = {variables: {}, cacheStrategy: CacheLong()}, ) { const result = await withCache.fetch<{data: T; error: string}>( 'https://rickandmortyapi.com/graphql', { method: 'POST', headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ query, variables: options.variables, }), }, { cacheKey: ['r&m', query, JSON.stringify(options.variables)], cacheStrategy: options.cacheStrategy, shouldCacheResponse: (body: {data: T; error: string}) => body.error == null || body.error.length === 0, }, ); return result.data; } return {query}; } ``` *** ## Step 2: Create the API client and pass to the Remix context You can now add your API client to the app's context file so it's available to load data from your routes. ## Pass API client to Remix context ## /app/lib/context.js ```js // ...Existing context.js imports here... import {createRickAndMortyClient} from './app/lib/createRickAndMortyClient.server'; export async function createAppLoadContext( request: Request, env: Env, executionContext: ExecutionContext, ) { // ... Existing context creation code here... // Create the Rick and Morty API Client const rickAndMorty = createRickAndMortyClient({ cache, waitUntil, request, }); return { ...hydrogenContext, rickAndMorty, }; }; ``` ```ts // ...Existing context.ts imports here... import {createRickAndMortyClient} from './app/lib/createRickAndMortyClient.server'; export async function createAppLoadContext( request: Request, env: Env, executionContext: ExecutionContext, ) { // ... Existing context creation code here... // Create the Rick and Morty API Client const rickAndMorty = createRickAndMortyClient({ cache, waitUntil, request, }); return { ...hydrogenContext, rickAndMorty, }; }; ``` ##### JavaScript ``` // ...Existing context.js imports here... import {createRickAndMortyClient} from './app/lib/createRickAndMortyClient.server'; export async function createAppLoadContext( request: Request, env: Env, executionContext: ExecutionContext, ) { // ... Existing context creation code here... // Create the Rick and Morty API Client const rickAndMorty = createRickAndMortyClient({ cache, waitUntil, request, }); return { ...hydrogenContext, rickAndMorty, }; }; ``` ##### TypeScript ``` // ...Existing context.ts imports here... import {createRickAndMortyClient} from './app/lib/createRickAndMortyClient.server'; export async function createAppLoadContext( request: Request, env: Env, executionContext: ExecutionContext, ) { // ... Existing context creation code here... // Create the Rick and Morty API Client const rickAndMorty = createRickAndMortyClient({ cache, waitUntil, request, }); return { ...hydrogenContext, rickAndMorty, }; }; ``` *** ## Step 3: Query and render the list of entries You can now query your `rickAndMorty` API client from any loader function, on any route, using the same caching utilities that Hydrogen uses to query Shopify's Storefront API. The following simplified example shows how to render an unordered list of character names on the `/characters` route: ## Render character index route ## /app/routes/characters.\_index.jsx ```jsx import {json} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; import {CacheShort} from '@shopify/hydrogen'; // Fetch and return API data with a Remix loader function export async function loader({context}) { const {characters} = await context.rickAndMorty.query(CHARACTERS_QUERY, { cache: CacheShort(), }); return json({characters}); } // Render the component using data returned by the loader export default function Characters() { const {characters} = useLoaderData(); return (

Rick & Morty Characters

    {(characters.results || []).map((character) => (
  • {character.name}
  • ))}
); } // Query the API for a list of characters const CHARACTERS_QUERY = `#graphql:rickAndMorty query { characters(page: 1) { results { name id } } } `; ``` ```tsx import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; import {CacheShort} from '@shopify/hydrogen'; // Fetch and return API data with a Remix loader function export async function loader({context}: LoaderFunctionArgs) { const {characters} = await context.rickAndMorty.query(CHARACTERS_QUERY, { cache: CacheShort(), }); return json({characters}); } type Character = { name: string; id: string; }; // Render the component using data returned by the loader export default function Characters() { const {characters} = useLoaderData(); return (

Rick & Morty Characters

    {(characters.results || []).map((character: Character) => (
  • {character.name}
  • ))}
); } // Query the API for a list of characters const CHARACTERS_QUERY = `#graphql:rickAndMorty query { characters(page: 1) { results { name id } } } `; ``` ##### JSX ``` import {json} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; import {CacheShort} from '@shopify/hydrogen'; // Fetch and return API data with a Remix loader function export async function loader({context}) { const {characters} = await context.rickAndMorty.query(CHARACTERS_QUERY, { cache: CacheShort(), }); return json({characters}); } // Render the component using data returned by the loader export default function Characters() { const {characters} = useLoaderData(); return (

Rick & Morty Characters

    {(characters.results || []).map((character) => (
  • {character.name}
  • ))}
); } // Query the API for a list of characters const CHARACTERS_QUERY = `#graphql:rickAndMorty query { characters(page: 1) { results { name id } } } `; ``` ##### TSX ``` import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData} from '@remix-run/react'; import {CacheShort} from '@shopify/hydrogen'; // Fetch and return API data with a Remix loader function export async function loader({context}: LoaderFunctionArgs) { const {characters} = await context.rickAndMorty.query(CHARACTERS_QUERY, { cache: CacheShort(), }); return json({characters}); } type Character = { name: string; id: string; }; // Render the component using data returned by the loader export default function Characters() { const {characters} = useLoaderData(); return (

Rick & Morty Characters

    {(characters.results || []).map((character: Character) => (
  • {character.name}
  • ))}
); } // Query the API for a list of characters const CHARACTERS_QUERY = `#graphql:rickAndMorty query { characters(page: 1) { results { name id } } } `; ``` Run `shopify hydrogen dev` to start the development server, then open to verify that the query succeeded. *** ## Next steps * If you haven't already, learn about [querying first-party Shopify APIs with Hydrogen](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching) * Learn more about [caching third-party API data](https://shopify.dev/docs/storefronts/headless/hydrogen/caching/third-party) with Hydrogen *** * [What you'll build](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#what-youll-build) * [Step 1: Create a new third-party API client](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#step-1-create-a-new-third-party-api-client) * [Step 2: Create the API client and pass to the Remix context](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#step-2-create-the-api-client-and-pass-to-the-remix-context) * [Step 3: Query and render the list of entries](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#step-3-query-and-render-the-list-of-entries) * [Next steps](https://shopify.dev/docs/storefronts/headless/hydrogen/data-fetching/third-party.md#next-steps)