Build a Cookie Clicker App with Clerk and Hasura
- Category
- Guides
- Published
In this tutorial we will use Clerk with Hasura to build a full-stack Next.js app with a database and GraphQL API, all without having to write any backend code.
Hasura provides a GraphQL engine that can help you build real-time APIs and ship modern apps faster. Although Hasura itself does not handle authentication, you can bring your own auth server and integrate it with Hasura via JWTs. This is where Clerk fits in.
In this tutorial we will use Clerk with Hasura to build a full-stack Next.js app with a database and GraphQL API, all without having to write any backend code.
If you would like to skip ahead and see the completed codebase, browse to the repo here.
Assumptions
This tutorial makes the following assumptions:
- Basic command line usage
- Node.js installed and
npm
(oryarn
if you prefer) for package management - Experience with React components and hooks
- Familiarity with the Next.js application structure (created with
create-next-app
) - Comfortable running Docker Compose commands (alternative path is to use Hasura Cloud)
- Clerk and Hasura accounts already set up (if you haven’t done so, do it now... we’ll wait)
Set up Clerk project
We’re going to start off with creating a new project from the Clerk dashboard. I’m going to name this application “More Cookies Please” (you’ll see why shortly) and leave the default options selected for authentication strategy and turn on social login with Google.
The next thing we need to do is navigate to JWT Templates from the left menu and create a template based on Hasura.
The next thing we need to do is navigate to JWT Templates from the left menu and create a template based on Hasura.
Click the “Apply changes” button and navigate back to the Home dashboard. Here we’re going to copy the Frontend API key.
That’s all we need to do in the Clerk dashboard.
Clone the starter repo
Now it’s time to clone the clerk-hasura-starter repo from GitHub. Name this project directory more-cookies-please
to match the Clerk instance name.
Once you have the repository downloaded to your computer, run the following commands to change into the directory, install the package dependencies, and copy the .env.local.sample
so we can set a couple environment variables:
Add in the Frontend API key from Clerk that was copied earlier. We’re also going to set the GraphQL endpoint even though it hasn’t been created yet.
Note: CLERK_API_KEY
is available in the sample but isn’t needed for this tutorial.
Once those environment variables are set, start up the application with npm run dev
Open your web browser to http://localhost:3000 and you should see the starter application homepage, which prompts you to sign up. So now let’s sign up for an account.
Click the Sign up button and you will be redirected to a Sign Up form generated for your application with Clerk components. I’m going to choose Sign up with Google since it's the fastest (and doesn’t require a new password).
Once you’ve signed up and logged in, you should see the following screen:
Now it’s time to customize this application. We’re going to install a little package I created based on the excellent react-rewards library and inspired by Cookie Clicker (thank @bsinthewild for reminding me of this).
Note: ⚠️ You will see some warnings about conflicting peer dependencies due to a mismatch of React versions, but it’s safe to proceed.
We’re going to create a new file called MoreCookies.js
inside of the components/
folder.
Because this library does not support server-side rendering (SSR), we need to make use of the dynamic imports from Next.js or we’ll get some nasty errors.
Now let’s add the component to pages/index.js
:
The ClerkFeatures
component can be removed, but the Footer
and Home
components should remain untouched.
You can see here we are making use of the SignedIn
and SignedOut
components from Clerk. We can also finally see the big cookie button!
Go ahead and click it for a nice reward. You’ve earned it. 🍪
Set up Hasura GraphQL engine
Clicking the cookie is a lot of fun, but what is even more fun? Keeping count of all those clicks!
This is where the Hasura GraphQL integration comes in.
There are two different ways we can connect to Hasura: we can use Hasura Cloud or Hasura Core running from a Docker container. We’re going to do the latter for this tutorial, but you can see instructions for connecting with Hasura Cloud in our integration documentation.
The starter repo already contains the docker-compose.yml
file we need.
The only part we need to update here is the JWT secret. Uncomment the line for HASURA_GRAPHQL_JWT_SECRET
and add in the value for your Clerk Frontend API. The JWT URL path points to the JSON Web Key Set (JWKS) endpoint we have set up for your application.
Note: Make sure the https:// protocol is included in the URL in front of the Frontend API.
Once the JWT secret is set, run the following command:
This will spin up Docker services for GraphQL engine as well as a Postgres database.
You can confirm that the services are running correctly with the following:
If all is good, you should see output similar to:
Head to http://localhost:8080/console to open the Hasura console.
Hasura has already done the work of setting up a GraphQL endpoint for us and also provided the GraphiQL integrated development environment (IDE) to explore the API.
It’s time to set up the database to keep the score count.
Navigate to the Data page and fill out the form to Connect Existing Database. (Remember we have the Postgres one running from Docker Compose?)
Name the database default
(or something more clever) and input the database URL copied from the docker-compose.yml
file. Click connect and the data source will be added.
The next step is to create the database table named scoreboard
and add two fields:
user_id
is a Text field that will contain the user ID from Clerkcount
is an Integer field that will keep track of the click count
Set the user_id
as the Primary Key for the table. Then click the Add Table button.
The next thing we need to do is set permissions for the “user” role. Click on the Permissions tab and enter user
as a new role. Then we need to set the basic CRUD (Create, Read, Update, Delete) operations on the table.
Insert (Create)
For Insert permissions, set the following values:
- Row insert permissions: Without any checks
- Column insert permissions:
count
checked - Column presets:
user_id
from session variableX-Hasura-User-Id
With the Clerk integration, the user ID will be set as the session variable and Hasura will then set that as the user_id
column when the request is made.
Select (Read)
For Select permissions, set the following values:
- Row select permissions: With custom check
{"user_id":{"_eq":"X-Hasura-User-Id"}}
- Column select permissions:
count
anduser_id
checked
The custom check ensures only the current authenticated user can read their own count. If the user ID from the session variable matches the one from the table, the user is granted read permission to every column in their database row.
Update
For Update permissions, set the following values:
- Pre-update check: With same check as select
{"user_id":{"_eq":"X-Hasura-User-Id"}}
- Column update permissions:
count
checked
Having the same custom check prevents another authenticated user from updating someone else’s count.
Delete
We can skip over Delete permissions since we aren’t implementing a mechanism to delete user records from the scoreboard.
Your final permissions access chart should look like the following:
Configure the client
Now it’s time to make authenticated requests from the codebase to Hasura.
If you take a look at hooks/index.js
, you can see the useQuery
hook that has been set up. It makes use of graphql-request, a minimal GraphQL client, with the useSWR hook to perform query requests to the GraphQL endpoint.
What we’re doing is reading the custom JWT (from the template we named hasura
) from the session object provided by Clerk. Note that the call to getToken
is asynchronous and returns a Promise that needs to be resolved before accessing the value.
We pass the custom fetcher function, which accepts a GraphQL query and optional variables, to useSWR
. The blockRequest
parameter is something we’ll make use of later to prevent certain calls from happening.
Let’s try this out to make sure we can get some data. Open up components/MoreCookies.js
and import the useQuery
hook and log the data
to the console:
If all went well, you should see the following:
The data is undefined
at first but then gets populated. scoreboard
is an empty Array because we haven’t recorded any click counts yet. So let’s do that now.
In order to make the GraphQL mutation we’re going to create another custom hook in hooks/index.js
:
This hook accepts the count
as the first parameter and the data
object containing previous data as the second parameter. It makes use of the useQuery
hook to apply the insert_scoreboard_one
insert mutation as an upsert. Instead of adding multiple rows to the database table, the on_conflict
argument sets a constraint on the primary key (user_id
) and if it already exists, only the count
column will be updated. Both count
and user_id
values are returned from a successful mutation.
If we replace the useQuery
with useCountMutation
in the MoreCookies
component, we can give us some credit for those clicks we’ve already made. (I set mine at 10
but you can be more or less generous.)
If you look at the browser console, you should now see something like:
You can confirm that this made it into the Postgres database by going to the Data tab in the Hasura Console and clicking into scoreboard and Browse Rows:
Success! The count (fake or not) has made it into the database and is associated with the authenticated user.
So now that everything is working as intended, we can go ahead and connect it all together. We’ll add one more custom hook that performs both the initial useQuery
and the useCountMutation
. This is going to need both useState
and useEffect
from React so make sure you import those.
It returns the current count
as well as an increment
function, both of which we can pass as props to the <CookieClicker />
component.
By setting the count
and onClick
props on CookieClicker
, it will now reward you with cookies based on the number of times the button is clicked. Keep clicking for more cookies!
Closing thoughts
Hope you had fun building this. You now have a complete Cookie Clicker app built using Clerk, Hasura, and Next.js — with no backend code required! To take it even further, you could implement an actual scoreboard that keeps track of all the cookie clicks from multiple users. Then deploy this app to production, share it with your friends, and see how much idle time they have on their hands. 😆
If you enjoyed this tutorial or have any questions, feel free to reach out to me (@devchampian) on Twitter, follow @ClerkDev, or join our support Discord channel. Happy coding!
Ready to get started?
Sign up today