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
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
leaving you with a project structure similar to:
Make sure to add the typescript types if you haven't already.
yarn add --dev typescript @types/node
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
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
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
}),
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
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
Here are my answers:
- default
- http://localhost:4000/graphql
- src/graphql/*/.graphql
- default
- default
- no
- "gen"
Now we need to create some folders
- Create a folder called "generated" under the "src"-folder. Here all of the code generated by graphQL code-gen will be saved.
- 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
Fetching and displaying the data
First, create query.graphql
file:
query Games {
games {
id
myTeamScore
opponentTeamScore
date
}
}
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
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>
);
};
I map over the array of Game
s 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 🤯
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😅
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
}
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>
);
};
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>
The resulting cards will look like this:
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🥰
Top comments (2)
Hi! I got CORS error and you said I should change index.ts file but there is no index.ts anywhere. Can you tell me where is index.ts because you also don't have index.ts file?
Sorry for late answer. There is an error in the article, it should index.tsx not index.ts. Sorry!