urql is a GraphQL client for your frontend that boasts a smart caching mechanism, support for queries, mutations, subscriptions, and does it all in a very small ~7.4kb package.
We'll explore querying content at build using @urql/core
(re-exported from urql
) and urql
react hooks to query content on the client-side on request.
Core urqlAnchor
If you've used Next.js, then you'll be familiar with getStaticProps
get your static content, and pass it as props to the page.
If you want to fetch any data on the client you should check out working with next-urql
, but this guide is for fetching static data only. You'll also want to make sure your Permanent Auth Token has limited read permissions.
Let's first setup our urql client by importing createClient
and passing it our GraphCMS endpoint:
import { createClient } from 'urql';const client = createClient({url: 'https://api-eu-central-1.graphcms.com/v2/ck8sn5tnf01gc01z89dbc7s0o/master',});
Then inside of getStaticProps
you can use client.query()
and pass it your query (and optional variables). You'll need to chain .toPromise()
too.
You'll end up with something like this:
import { gql } from 'urql';export const getStaticProps = async () => {const ProductsQuery = gql`{products {slugname}}`;const {data: { products },} = await client.query(ProductsQuery).toPromise();return {props: {products,},};};
Now let's imagine we render a list of links to individual product pages, with this we can use Next.js dynamic routes to get the slug
of a product page, and pass this to urql.
The file /products/[slug].js
would look a little something like:
export async function getStaticProps({ params }) {const { slug } = params;const ProductBySlugQuery = gql`query GetProductBySlug($slug: String!) {product(where: { slug: $slug }) {namedescriptionprice}}`;const {data: { product },} = await client.query(ProductBySlugQuery, {slug,}).toPromise();return {props: {product,},};}
As with getStaticProps
we'll need to declare getStaticPaths
too. Unless you're adopting incremental static generation, you'll want to query GraphCMS to get all of your product slugs. It would look something like this:
export async function getStaticPaths() {const ProductSlugsQuery = gql`{products {slug}}`;const {data: { products },} = await client.query(ProductSlugsQuery).toPromise();return {paths: products.map(({ slug }) => ({params: { slug },})),fallback: false,};}
That's the core urql client - fetching data!
You can play with this example on CodeSandbox:
React urqlAnchor
The developer experience shines thanks to the collection of methods urql has available for your framework.
Let's take a look at how you can use urql with React on the frontend, without statically building pages.
Before we can begin to update our index page of products, we'll need to wrap our application with a Provider
component and pass it our client.
import React from 'react';import ReactDOM from 'react-dom';import { createClient, Provider } from 'urql';import App from './App';const client = createClient({url: 'https://api-eu-central-1.graphcms.com/v2/ck8sn5tnf01gc01z89dbc7s0o/master',});ReactDOM.render(<React.StrictMode><Provider value={client}><App /></Provider></React.StrictMode>,document.getElementById('root'));
From here, we can import useQuery
from urql
and pass it on our query, but this time it'll be executed on the frontend.
We'll get from the array useQuery
returns the first item, which we'll call result
. This result
will be an object, and we can get from that data
, error
and whether or not it's fetching
.
useQuery
doesn't need your GraphCMS endpoint (or a client) because it is aware of the context surrounding it that is <Provider value={client}>
.
Now, all that's left to do is update our page to show a message when we're fetching, an error occurred, or the data we get back from GraphCMS.
import { gql, useQuery } from 'urql';const ProductsQuery = gql`{products {slugname}}`;function App() {const [result] = useQuery({ query: ProductsQuery });const { data, fetching, error } = result;if (fetching) return <p>Fetching products</p>;if (error) return <p>Oh no... {error.message}</p>;return (<ul>{data.products.map(({ slug, name }) => (<li key={slug}>{name}</li>))}</ul>);}export default App;
You can play with this example on CodeSandbox: