All code for this blog post can be found at https://github.com/bkinsey808/nx-next-strapi.
The Case for GraphQL
GraphQL is rapidly growing in popularity as an API alternative to REST. GraphQL is self-documenting to a large extent and allows a client to efficiently query just for the data wanted. GraphQL comes with a wide range of tooling on both the editor and the browser to make the developer experience a real pleasure. I would also argue that GraphQL allows client development to be more decoupled from server development. A more thorough comparison can be found here.
The Case for Urql
I have worked with the extremely lightweight graphql-request and the heavy-weight Apollo Client, but Urql is my preferred client-side GraphQL library in most cases. I believe Urql is unique in that it supports both a document cache and a normalized cache, and both are implemented as exchanges (similar to middleware), which hints at the beauty and power of Urql’s architecture. The exchange architecture gives Urql the best of both worlds: it feels lightweight, yet is extensible to very nearly the full power of Apollo Client.
The Case for GraphQL-Code-Generator
While Apollo’s codegen tool is also excellent, The Guild’s GraphQL-Code-Generator won me over with its awesome plug-in system, which just so happens to have a custom plug-in specifically for TypeScript and Urql. Not only does this tool codegen TypeScript types for query results, it even generates fully typed hooks for using the queries in React functional components.
GraphQL Browser Extensions
The network tab on Chrome Dev Tools is a good start. One hint is that you can filter to just see the GraphQL requests without the extra “OPTIONS” requests by filtering on graphql -method:OPTIONS like this:
The problem is that you cannot see the names of your GraphQL requests without digging into the details. The solution is an extension called GraphQL Network Inspector.
To be clear, the name is the string just after query or mutation, and it is best practice to use unique names and always capitalize, for example:
Urql also has an excellent dev tools extension, but don’t forget you will need to add the devtools exchange in your client code.
One more Chrome extension I’ve found very helpful is called ModHeader, which can be handy for setting the Authorization header when using an instance of GraphQL Playground.
GraphQL VSCode Extension
I use only one GraphQL extension on Visual Studio Code, the official one. It’s not perfect, but it supports autocomplete, when set up properly, which is pretty amazing. In VSCode on Windows, you put your cursor where you want and Ctrl+Space to open up the autocomplete options.
Show me the Code!
All the code in this article can be found here. Let’s start with getUrqlClientOptions:
The authExchange is in charge of putting the token in the Authorization header, among other things. More information about how this was set up here. For some reason, I found the authExchange was not compatible with NextJs’s SSG (Server Side Generation). This is an inconvenient but not insurmountable obstacle.
Authorization on requests. For this, I use a pair of custom higher order functions (HOCs) which build off of next-urql‘s withUrqlClient:
Here is a page, index.tsx, generated with SSG:
Note that usePostsQuery is codegen’d from the POST_QUERY string. Note also the withNoAuthUrqlClient HOC. The query, run at build time, will not have the Authorization token set. This component’s HTML is then generated on the server-side at build time (SSG).
Here is a page [slug].tsx generated with SSR instead of SSG.
SSR means it is generated on the server side at request time instead of build time. In this case, I found SSR was necessary in order to show new comments as they were added.
In order to add a comment, you have to be authenticated. Here is the login.tsx page:
loginForm.tsx:
useLoginForm.tsx:
Note that useLoginMutation() is codegen’d.
Let’s look and see how this gets submitted, getLoginOnValidSubmitHandler.tsx
And that’s how the Auth token is set. Unfortunately, the GraphQL error coming from strapi is not fully typed, but the data always is.
How Strapi handles Comment requests
This is how the Comment Collection-Type is set up in Strapi:
Permissions are set up like this:
But that doesn’t mean we want any authenticated user to be able to do whatever they want with any comment!
We don’t want a person deleting somebody else’s comment, for example. Strapi’s documentation does a good job covering how to make sure users can only do stuff with their own content via the API, but I will show the code for this specific case.
In comment.js, we have to make sure we automatically add the Author to the createComment request (based on the auth token), so the client doesn’t have to set the Author, nor should we trust the client to set Author directly. This is the code:
If a user tries any shenanigans against another user’s comment, this Strapi code should check the Author, and stop them:
The Future of NextJs/Strapi
The NextJs/Strapi stack is a powerful one, made even better with GraphQL, in my opinion. Give it a spin and let me know what you think. The limitation with Urql authExchage blocking SSR/SSG is unfortunate, and hopefully, a solution or better workaround will be found, but besides that, I haven’t run into too many issues going down this path. It seems like a very promising direction.