Shopify Admin GraphQL API + gql.tada = ❤️
Say goodbye to codegen
Codegen can be annoying! You have to manage a separate process in order to leverage type safety. In this guide we’ll walk through how to use gql.tada for making type-safe calls to the Shopify Admin API in your Remix app.
Installation
Follow the installation instructions to
get gql.tada
set up properly in your project.
For the schema
in your tsconfig
plugin configuration, you can specify a direct proxy URL from Shopify that allows for introspection:
{
"compilerOptions": {
"plugins": [
{
"name": "@0no-co/graphqlsp",
"schema": "https://shopify.dev/admin-graphql-direct-proxy/2024-07",
"tadaOutputLocation": "./app/graphql-env.d.ts"
}
]
}
}
Make sure to replace 2024-07
with your API version if applicable.
GraphQL Client Setup
Install urql
, a GraphQL client which will
facilitate our queries and mutations to the Shopify Admin API.
Create a function that will create a client for a given shop:
// ~/app/graphql.server.ts
import { cacheExchange, Client, fetchExchange } from '@urql/core';
import { prisma } from '~/db.server';
const API_VERSION = '2024-07';
export const createShopifyClient = async (shop: string) => {
const session = await prisma.session.findFirst({
where: { shop, isOnline: false }
});
if (!session) throw new Error(`Session not found for shop: ${shop}`);
return new Client({
url: `https://${shop}/admin/api/${API_VERSION}/graphql.json`,
exchanges: [cacheExchange, fetchExchange],
fetchOptions: {
headers: {
'X-Shopify-Access-Token': session.accessToken
}
}
});
};
Integrate with authenticate.admin
Shopify’s Remix app template includes a shopify.server.ts
file which provides an export
called authenticate
. This is what facilitates authentication with Shopify and gives you
a way to make Admin API queries. Augment it to include the urql
client:
// ... shopifyApp setup ...
export const authenticate = {
/**
* Authenticates with Shopify, allowing access to Shopify's APIs when accessing
* the app admin.
*
* Note: This injects a `urql` client, which can consume types from `gql.tada`. You should use this over
* `admin.graphql` whenever possible for type safe variables and response data. Example:
*
* ```ts
* const { admin, session } = await authenticate.admin(request);
*
* const response = await admin.urql.mutation(yourGraphQLQuery, {
* someVariable: 'hello world!'
* });
*
*/
admin: async (request: Request) => {
const { session, admin, ...rest } = await shopify.authenticate.admin(request);
const client = await createShopifyClient(session.shop);
return { session, admin: { urql: client, ...admin }, ...rest };
}
};
// ...
Example usage
Here’s an example route that queries a list of products from the Shopify API,
using a query definition created with gql.tada
:
import { json, LoaderFunctionArgs } from '@remix-run/node';
import { BlockStack, List, TextField } from '@shopify/polaris';
import { Form, useLoaderData } from '@remix-run/react';
import { useState } from 'react';
import { authenticate } from '~/shopify.server';
// Make sure the import path here is correct, aligned with how
// you set up gql.tada!
import { graphql } from '~/shared/graphql';
const productsQuery = graphql(`
query Products($search: String!) {
products(query: $search, first: 50) {
edges {
node {
id
title
}
}
}
}
`);
export const loader = async ({ request }: LoaderFunctionArgs) => {
const { admin } = await authenticate.admin(request);
const searchParams = new URL(request.url).searchParams;
const response = await admin.urql.query(productsQuery, {
search: searchParams.get('search') || ''
});
return json({
products: response.data?.products.edges.map(edge => edge.node) || []
});
};
export default function Products() {
const { products } = useLoaderData<typeof loader>();
const [search, setSearch] = useState('');
return (
<BlockStack>
<Form>
<TextField
label="Search"
autoComplete="off"
name="search"
value={search}
onChange={setSearch}
/>
</Form>
<List>
{products.map(product => (
<List.Item key={product.id}>{product.title}</List.Item>
))}
</List>
</BlockStack>
);
}
Notice how the query variables and response data are all typed properly, without the need for you to manage a separate process for generating types for your query documents.
Find this helpful? You might also like our apps:
- Customer Fields allows you to build custom registration forms and retrieve valuable data from customers at just the right time.
- Meteor Mega Menu offers a variety of beautiful dropdown menu templates which you can attach to your exisiting Shopify navigation, meaning your store could have a menu makeover in minutes.