GraphQL Query in NextJS with useSWR and graphql-request
Tags: nextjs, react, graphql, javascript
Published: 2020-05-26 13:00:12

NextJS has made server-side rendering cool and easy. It's almost irresistible not using it, as it comes with so many things out of the box.

In this post, we will be fetching remote data in a new [NextJS](https://nextjs.org) App using a GraphQL API. We'll be using [useSWR](https://swr.now.sh) and [graphql-request](https://www.npmjs.com/package/graphql-request). We'll use [Chakra](https://chakra-ui.com) UI to beautify our app and [https://github.com/lennertVanSever/graphcountries](https://github.com/lennertVanSever/graphcountries) for data source.

**Step 1: ** Create a new app with `next-app`
```shell
yarn create next-app
# or
npm init next-app
```
Follow the prompt and choose `Default starter app`

**Step 2:** Install `useSWR` and `graphql-request` for data fetching
cd into the directory and install the dependencies
```shell
yarn add graphql-request swr
# or
npm install graphql-request swr
```

**Step 3: ** Install Chakra UI(This step is optional, but Chakra UI is awesome)
```shell
yarn add @chakra-ui/core @emotion/core @emotion/styled emotion-theming
# or
npm install @chakra-ui/core @emotion/core @emotion/styled emotion-theming
```
Then, open the folder in your favourite editor

**Step 4:** Create a theme file, extending the default theme from Chakra UI
`components/theme.js`
```javascript
import { theme } from "@chakra-ui/core";

const customTheme = {
  ...theme,
  breakpoints: ["30em", "48em", "62em", "80em"],
  fonts: {
    heading: '"Avenir Next", sans-serif',
    body: "system-ui, sans-serif",
    mono: "Menlo, monospace",
  },
  fontSizes: {
    xs: "0.75rem",
    sm: "0.875rem",
    md: "1rem",
    lg: "1.125rem",
    xl: "1.25rem",
    "2xl": "1.5rem",
    "3xl": "1.875rem",
    "4xl": "2.25rem",
    "5xl": "3rem",
    "6xl": "4rem",
  },
  colors: {
    ...theme.colors,
    brand: {
      900: "#1a365d",
      800: "#153e75",
      700: "#2a69ac",
    },
  },
};
export default customTheme;
```
NextJS uses file-based routing. So, every file in `pages` directory become a route, by default.
So, if we had `theme.js` in the `pages` directory, we would end up with a `/theme` route.
Hence, non-route files are to be placed in other directories outside of `pages`

**Step 5:** Create a layout file to inject the theme at the root of our application
`components/layout.js`
```jsx
import { ThemeProvider, CSSReset } from "@chakra-ui/core";
import theme from "./theme";

export default ({ children }) => (
  <ThemeProvider theme={theme}>
    <CSSReset />
    {children}
  </ThemeProvider>
);
```

**Step 6:** Create a barebones homepage with static content
`pages/index.js`
```jsx
import Head from "next/head";
import { Heading } from "@chakra-ui/core";
import Layout from "..components/layout";

export default function Home() {
  return (
    <>
      <Head>
        <title>Countries</title>
      </Head>
      <Layout>
        <Heading size="2xl" as="center">
          Countries
        </Heading>
      </Layout>
    </>
  );
}

```

**Step  7:** Import graphql-request and swr for data fetching
The `pages/index.js` import section should, then, look like this:
```javascript
import Head from "next/head";
import { Heading, Grid, Box, Badge, Image } from "@chakra-ui/core";
import { request } from "graphql-request";
import useSWR from "swr";
import Layout from "../components/layout";
```

**Step 8:**  Initialise the query and API endpoint:
`pages/index.js`
```javascript
const API_ENDPOINT = 'https://api.graph.cool/simple/v1/movies'
const countriesQuery = `{
  Country{
    _id
    name
    capital
    populationDensity
    currencies {
      _id
      code
    }
    timezones {
      _id
      name
    }
    officialLanguages {
      _id
      name
    }
    flag {
        _id
      svgFile
    }
  }
}`
```
_You can put your API endpoint in a .env file and you, probably, should. Link here: [https://nextjs.org/docs/basic-features/environment-variables](https://nextjs.org/docs/basic-features/environment-variables)_

**Step 9:** Make a request to the endpoint using `SWR`
Within the `Home` function before the `return` block:
```javascript
const { data: countries, error } = useSWR(countriesQuery, (query) =>
    request(API_ENDPOINT, query)
  );
```
`useSWR` returns 2 values: data and error, based on the status of the request.
If you `console.log({ countries, error });` at this point, you should see your data or error

**Step 10: **Prepare the component that will display the result
`components/country.js`
```jsx
import { Box, Badge, Image } from "@chakra-ui/core";

const Country = ({ country }) => (
  <Box maxW="sm" borderWidth="1px" rounded="lg" overflow="hidden">
    <Image src={country.flag?.svgFile} alt={country.nativeName} />
    <Box p="6">
      <Box d="flex" alignItems="baseline">
        {country.currencies &&
          country.currencies.map((currency) => (
            <Badge rounded="full" px="2" variantColor="teal" key={currency._id}>
              {currency.name}
            </Badge>
          ))}
        <Box
          color="gray.500"
          fontWeight="semibold"
          letterSpacing="wide"
          fontSize="xs"
          textTransform="uppercase"
          ml="2"
        >
          {country.capital} &bull;
          {country.timezones &&
            country.timezones.map((timezone) => (
              <span key={timezone._id}>{timezone.name}</span>
            ))}
        </Box>
      </Box>
      <Box mt="1" fontWeight="semibold" as="h4" lineHeight="tight" isTruncated>
        {country.name}
      </Box>

      <Box>
        {country.populationDensity}
        <Box as="span" color="gray.600" fontSize="sm">
          / sq.km
        </Box>
      </Box>
      {country.officialLanguages &&
        country.officialLanguages.map((language) => (
          <Box as="span" color="gray.600" fontSize="sm" key={language._id}>
            <span>{language.name}</span>
          </Box>
        ))}
    </Box>
  </Box>
);
export default Country;
```
All we did in the above was destructure `country` from the component props and used the `Box` component given us by Chakra UI to make the UI.

**Step 11: ** Display the data and error component
Replace `pages/index.js` with the code below:

```jsx
import Head from "next/head";
import { Heading, Grid, Alert, AlertIcon } from "@chakra-ui/core";
import { request } from "graphql-request";
import useSWR from "swr";
import Layout from "../components/layout";
import Country from "../components/country";

const API_ENDPOINT = "https://countries-274616.ew.r.appspot.com/";
const countriesQuery = `query getCountries{
  Country{
    _id
    name
    capital
    populationDensity
    currencies {
      _id
      code
    }
    timezones {
        _id
      name
    }
    officialLanguages {
      _id
      name
    }
    flag {
      _id
      svgFile
    }
  }
}`;

export default function Home() {
  const { data: countries, error } = useSWR(countriesQuery, (query) =>
    request(API_ENDPOINT, query)
  );
  return (
    <>
      <Head>
        <title>Countries</title>
      </Head>
      <Layout>
        <Heading size="2xl" as="center">
          Countries
        </Heading>
        {error && (
          <Alert status="error">
            <AlertIcon />
            There was an error processing your request
          </Alert>
        )}
        <Grid templateColumns="repeat(3, 1fr)" ml="10" mr="10" gap={6}>
          {countries && countries.Country &&
            countries.Country.map((country) => <Country country={country} key={country._id} />)}
        </Grid>
      </Layout>
    </>
  );
}
```
Run the app with `yarn run dev` or `npm run dev`.
You should see a list of countries, maps and their details listed.

In the next post, we will implement search functionality to demonstrate passing variables to GraphQL.

Full code available here on [Github](https://github.com/abiodunsulaiman694/countries-with-nextjs)

Thanks for reading. Please, leave your comment in the feedback.