DEV Community

loading...
Cover image for Fetching and displaying data with GraphQL on a next.js front-end

Fetching and displaying data with GraphQL on a next.js front-end

lastnameswayne profile image Swayne ・6 min read

Setting up the front-end

In this post I will be building the frontend to the graphQL server I built in my CRUD-article and authenthication-artice. For this particular blog post we will be focusing on CRUD, so only that part is necessary.

Since I want good SEO on my front-end, so I will need SSR. To achieve this, we will use next.js with the bonus of also getting fantastic routing. I will be using Chakra-UI to style the app

yarn create next-app --example with-chakra-ui frontend
Enter fullscreen mode Exit fullscreen mode

now, just yarn dev and you should see a nice welcome-screen. Let's delete it.😑

To convert to tsx you should:

  • delete component folder
  • delete any filer other than _app.js, index.js and theme.js
  • rename _app.js, index.js and theme.js to _app.tsx, index.tsx and theme.tsx
  • write this in your index.tsx
import { Box } from "@chakra-ui/react"
import React from "react"

const Index = () => (
  <Box>hello</Box>
)

export default Index
Enter fullscreen mode Exit fullscreen mode

leaving you with a project structure similar to:

Project structure

Make sure to add the typescript types if you haven't already.

yarn add --dev typescript @types/node
Enter fullscreen mode Exit fullscreen mode

Finally, time to develop. To begin with, I want to load a list of the basketball-games we have in our backend!

We will need the Apollo Client to manage the state, fetch, cache and (later) modify the application data. The apollo client has many different features, and I will cover more in the future!

Apollo-client

add the apollo-client and Graphql

yarn add @apollo/client graphql
Enter fullscreen mode Exit fullscreen mode

We need to configure Apollo Client in _app.tsx

import { ChakraProvider, ColorModeProvider } from '@chakra-ui/react'

import theme from '../theme'
import React from 'react';
import ReactDOM from 'react-dom';
import './styles/index.css';
import {
  ApolloProvider,
  ApolloClient,
  createHttpLink,
  InMemoryCache
} from '@apollo/client';

function MyApp({ Component, pageProps }) {

const httpLink = createHttpLink({
  uri: 'http://localhost:4000/graphql'
});

const client = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache()
});

  return (
    <ApolloProvider client ={client}>
       <ChakraProvider resetCSS theme={theme}>
      <ColorModeProvider
        options={{
          useSystemColorMode: true,
        }}
      >
        <Component {...pageProps} />
      </ColorModeProvider>
    </ChakraProvider>
    </ApolloProvider>
  )
}

export default MyApp
Enter fullscreen mode Exit fullscreen mode

First we create a httplink pointing to our localhost. We need to pass the link to a new instance of the ApolloClient(). We also create a new instance of InMemoryCache which is the cache used by Apollo.

Lastly, make sure to wrap your app in <ApolloProvider> and pass in the client you created in the few lines above.

If you are getting a CORS-error, add these lines in index.ts

app.use(
    cors({
      origin: "http://localhost:3000",
      credentials: true
    }),
Enter fullscreen mode Exit fullscreen mode

It baically tells CORS that requests from the front-end, http://localhost:3000, are safe to receive.

To make our lives easier, we will be using an amazing tool called GraphQL code-gen.

GraphQL Code-Gen

Let's add graphQL code-gen to our project! GraphQL code generator is a tool to generate types and hooks for your graphQL mutations and queries. For example, code-gen would generate hooks for the createGame mutation we created in the previous tutorial, so we can use it on the front-end.

We need to go through a little setup, I promise it will be worth itπŸ™

Install the CLI and typescript types on the front-end:

yarn add -D @graphql-codegen/cli
yarn add @graphql-codegen/typescript-operations
Enter fullscreen mode Exit fullscreen mode

We need to initialize graphQL codegen. When initializing, you will be asked some questions about your project. You can copy mine if you want. The questions help the program create the config file, codegen.yaml, which you can always edit later.

yarn graphql-codegen init
Enter fullscreen mode Exit fullscreen mode

Here are my answers:

Now we need to create some folders

  1. Create a folder called "generated" under the "src"-folder. Here all of the code generated by graphQL code-gen will be saved.
  2. Create a new folder called "graphql" and inside of that create a folder called "queries". We now have a folder where we can write our queries to interact with the backend.

You should have a project structure similiar to:

β”œβ”€β”€ generated
β”‚   └── 
β”œβ”€β”€ graphql
β”‚   └── queries
β”‚             └── 
β”œβ”€β”€ pages
β”‚   β”œβ”€β”€ _app.tsx
β”‚   └── index.tsx
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ package.json
β”œβ”€β”€ codegen.yml
β”œβ”€β”€ next-env.d.ts
β”œβ”€β”€ tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Fetching and displaying the data

First, create query.graphql file:

query Games {
  games {
    id
    myTeamScore
    opponentTeamScore
    date
  }
}
Enter fullscreen mode Exit fullscreen mode

This is simply the same query which you would write in the graphQL playground. It gets an array of Game-objects. I have named the query "Games". If you are using VS-Code, I can recommend this plugin to get syntax-highlighting in .graphql files

Now we can just run

yarn gen
Enter fullscreen mode Exit fullscreen mode

in the terminal. This will generate the required hooks for all of our .graphql-files and tbeir corresponding types.

Navigate over to index.tsx, where we will map over the data.

const Index = () => {
  const { data } = useGamesQuery();

  return (
    <Box>
      {!data
        ? null
        : data.games.map((e) => {
            return <Box key={e.id}>{e.date}</Box>;
          })}
      :
    </Box>
  );
};
Enter fullscreen mode Exit fullscreen mode

I map over the array of Games gotten from gamesQuery(), and show the date of each game!

Take a minute to appreciate how easy that wasπŸ™ πŸ™

The workflow is:

  • Write your query/mutation in the graphQL playground
  • Paste into a .graphQL file inside either the "query"- or "mutation"-folder
  • run yarn gen
  • We now have fully typed hooks to do whatever we want.

Seriously, just look at this autofill❀️ Just pick the fields that you want to display 🀯

Example of autofill

If the user is on a slow-connection, we should show a loading-animation to indicate that the data is on it's way.

Loading

By hovering over useGamesQuery(), code-gen has already written the documentation for usπŸ˜…

useGamesQuery details

As you can see, we can also get a loading boolean, which can be used to show a spinner if a user has a slow connection. Let's develop that feature!

if (loading) {
    return <Spinner></Spinner>;
  } else {
    //display the data here
}
Enter fullscreen mode Exit fullscreen mode

So if the data is loading, show a spinner. I usually use the spinner from Chakra-UI, it's pretty great.

Styling

While you were reading the article, I styled the appπŸ˜† Chakra-UI makes it pretty easy, as we get a ton of pretty components out the box. In general, I am also a fan of CSS-in-JS, which Chakra also supports.

I can make a new React component, GameCard.tsx. Here is the code for the styling.

interface GameCardProps {
  date: string;
  myTeamScore: number;
  opponentTeamScore: number;
}

export const GameCard: React.FC<GameCardProps> = (props) => {
  return (
    <Box maxW="xs" m={2} borderWidth="1px" borderRadius="lg" overflow="hidden">
      <Box p="6">
        <Box d="flex" alignItems="baseline">
          {props.myTeamScore > props.opponentTeamScore ? (
            <Badge borderRadius="full" px="2" colorScheme="teal">
              Win
            </Badge>
          ) : (
            <Badge borderRadius="full" px="2" colorScheme="red">
              Loss
            </Badge>
          )}
          <Box
            color="gray.500"
            fontWeight="semibold"
            letterSpacing="wide"
            fontSize="xs"
            textTransform="uppercase"
            ml="2"
          >
            {props.date}
          </Box>
        </Box>

        <Box
          mt="1"
          maxW="50%"
          fontWeight="semibold"
          as="h4"
          lineHeight="tight"
          isTruncated
        >
          <Flex ml="auto">
            <Text>{props.myTeamScore}-</Text>
            <Text>{props.opponentTeamScore}</Text>
          </Flex>
        </Box>
      </Box>
    </Box>
  );
};
Enter fullscreen mode Exit fullscreen mode

Since this isn't a guide on styling, I won't go through everythingπŸ™‚

We can now map over our new <GameCards> in index.tsx and just pass in the data via props.

<Box>
        {!data
          ? null
          : data.games.map((e) => {
              return (
                <GameCard
                  myTeamScore={e.myTeamScore}
                  opponentTeamScore={e.opponentTeamScore}
                  date={e.date}
                  key={e.id}
                ></GameCard>
              );
            })}
</Box>
Enter fullscreen mode Exit fullscreen mode

The resulting cards will look like this:

Styled cards

While we are fetching the data perfectly, our database clearly consists of false, incorrect data, since we are not winning all the time. jk.

Conclusion

Nice! We have now learnt how to show the data in our database fetched with GraphQL. As you have noticed, GraphQL Code Gen simplifies our lives considerablyπŸ₯°

Discussion (0)

pic
Editor guide