How to Create a FullStack Dapp using Next JS, Tailwind CSS and The graph. (Part 1)
Tags: web3, nextjs, javascript, tutorial
Published: 2022-08-13 17:20:00

Hello there 🖐🏽, This is a written tutorial on a Fullstack Decentralized application using some web3 technologies, I call this dapp **NFT AIR**

The idea behind this dapp is to allow people to showcase their artwork to others, but not for sale. it can be memes, pictures, drawings, sketches etc.

   And I know what you're thinking and **NO** its not like an Opensea type of Dapp or a web3 version of insta, and yeah its a bit similar to those apps as people can upload their artwork for viewing, which can either be downloadable or not also seeing and also able to react to posts of others.

The full code of we'll be going through in this tutorial is available on my GitHub [here](https://github.com/OleanjiKingCode/Improved-MemeForest) you can check it as we progress down the tutorial, the fully deployed website is also [here](https://improved-meme-forest.vercel.app/). 

[**NOTE** : If you wan to improve some feature you are welcome to make Pull requests or raise an issue ill be sure o look at them and work on it together]



The preview of NFT AIR's website
![Nft Air's website preview ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2j8ojhgxhk7b0edbtjz.gif)




The preview of the Dapp (above) is what this tutorial is gonna achieve and likei said before the name i gave it is **NFT AIR** (Don't ask about it, I know it sucks 🙈).
Unfortunately this is only Part 1 of the full Dapp so this Part only explains and shows how the backend works using theses technologies  :

- [Solidity] (https://docs.soliditylang.org/en/v0.8.16/#): The language used to write the backend of the Dapp.
- [Hardhat] (https://hardhat.org/): This is the Ethereum development environment we'll be using in this tutorial.
- [The graph protocol] (https://thegraph.com/en/): This is used for indexing our data that will be uploaded to the blockchain.
- [Ankr] (https://www.ankr.com/): this is our rpc node provider
- [Polygon Mumbai Testnet] (https://polygon.technology/): The Blockchain we'll be using in this tutorial.
- [Ethers.js] (): Ethereum Web Client Library used to interact with the blockchain.

## Challenges and Thought

I didn't face much challenges when building the backend as I've done something similar in a [previous project] (https://meme-forest-demo-app.vercel.app/), but this is my first time using [The graph protocol] (https://thegraph.com/en/) and reading about and using [Ankr] (https://www.ankr.com/) node provider for a Dapp project I mostly use [Alchemy] (https://www.alchemy.com/) or [Infura] (https://infura.io/).


## Overview Of The Project

Let's take a look at the backend of this NFT AIR project in a simpler manner, If so then these are what we'll be accomplishing in this Part

1. Installing dependencies
2. Writing the Smart contract in Solidity language
3. Testing the Smart contract and Hardhat Config. 
4. Deploying of Smart Contract
5. Setting up a subgraph.
6. Tracking and storing our data emitted using The graph Indexers 

## Prerequisites 

1. Text Editor of your choice Installed on your machine
   (preferably [Vs code] (https://code.visualstudio.com/) )
2. [Node.js] (https://nodejs.org/en/) should be installed also.
3. And most importantly have a nice bottle of water/juice next to 
   you 😉.


## Project BuidLing and Setup
   Lets Begin! (Take a sip of that drink you have and lest start)


- **Next app** 
In your Command prompt/terminal pointing to the location you want the dapp in, and then you run the code below using [npm] (https://nodejs.org/en/) or [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#windows-stable) or pnpm, any one of your choice to create a next app

```
yarn create next-app nft-air-dapp-tutorial
```

- **Installing dependencies** 
we need to install a few dependencies before writing any line of code, we would we installing; hardhat, ether.js. 
_( Note : in the "nft-air-dapp-tutorial" path )_

```
  yarn add ethers hardhat @nomiclabs/hardhat-waffle ethereum- 
  waffle chai @nomiclabs/hardhat-ethers @openzeppelin/contracts 
  dotenv
```
 
_you can also use "npm install" in place of yarn add_

- **Hardhat Development Environment**

In this tutorial we'll be using hardhat as the Ethereum dev environment, there are others like [foundry] (https://github.com/foundry-rs/foundry)  , [Truffle] (https://trufflesuite.com/truffle/).

```
yarn hardhat
```
![Hardhat Terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abcc9it8l7fz8k7zp3ex.png)
 
After running the line above you should be asked some question on the path and type of project, etc . Do them correctly and then open your app in vs code using the code below in your terminal.

```
code .
```
then your Visual Studio window should open up with some files and folders in the "Explorer tab" should look like this 

![File structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dntna4wvgj2abbx4ny2b.png)

The above image should be similar to your file structure in your Text editor.

- _contracts_: This folder would contain our smart contracts files written in solidity.

- _node_modules_ : This folder contains the dependencies installed that would be used in the project.

- _pages_: This folder is for the frontend part where the different pages of our dapp would be kept in.

- _public_: This is also a frontend folder  where our media files would be kept.

- scripts: This folder keeps in the deploy script for our contract. 

- _styles_: Like the name contains Files related to the styling for the frontend aspect of this dapp.

- _test_: This folder as the name suggests contains test scripts written in JavaScript to test our smart contract before deployment.


## Configuration

In your _hardhat.config.js_ file located in your project's root directory you can paste the code below there , (_I used solidity version 0.8.7 here yours may be higher_)

```
require("@nomiclabs/hardhat-waffle");

module.exports = {
  solidity: "0.8.7",
  networks: {
    hardhat:{
      chainId:1337
    },
};
```
This sets the solidity version used and the local hardhat network.


## Smart Contract

The smart contract code for this project is a bit much and may be overwhelming at first look, but I will explain through all the functions. 
The full solidity code for the contracts is [here] (https://github.com/OleanjiKingCode/Improved-MemeForest/tree/master/contracts).
To start create a new file in your contracts folder and name it _Nft-Air.sol_ and paste this code into it.

_(If you are having errors showing up on your import lines install Solidity and hardhat extension in vs code )_

```
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "hardhat/console.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract MemeForest is ReentrancyGuard{
    using Counters for Counters.Counter;
    Counters.Counter public NumOfAllMemes;
    Counters.Counter public NumOfAllMembers;

    struct MemeMembers {
        string Name;
        address MemeberAddress;
        uint MyId;
        uint MyMemes;
        uint MyStarredMemes;
        uint MyDeletedMemes;
        string Datejoined;
    }

    struct MemeFiles {
        string Memeinfo;
        address Owner;
        uint fileId;
        bool starred;
        uint Stars;
        uint Likes;
        string DateOfCreation;
        string FileType;
        bool IsDownloadable;
    }

    mapping(uint => MemeMembers) private IdMembers;
    mapping(address => bool) private alreadyAMember;
    mapping(address => mapping(uint => bool )) private DidyouStar;
    mapping(address => mapping(uint => bool )) private DidyouLike;
    mapping (uint => MemeFiles) private IdMemeFiles;
    mapping(uint => address) private StarredMemeFiles;

    uint public NumberOfUploads;

    event Memberjoined (
        uint256 MemberId,
        string MemberName,
        string Datejoined,
        address MemberAddress, 
        uint256 MemberTotalMemes,
        uint256 MemberStarredMemes,
        uint256 MemberDeletedMemes,
        uint256 MemberTotalLikes
    );

    event CreateMeme (
        uint256 MemeId,
        string MemeInfo,
        address MemeCreator,
        bool IsMemeStarred,
        uint256 MemeStars,
        uint256 MemeLikes,
        string DateOfCreation,
        string Filetype,
        bool IsDownloadable,
        uint Membernum,
        uint NewNumberMemberMemes
    );

    event StarredMeme (
        uint256 MemeId,
        uint256 NewStarNo,
        uint256 CreatorId,
        address CreatorAddress,
        uint256 CreatorStarredMemes
    );

    event UnStarringMeme (
        uint256 MemeId,
        uint256 NewStarNo,
        uint256 CreatorId,
        address CreatorAddress,
        uint256 CreatorStarredMemes
    );
    event LikingMeme (
        uint256 MemeId,
        uint256 NewLikesNo,
        uint256 CreatorId,
        address liker
    );
    event UnLikingMeme ( 
       uint256 MemeId,
        uint256 NewLikesNo,
        uint256 CreatorId,
        address Unliker
        );

//////////////////////////////
/////////Functions here//////
/////////////////////////////

}
```

Explaining the full contract in parts this above contains:

- The libraries imported,
- Global variables used in the contract,
- mappings, structs and events 


This part of the smart contract doesn't have any functions only variables.

In the above contract after importing a couple of libraries from [openzeppelin] (https://docs.openzeppelin.com/) and also for writing to the console using console.sol from hardhat.

There are two structs are defined which represent the details for both members of the dapp and their artwork (nft created).
Which is followed by a series of mappings that are used to keep track of some variables.

Finally there are 6 different events created which represent the actions that would affect/change the data stored on the blockchain, they are going to be emitted later on which signals the subgraph. 

_The remaining smart contract code (below)_


```
function CreateMembers (string memory _name, string memory _date) public nonReentrant{
        require(alreadyAMember[msg.sender] == false, "You are already a member");

        NumOfAllMembers.increment();
        uint currentMemberId = NumOfAllMembers.current();

        IdMembers[currentMemberId] = MemeMembers (
            _name,
            msg.sender,
            currentMemberId,
            0,
            0,
            0,
            _date

        );

        alreadyAMember[msg.sender] = true;
        
        emit Memberjoined (
            currentMemberId,
            _name,
            _date,
            msg.sender,
            0,
            0,
            0,
            0
        );
    }


    function fetchMembers() public view returns(MemeMembers[] memory) {
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentIndex = 0;
        MemeMembers[] memory members = new MemeMembers[] (currentMemberNum);
        for (uint256 index = 0; index < currentMemberNum; index++) {
            uint currenNum = IdMembers[index + 1].MyId;
            MemeMembers storage memeMem = IdMembers[currenNum];
            members[currentIndex] = memeMem;
            currentIndex+=1;
        }
        return members;
    }


    function GetMemberByAddr(address _member)external view returns(MemeMembers[] memory){
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentIndex = 0;
        MemeMembers[] memory foundMember = new MemeMembers[] (1);
        for(uint i = 0; i< currentMemberNum; i++){
            if(_member == IdMembers[i+1].MemeberAddress ){
                uint currentmem = IdMembers[i+1].MyId;
                MemeMembers storage memMem = IdMembers[currentmem];
                foundMember[currentIndex] = memMem;
            }
        }
        return foundMember;
    }


    function IsAMember(address sender) external view returns(bool) {
        bool member = alreadyAMember[sender];
        return member;
    }

    function CreateMemeItems( string memory memeinfo,
    address _owner, 
    string memory _date,
    string memory _filetype,
    bool _isDownloadable
    ) 
    public nonReentrant{
        NumOfAllMemes.increment();
        uint256 currentMeme =  NumOfAllMemes.current();
        IdMemeFiles[currentMeme] = MemeFiles(
            memeinfo,
            _owner,
            currentMeme,
            false,
            0,
            0,
            _date,
            _filetype,
            _isDownloadable
        );
         uint currentMemberNum = NumOfAllMembers.current();
          uint currentNum;
          uint newMemes;
        for (uint i = 0; i < currentMemberNum; i++) {
            if(_owner == IdMembers[i+1].MemeberAddress){
                currentNum = IdMembers[i+1].MyId;
                newMemes = IdMembers[currentNum].MyMemes;
                newMemes +=1;
                IdMembers[currentNum].MyMemes = newMemes;
            }
        }

        emit CreateMeme (
            currentMeme,
            memeinfo,
            _owner,
            false,
            0,
            0,
            _date,
            _filetype,
            _isDownloadable,
            currentNum,
            newMemes
        );
    }


    function fetchAllMemes() public view returns(MemeFiles[] memory) {

        uint currentMemeNum = NumOfAllMemes.current();

        uint currentIndex = currentMemeNum;
        MemeFiles[] memory memes = new MemeFiles[] (currentMemeNum);

        for (uint256 index = 0; index < currentMemeNum; index++) {
            uint currenNum = IdMemeFiles[index +1].fileId;
            MemeFiles storage memeFiles = IdMemeFiles[currenNum];

            memes[currentIndex - 1] = memeFiles;
            currentIndex-=1;
        }
        return memes;
    }

    function LikeMeme(uint _id) public {
        uint currentMemeNum = NumOfAllMemes.current();
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentNum;
        uint256 newLikes;
        for(uint i = 0; i < currentMemeNum; i++){
            if(_id == IdMemeFiles[i+1].fileId) {
              
                newLikes = IdMemeFiles[i+1].Likes;
                newLikes +=1;
                IdMemeFiles[i+1].Likes =  newLikes;
                DidyouLike[msg.sender][_id]= true;  
            }
        }
        for (uint index = 0; index < currentMemberNum; index++) {
            if(msg.sender == IdMembers[index+1].MemeberAddress){
                currentNum = IdMembers[index+1].MyId;
            }
        }
    
        emit LikingMeme(
            _id,
            newLikes,
            currentNum,
            msg.sender
        );
    }

    function UnLikeMeme(uint _id) public {
        uint currentMemeNum = NumOfAllMemes.current();
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentNum;
        uint256 newLikes; 
        for(uint i = 0; i < currentMemeNum; i++){
            if(_id == IdMemeFiles[i+1].fileId) {
              
               newLikes = IdMemeFiles[i+1].Likes;
                newLikes -=1;
                IdMemeFiles[i+1].Likes =  newLikes;
                DidyouLike[msg.sender][_id]= false;
            }
        }
        for (uint index = 0; index < currentMemberNum; index++) {
            if(msg.sender == IdMembers[index+1].MemeberAddress){
                currentNum = IdMembers[index+1].MyId;
            }
        }
    
        emit UnLikingMeme(
            _id,
            newLikes,
            currentNum,
            msg.sender
        );
    }
    
    function WhatDidILike (uint _id, address sender) public view returns (bool) {
         bool youLiked =  DidyouLike[sender][_id];
         return youLiked;
    }

    function StarMeme(uint _id ) public {
        uint currentMemeNum = NumOfAllMemes.current();
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentNum;
        uint newstars;
        uint newstarredMemes;
        for(uint i = 0; i < currentMemeNum; i++){
            if(_id == IdMemeFiles[i+1].fileId) {
                IdMemeFiles[_id].starred = true;
                newstars=IdMemeFiles[_id].Stars;
                newstars+=1;
                IdMemeFiles[_id].Stars = newstars ;
                DidyouStar[msg.sender][_id]= true; 
            }
        }
        for (uint index = 0; index < currentMemberNum; index++) {
            if(msg.sender == IdMembers[index+1].MemeberAddress){
                currentNum = IdMembers[index+1].MyId;
                newstarredMemes=IdMembers[currentNum].MyStarredMemes;
                newstarredMemes +=1;
                IdMembers[currentNum].MyStarredMemes = newstarredMemes;
            }
        }
        emit StarredMeme (
        _id,
        newstars, 
        currentNum,
        msg.sender,
        newstarredMemes
        );
    }

    
    function RemoveStarMeme(uint _id) public {
        uint currentMemeNum = NumOfAllMemes.current();
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentNum;
        uint newstars;
        uint newstarredMemes;
        for(uint i = 0; i < currentMemeNum; i++){
            if(_id == IdMemeFiles[i+1].fileId) {
                IdMemeFiles[_id].starred = false;
                newstars=IdMemeFiles[_id].Stars;
                newstars-=1;
                IdMemeFiles[_id].Stars = newstars ;
                DidyouStar[msg.sender][_id]= false;
            }
        }
         for (uint index = 0; index < currentMemberNum; index++) {
            if(msg.sender == IdMembers[index+1].MemeberAddress){
                 currentNum = IdMembers[index+1].MyId;
                newstarredMemes=IdMembers[currentNum].MyStarredMemes;
                newstarredMemes -=1;
                IdMembers[currentNum].MyStarredMemes = newstarredMemes;
             }
        }
        emit UnStarringMeme (
         _id,
        newstars, 
        currentNum,
        msg.sender,
        newstarredMemes
        );
    }

    function WhatDidIStar (uint _id, address sender) public view returns (bool) {
         bool youStarred =  DidyouStar[sender][_id];
         return youStarred;
    }

    function fetchMyStarredMemes(address sender) public view returns (MemeFiles[] memory) {
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentNum;
        for (uint i = 0; i < currentMemberNum; i++) {
            if(sender == IdMembers[i+1].MemeberAddress){
                uint val = IdMembers[i+1].MyId;
                currentNum = IdMembers[val].MyStarredMemes;
            }
        }
      
        uint currentMemeNum = NumOfAllMemes.current();
        MemeFiles[] memory memes  = new MemeFiles[] (currentNum);
        uint currentIndex = 0;
        for (uint index = 0; index < currentMemeNum; index++) {
            uint id = IdMemeFiles[index+1].fileId;
            if(DidyouStar[sender][id] == true && IdMemeFiles[id].starred == true ){   
                MemeFiles storage memeFiles = IdMemeFiles[id];
                memes[currentIndex] = memeFiles;
                currentIndex+=1;
            }
        }   
        return memes; 
    }

    function fetchMyMeme(address sender) public view returns (MemeFiles[] memory) {
        uint currentMemberNum = NumOfAllMembers.current();
        uint currentNum;
        for (uint i = 0; i < currentMemberNum; i++) {
            if(sender == IdMembers[i+1].MemeberAddress){
                uint val = IdMembers[i+1].MyId;
                currentNum = IdMembers[val].MyMemes;
                console.log(val);
            }
        }
        
        uint currentMemeNum = NumOfAllMemes.current();
        uint currentIndex = 0;
        MemeFiles[] memory memes = new MemeFiles[] (currentNum);
         for (uint i = 0; i < currentMemeNum; i++) {
            uint id = IdMemeFiles[i+1].fileId;
            if(sender ==  IdMemeFiles[id].Owner  ){ 
                MemeFiles storage memeFiles = IdMemeFiles[id];
                memes[currentIndex] = memeFiles;
                currentIndex+=1;
            }
         }
         return memes;
    }

```

 There might be a confusion due to the word 'memes' used a lot this is because this dapp is an upgrade of [this] (https://github.com/OleanjiKingCode/MemeForest-Demo-App) called [Memeforest] (https://meme-forest-demo-app.vercel.app/).

The functions above are to be placed in the specified area in the first half of the smart contract.

The functions above are a self explanatory as their names represent what they do.

## Testing the smart contract
We have to test our smart contract before deployment to find errors that needs fixing, (All errors need fixing though lol xD ).

In order to do this open your test folder in your project's root directory and open the file named smaple-text.js and paste the code below inside (This is where we write our tests in js)

```
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Greeter", function () {
  it("Should create two members,ceate two nft,like one,star one,fetch all, fetch starred and fetch mine " , async function () {
    const Nft = await ethers.getContractFactory("MemeForest");
    const nft= await Nft .deploy();
    await nft.deployed();
    
    let today = new Date().toISOString().slice(0, 10);
    console.log(today)
    const [_, buyerAddress,thirdone] = await ethers.getSigners()
    const createMember = await nft.connect(buyerAddress).CreateMembers("first kid", today);
    const createMember2 = await nft.connect(thirdone).CreateMembers("second kid", today);
    const fetchMembers= await nft.connect(buyerAddress).fetchMembers();
    console.log(fetchMembers);
    const addr =  await buyerAddress.getAddress()
    const addr2 =  await thirdone.getAddress()
    const fectme = await nft.GetMemberByAddr(addr);
    console.log(fectme);
    let another = new Date().toISOString().slice(0, 10);
    await nft.connect(buyerAddress).CreateMemeItems("MemeLinkInfo1",addr,another,"jpeg",true);
    await nft.connect(thirdone).CreateMemeItems("MemeLinkInfo2",addr2,another,"mp4",false);

    const Allmeme = await nft.fetchAllMemes()
    console.log(Allmeme)

    console.log("liking meme")
    await nft.connect(thirdone).LikeMeme("1");

    console.log("staring meme")
    await nft.connect(buyerAddress).StarMeme("2");


    console.log("fetching starred memes right now...............")
    const FetchStarredMemes = await nft.connect(buyerAddress).fetchMyStarredMemes(addr);

    console.log(FetchStarredMemes)
    console.log("fetching starred memes right now...............")

    console.log("fetching my meme")
    const first = await nft.connect(buyerAddress).fetchMyMeme(addr)
    console.log(first)
    console.log("fetching my second meme")
    const second = await nft.connect(thirdone).fetchMyMeme(addr2)
    console.log(second)

  });

});

```
To run the above code do run the code below 

```
yarn hardhat test

or

npx hardhat test
```

You should get this message after a series of logs that appears on the terminal => _✔ Should create two members,ceate two nft,like one,star one,fetch all, fetch starred and fetch mine_

After getting the successful message we can now deploy our contract and then the setup of our subgraph.


## Deployment

The code below deploys our smart contract as well as writing the contact address of our contract to a file that we'll create now called _constant.js_ in the root directory of our project


```
const hre = require("hardhat");
const filesys = require("fs");

async function main() {

 
  const Meme = await ethers.getContractFactory("MemeForest");
  const meme = await Meme.deploy();
  await meme.deployed();
  console.log("MemeForest deployed to:", meme.address);

  filesys.writeFileSync('./constant.js' , `
  export const MemeForestAddress ="${meme.address}"
  
  
  `)
}


main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

```

A new file called **constant.js** would be created in the root of the project, but before that we need to actually deploy our contract and to do that, we need to first update our 
_hardhat.config.js_ file as we will be using an rpc endpoint from [ankr] (https://www.ankr.com/) and also using our **_private key_**.

**.env file **

- Create a file named '.env' in the base of project (i.e not inside any folder) then paste this code inside 

```
ANKR_ID = ""
PRIVATE_KEY=" "
```

We will need to populate the empty quotes, for the ANKR_ID visit [ankr] (https://www.ankr.com/) and get the mumbai testnet rpc endpoint [here] (https://www.ankr.com/rpc/polygon).

![Ankr Homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c24012wef6xyh8ec5nqc.png)


![Mumbai Rpc endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iv9zqy5xhp64m88mf1s6.png)
 
Copy the above endpoint and paste in the .env file.

For the Private key you need to have [Metamask] (https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?hl=en) installed in your browser.


![Getting private key from metamask](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z31gzrr27cviaclfipo3.png)

- First open your account details
- The click on the Export private key button 
- Enter your Metamask password and then copy your private key.


![Copy your private key ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3mvrl48n7p1yj08i9zj.png)

_**NOTE: DO NOT SHARE YOUR PRIVATE KEY WITH ANYONE **_

Paste the key in the PRIVATE_KEY variable in your .env file.


hardhat.config.js file should be updated to the code below

```
require("@nomiclabs/hardhat-waffle");
require("dotenv").config({ path: ".env" });

const ANKR_ID = process.env.ANKR_ID;
const PRIVATE_KEY = process.env.PRIVATE_KEY;

module.exports = {
  solidity: "0.8.7",
  networks: {
    hardhat:{
      chainId:1337
    },
    mumbai:{
      url:ANKR_ID,
      accounts:[PRIVATE_KEY],
    },
  },
 
  
};
```
So this is all we need to deploy our smart contract. Then we can finally run this 

```
npx hardhat compile
npx hardhat run scripts/deploy.js --network mumbai

or 

yarn hardhat compile
yarn hardhat run scripts/deploy.js --network mumbai
```

This deploys your smart contract and populate the constant.js file with your contract address.


## **Subgraph Setup**

[The graph protocol] (https://thegraph.com/en/) is a decentralized query protocol for indexing the blockchain. To understand more about the graph I suggest you read the [docs] (https://thegraph.com/docs/en/).

Before we continue I hope you have yarn installed on your machine, if not get yarn [here] (https://classic.yarnpkg.com/lang/en/docs/install/#windows-stable).

A brief explanation on how the graph protocol works; In our smart contract when we add/change data on the blockchain we emit events containing that information and it gets stored in the grph's node and when we need it back we can query the data just like how the picture below explains (from their website).

![The Graph workings Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bkqk60t4ziuv46o716zd.png)


To get started you'll need to create your own subgraph by heading to [The graph hosted service] (https://thegraph.com/hosted-service/), where you'll create an account if you don't have one and go to your dashboard, then click on _Add Subgraph_ button.
 

![My Dashoard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzegejdo8ewh1d32it9h.png)
When creating the subgraph you will fill in some details about the subgraph for the dapp you are building.

![Creating a subgraph](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qvncxl4x4tx428giuku.png)

After filling the details about the subgraph then you are taken to page with  the next steps and commands to follow shows up like in the diagram below.

![Commands to follow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3qq5my8lksdnfn6owk61.png)

- Firstly we'll need to run this (either yarn or npm) to install some packages needed.

![picture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/01xynrjbmcml0za0l3y0.png)
  
 - After the installation of the packages you need to create a file named _abi.json_ in the root of your project, then go to the _contracts_ subfolder in the _artifacts_ folder, where you'll see another folder in the name of your contract, open it and open the file with this format _**YOUR-CONTRACT-NAME.json**_.

[artifacts/contracts/YOUR-CONTRACT-NAME.json]

Then copy the abi (starting with a square braces and ending with one then paste the copied code in the abi.json file you created).

![Abi file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fmhssahonrrp2cs2wiaq.png)
 
Then continue to follow the commands in the Init step .

 ![Init](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7cg88ki6c9rvtxa1ybtv.png)
 

```
graph init --product hosted-service <GITHUB_USER>/<SUBGRAPH NAME>

```
 Run the above, changing the "GITHUB_USER" to your GitHub username and the "SUBGRAPH NAME" to the name you gave this subgraph.

![Choose Ethereum](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/reu2kud9jchg0aruqlfs.png)
 
Continue to follow the command, (remember the network is mumbai)

![Follow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/721bfd9n671o9z8vumia.png)

Write in abi.json where it asks for the file path, follow the rest and it generates a Subgraph.


![Next steps](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7cvx8cqzd81gg9ovz33l.png)

Then follow the next steps given like above. 

![NEXT](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8i6qclfhfr99iykydkl1.png)
 
The deploy key is the "Access Key" with a combination of numbers and letters in your subgraph page.

Then run 

```
yarn deploy
```
pointing to your new subgraph folder.

A link should now be there going to your new deployed subgraph

Alas!!!!!!! We've deployed our Subgraph.

_Its been a while so I suggest you take a sip from that drink of yours_

Now then,

How your subgraph should look when expanded

![folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hkl5btnkave2037vho7l.png)

 
Open the build folder and go to "subgraph.yaml" file and change this
![picture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vrcpnhna4461oa86rcpz.png)
by adding _startBlock: BLOCK_NUMBER_ after the abi line.

Your block Number can be gotten by pasting your address in the [MumbaiPolygonScan] (https://mumbai.polygonscan.com/) then scroll down to when the contract was created and copy your block number, like the below number in circle.


![the block number](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ob6r84cnksd6pd1spueb.png)


Done? Good work.

Open the schema.graphl above the sugbraph.yaml file and paste the code in there.

```
type Meme @entity {
  id: ID!
  MemeInfo: String! # string
  Owner: Bytes!
  IsStarred: Boolean!
  Stars: BigInt!
  Likes: BigInt!
  Date: String!
  FileType: String!
  IsDownloadable: Boolean!
  StarredAddresses:[Bytes!]
  LikesAddresses:[Bytes!]
}

type Memeber @entity {
  id: ID!
  Name: String!
  Adddress:Bytes!
  TotalMeme: BigInt!
  StarredMemes: BigInt!
  DeletedMemes: BigInt!
  Date: String!
}

```
**_[Like I said before you might be confused with the word memes I use mostly, this is because nft air is an extension of Memeforest and you can change the meme words to nfts or anything you want.]_**

The schema above contains Entities that represent data structures that will be stored in the graph's node.

We have two entities due to the fact that we are only storing 
- Member details and
- Their nft/memes/art they upload.

Run 

```
yarn codegen
```
To generate some code file, after which paste the code below in the typescript (.ts) file inside the src folder in your subgraph


```
import { Address, BigInt } from "@graphprotocol/graph-ts"
import {
  MemeForest,
  CreateMeme,
  LikingMeme,
  Memberjoined,
  StarredMeme,
  UnLikingMeme,
  UnStarringMeme
} from "../generated/MemeForest/MemeForest"
import { Meme, Memeber } from "../generated/schema"

export function handleCreateMeme(event: CreateMeme): void {
  // Entities can be loaded from the store using a string ID; this ID
  // needs to be unique across all entities of the same type
  let entity = Meme.load(event.params.MemeId.toString())
  // let address = new Address (0)
  // Entities only exist after they have been saved to the store;
  // `null` checks allow to create entities on demand
  if (!entity) {
    entity = new Meme(event.params.MemeId.toString())
  }
  entity.MemeInfo = event.params.MemeInfo;
  entity.Owner= event.params.MemeCreator;
  entity.IsStarred = event.params.IsMemeStarred;
  entity.Stars = event.params.MemeStars;
  entity.Likes = event.params.MemeLikes;
  entity.Date = event.params.DateOfCreation;
  entity.FileType = event.params.Filetype;
  entity.IsDownloadable = event.params.IsDownloadable;
  entity.StarredAddresses =[]
  entity.LikesAddresses =[]

  

  let member = Memeber.load(event.params.Membernum.toString());
  if(!member) {
    return ;
  }
  member.TotalMeme = event.params.NewNumberMemberMemes;
 
  member.save()
  entity.save()

}

export function handleLikingMeme(event: LikingMeme): void {
  let entity = Meme.load(event.params.MemeId.toString())

  if(!entity) {
    return ;
  }

  entity.Likes = event.params.NewLikesNo;

  let member = Memeber.load(event.params.CreatorId.toString());
  // You have to be a member to be able to like a meme
  if(!member){
    return;
  }
  let Thisaddress = member.Adddress;
  let memes = entity.LikesAddresses
  if(!memes){
    return;
  }
  memes.push(Thisaddress);
  entity.LikesAddresses = memes;
  entity.save()
}

export function handleMemberjoined(event: Memberjoined): void {
  let member = Memeber.load(event.params.MemberId.toString());
  if(!member) {
    member = new  Memeber(event.params.MemberId.toString());
    
  }
    member.Adddress = event.params.MemberAddress;
    member.Name = event.params.MemberName;
    member.Date = event.params.Datejoined;
    member.TotalMeme = event.params.MemberTotalMemes;
    member.StarredMemes = event.params.MemberStarredMemes;
    member.DeletedMemes = event.params.MemberDeletedMemes;
    member.save()
}

export function handleStarredMeme(event: StarredMeme): void {
  let entity = Meme.load(event.params.MemeId.toString())
  if(!entity) {
    return ;
  }
  entity.IsStarred = true;
  entity.Stars = event.params.NewStarNo;
  let member = Memeber.load(event.params.CreatorId.toString());
  // You have to be a member to be able to staR a meme
  if(!member){
    return;
  }
  
  let Thisaddress = member.Adddress;
  let memes = entity.StarredAddresses
  if(!memes){
    return;
  }
  memes.push(Thisaddress);
  entity.StarredAddresses = memes;
  entity.save()
  member.StarredMemes = event.params.CreatorStarredMemes;
  member.save()

  
}

export function handleUnLikingMeme(event: UnLikingMeme): void {
  let entity = Meme.load(event.params.MemeId.toString())

  if(!entity) {
    return ;
  }

  entity.Likes = event.params.NewLikesNo;
  let member = Memeber.load(event.params.CreatorId.toString());
  // You have to be a member to be able to like a meme
  if(!member){
    return;
  }
  let Thisaddress = member.Adddress;
  let memes = entity.LikesAddresses
  if(!memes){
    return;
  }
  let index = memes.indexOf(Thisaddress)
  memes.splice(index,1);
  entity.LikesAddresses = memes;
  entity.save()
}

export function handleUnStarringMeme(event: UnStarringMeme): void {
  let entity = Meme.load(event.params.MemeId.toString())
  if(!entity) {
    return ;
  }
  entity.IsStarred = false;
  entity.Stars = event.params.NewStarNo;
 

  let member = Memeber.load(event.params.CreatorId.toString());
  // You have to be a member to be able to like a meme
  if(!member){
    return;
  }
  let Thisaddress = member.Adddress;
  let memes = entity.StarredAddresses
  if(!memes){
    return;
  }
  let index = memes.indexOf(Thisaddress)
  memes.splice(index,1);
  entity.StarredAddresses = memes;
  entity.save()

  member.StarredMemes = event.params.CreatorStarredMemes;
  member.save()
}
```

Then save and deploy by running this in your terminal

```
yarn deploy
```
When its done your subgraph should now be **100% synced**

![Synced](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8w7hptst4sk6aw4mftax.png)


Then now you can open your subgraph playground and play around with it, but there wont be any data in there yet due to the fact that no data has been emitted out to the graph's node, which we'll do in the frontend part (PART TWO).


Well done! you've successfully done the following:
- Created a nextJs project and Installed Hardhat
- Wrote your smart contract
- Tested it and Deployed it
- and connected it to a graph's node by creating a subgraph.

Congratulations!!!!!!!!!!!!!!!!!!!!🎉🎉🚀🚀💖🎊
You have built half of a Full stack dapp.

If you have any questions you can comment below.