Render Queries
Let's now add the comments feature to our frontend React app.
Update GraphQL query
We'll start by updating our homepage to show the number of comments in each article. To do this, we need to update the GraphQL query the homepage is making.
In packages/web/src/pages/Home.tsx
, replace the useTypedQuery
with:
// Handle empty document cache
// https://formidable.com/open-source/urql/docs/basics/document-caching/#adding-typenames
const context = useMemo(
() => ({ additionalTypenames: ["Article", "Comments"] }),
[]
);
const [articles] = useTypedQuery({
query: {
articles: {
id: true,
url: true,
title: true,
comments: {
id: true,
},
},
},
context,
});
Here we are adding comments
to our query. You'll notice we aren't writing a typical GraphQL query. We are writing the query as an object. It's using a typesafe GraphQL client.
We are also making a change to additionalTypenames
. This is to fix a quirk of Urql's Document Cache, we'll look at this in the next chapter.
Typesafe GraphQL client
To make a GraphQL query, we are using a React Hook called useTypedQuery
.
It's making the articles
query that we looked at in the last chapter. The change here is that we are now requesting the comments
field as well.
You'll notice that our code editor can autocomplete all the fields in this query and the new comments
field is automatically available. Our code editor can also point out if we make a mistake in our query!
Behind the scenes
Let's look at how our typesafe frontend GraphQL client works behind the scenes.
SST uses Urql, one of the most popular GraphQL clients. The useTypedQuery
hook wraps around Urql's useQuery
hook while using the types that Genql generates based on our GraphQL schema.
The types are code-genned automatically. We looked at this process back in the GraphQL API chapter.
The useTypedQuery
hook is imported from the graphql/
directory in our app. This directory is mostly code-genned but is meant to be committed to Git.
import { useTypedQuery } from "@my-sst-app/graphql/urql";
The useTypedQuery
hook needs an instance of our GraphQL client to make the queries. We define this in packages/web/src/main.tsx
.
const urql = new Client({
url: import.meta.env.VITE_GRAPHQL_URL,
exchanges: [cacheExchange, fetchExchange],
});
Where VITE_GRAPHQL_URL
is an environment variable that's passed in through our stacks. We looked at this back in the Project Structure chapter.
To ensure that the useTypedQuery
hook is able to access our Urql client across our app, we wrap it around our app using the React Context.
<React.StrictMode>
<UrqlProvider value={urql}>
<App />
</UrqlProvider>
</React.StrictMode>
Render comment count
Now we need to render the results. The Home
component renders each article on the homepage as a <li>
.
Replace the <li>
tag in the return
statement of the Home
component with.
<li key={article.id} className={styles.article}>
<div>
<h2 className={styles.title}>
<Link to={`/article/${article.id}`}>{article.title}</Link>
</h2>
<a target="_blank" href={article.url} className={styles.url}>
({article.url.replace(/(^\w+:|^)\/\//, "")})
</a>
</div>
<div className={styles.footer}>
<strong>{article.comments.length}</strong>
<span className={styles.footerSeparator}>•</span>
<Link to={`/article/${article.id}`}>View Comments</Link>
</div>
</li>
Here we are rendering the count of the comments and linking to the article page.
Client-side routing
The article page is available at /articles/:id
. Since our app is a frontend SPA (single-page application) we use a client-side router, called React Router to handle these routes.
Behind the scenes
Let's look at how our router is configured.
We currently have two pages in our application:
- Homepage —
/
- Articles page —
/articles/:id
We also need a route to handle 404 pages. For now, we'll redirect everything that doesn't match — *
, to the homepage.
All of this is configured on the app level in packages/web/src/main.tsx
.
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="article/:id" element={<Article />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</BrowserRouter>
);
}
In our article page, we can grab the id of the article from the URL. We do this using the useParams
React Router hook.
import { useParams } from "react-router-dom";
export default function Article() {
const { id = "" } = useParams();
// ...
Styling components
We also need to add a couple of styles to render the comments count in our homepage.
Add this to the bottom of our stylesheet in packages/web/src/pages/Home.module.css
.
.footer {
margin-top: 0.8rem;
}
.footerSeparator {
margin: 0 0.5rem;
}
Now if you refresh the app, you should see the comment count being displayed under each article.
Next, let's allow our users to view the comments and post them!