Building a fullstack web3 application with NextJS + The Graph
Are you curious about how to handle data requests when querying the blockchain? In this guide, we will walk through the process of building a web3 full-stack web application, covering tools like graph-client and urql to access and manage data from decentralized networks efficiently. Whether you are a seasoned developer or just starting in the web3 space, this guide will provide the steps to create a front-end app consuming data from the blockchain.
Web3 full-stack development is a relatively new subject. In web2, it’s all about centralized servers and HTTP requests. Consuming data from an API is a standardized process that most full-stack developers begin to learn in their early coding days.
But in decentralized applications, the data we want to consume lives on the blockchain, and querying is complex for several reasons.
How do we solve this querying issue?
Enter The Graph
The Graph in an indexing protocol used to query decentralized networks, like Ethereum. Instead of querying directly from the blockchain or keeping our centralized server, we can rely on The Graph by creating and deploying a subgraph, which will index a determined contract (or several, more on that later). Each subgraph is a tiny slice of the blockchain that we can query using GraphQL. Why is it better than a centralized solution? The Graph uses a structure of indexers and curators that helps it achieve decentralization and data consistency, making querying the blockchain easier without sacrificing its main benefits. I recommend this article for a deeper understanding of how The Graph works.
By the end of this tutorial, we should grasp how The Graph works, how to use subgraphs to query and consume data from a frontend, and which tools can be used to make the process simpler.
Project overview
In this guide, we will build a simple front-end application that queries data from a subgraph. For our data, we will index ENS contracts. ENS stands for Ethereum Name Service and is a known project that mints .eth domains as ERC721 tokens. I’m choosing this project because it has a lot of movement and transactions, which translates into yummy data that we can use to fuel our front-end app. We will index two ENS smart contracts to create an ENS explorer app that shows the latest transactions and historic transaction amounts per day.
Pre-requisites:
Tool overview
Project structure
For project scaffolding, we will have two folders, one containing our front-end and everything we need for rendering and querying data. The other will include the local version of our subgraph.
Our first step is to create a folder to keep both the front-end and subgraph as subfolders
mkdir ens-fullstack-app
cd ens-fullstack-app
Local subgraph setup
After creating our parent folder is time to initialize our subgraph; for that, we’ll be using The Graph’s official CLI: graph-cli
$ npm install -g @graphprotocol/graph-cli
To initialize our subgraph we run the following command:
graph init
The prompt will guide us trough the various options, here are the recommended settings:
Protocol: Ethereum
Product: subgraph studio
/* subgraph-slug is an identifier for the subgraph, we will need it later in Subgraph Studio */
Subgraph-slug: ens-explorer
Directory: just press enter
Ethereum network: mainnet
Contract address: 0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5
Contract Name: ETHRegistrarController
Index contract events as entities (Y/n): true
Add another contract (Y/n): false
Checking “index contract events as entities” is an excellent choice since it will generate an initial schema based on the contract and creates a mapping for each event the contract emits. This option saves us a lot of work by creating a subgraph structure that can be deployed as-is.
Let’s explore the generated files:
The important files are:
Before deploying, let’s make a small configuration change: let’s open subgraph.yml and under source, add startBlock
dataSources:
- kind: ethereum
name: ETHRegistrarController
network: mainnet
source:
address: "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5"
abi: ETHRegistrarController
# ETHRegistrarController was deployed on block 9380471, querying prior to that makes no sense
startBlock: 9380471
Why are we doing this? By default, The Graph indexes from a blockchain’s first block. It’s always a good idea to start at the block where our contract was deployed to avoid indexing blocks that won’t generate entities
Creating a subgraph in Subgraph Studio
Go to Subgraph Studio and connect your wallet, you’ll be requested to sign a transaction to ensure that you have actual access to that wallet. Don’t worry, it’s free.
Creating the subgraph
Once the wallet is properly connected we can hit the “Create subgraph” button which will open the following UI:
Enter your details, and keep in mind that “subgraph name” should be the same value you used for your subgraph slug. Once you are done hit “Create Subgraph”.
Your subgraph is now created and you can access the deploy key:
Now let’s go back to our terminal and run some commands to generate all code and deploy
graph auth --studio [YOUR_DEPLOY_KEY]
graph codegen && graph build
graph deploy --studio [YOUR_SUBGRAPH_SLUG]
When requested for a version just type 0.0.1 and hit enter. Once the deploy finished a success message should appear along with an URL for queries. Save it as we will need it to query from the frontend.
Now if we go back to Subgraph Studio status should have changed to deployed
We should also see our development query URL (the same link we saved from the CLI) and a tab “Playground” where we can test our queries:
Keep in mind that synching may take a while, and not all data will be available, but we can still start using the subgraph. If there are no results after a couple of minutes, check the Logs tab and verify that indexing starts at the desired block.
Our subgraph is now deployed and ready to use, let’s move on to the next task.
Building the dApp
Now that our data source is ready and deployed, we can build a decentralized application. For that, we will be using NextJS.
NextJS is a ReactJS framework that provides a very easy quickstart to creating frontend apps. There are some things we need to take into account while developing:
Getting started
Let’s begin by creating our app with default settings:
npx create-next-app —-typescript
Follow the prompts in the terminal by accepting the default unless you want to change anything. When finished, a success message should appear. NextJS will also initialize the folder as a repository if you're going to start versioning your changes immediately.
Now that we have our starting code, we’ll begin by laying out the app's structure and cleaning up a little bit. First, we’ll go to globals.css and replace NextJS-generated content with the following:
/* global.css */
:root {
--general-border: #e7eaf3;
--font-primary: #001001;
--general-background: #f8f9fa;
--blue-accent: #3498db;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande",
"Lucida Sans Unicode", Geneva, Verdana, sans-serif;
background-color: var(--general-background);
}
a {
color: var(--blue-accent);
text-decoration: none;
}
a:hover {
text-decoration: underline;
cursor: pointer;
}
This way, we’ll eliminate unused code and have some readymade color variables for our components. For folder structure, I like to keep a components folder with subfolders pointing to all the frontend components. Each subfolder will then contain an index.tsx and style.module.css per component, making the code easier to import.
So in our dapp folder at the root level, create the component folder, and inside, create a subfolder named Container.
Let’s now create index.tsx and styles.module.css:
// dapp/components/Container/index.tsx
import React from "react";
import style from "./styles.module.css";
import Link from "next/link";
type ContainerProps = {
title: string;
children: React.ReactNode[] | React.ReactNode;
};
export default function Container({ title, children }: ContainerProps) {
return (
<>
<nav className={style.navbar}>
<Link href="/">ENS Explorer</Link>
</nav>
<main className={style.mainContainer}>
<section>
<h2>{title}</h2>
{children}
</section>
</main>
</>
);
}
/* dapp/components/Container/styles.module.css */
.navbar {
height: 60px;
border-bottom: 1px solid var(--general-border);
color: var(--primary-font);
padding: 20px;
}
.mainContainer {
padding: 30px;
min-height: 100vh;
color: var(--primary-font);
width: 100%;
}
.mainContainer h1 {
margin-bottom: 20px;
}
.mainContainer h2 {
margin-bottom: 20px;
}
Here we have a simple layout component that will render a navbar with a <Link> home.
We also have two dynamic props: title, a text string, and children, a collection of ReactNodes. Because of how NextJS handles routes and limits global CSS, this approach of making a general layout using component composition works well to avoid repeating styles on pages.
Let’s add this component to our app. In pages/index.ts, replacing NextJS generated homepage with our own:
// pages/index.tsx
import Head from "next/head";
import Container from "@/components/Container";
export default function Home() {
return (
<>
<Head>
<title>ENS Explorer</title>
<meta name="description" content="ENS Explorer" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Container title="ENS txs">
</Container>
</>
);
}
If we run the project, we’ll see something like this; not very exciting, but it’s a nice start to fill it with data!
Here we want to showcase a list of the latest transactions of ENS names. For that we need to query from our subgraph
Querying the subgraph with graph-client and urql
As I mentioned in the intro, we will be using two things to complement our querying work in nextjs,
Let’s install all needed dependencies. Inside our dapp folder:
npm install --save-dev @graphprotocol/client-cli
One great thing is that graph-client generates all its code output on build-time, which means that our application will have access to it at runtime, with no extra steps required. That also means we’ll have to remember to build after changes, or we won’t see them impacting our schema.
That being said, let’s add an npm script to simplify bulding
in our dapp’s package.json:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"build-graphclient": "graphclient build"
},
Now we need to create graph-client’s configuration file. For that, just create a file named .graphclientrc.yml at the root of your project
sources:
- name: ETHRegistrarController
handler:
graphql:
endpoint: [YOUR_DEVELOPMENT_QUERY_URL]
We just added our data source pointing to our subgraph endpoint; if you don’t remember where to find yours go to https://thegraph.com/studio, enter your subgraph, and copy the generated development query URL
With our configuration file ready is now time to build. We’ll use our freshly added npm script
npm run build-graphclient
Sigamos en contacto: suscribite a Sin códigos, mi newsletter quincenal. También podés seguirme en redes para estar al tanto de todo mi nuevo contenido