How to Build a Full-Stack NFT dApp -A Complete Guide

How to Build a Full-Stack NFT dApp -A Complete Guide

calenderOctober 17, 2023 | Editor Choice
clock 3 min read
--:--
--:--
 Zaryab Rafique

Zaryab Rafique

Ethereum is a layer 1 blockchain that uses the Proof of Stake consensus mechanism. It has established itself as a leading blockchain platform to create Ethereum dApps and smart contracts, gaining significant prominence. To facilitate efficient and effective Ethereum development, developers can leverage a wide range of tools and frameworks.

This article will cover building the Full Stack NFT dApp on the Ethereum Blockchain. This guide will cover several components involved in developing the NFT dApp tutorial. The main components are smart contract language Solidity, Hardhat framework for contract deployment and testing, IPFS integration on the Express JS, MetaMask Ethereum Wallet integration, and Web3 integration in React JS. 

This component makes the NFT dApps, where users can mint their cross chain NFTs by uploading their ARTs. Let’s start exploring these components individually and build the Full Stack NFT minting dApp.

Solidity

Solidity is an object-oriented, high-level language used to write smart contracts on EVM blockchains. It is a general-purpose, statically typed language that supports inheritance, libraries, and complex user-defined types. Solidity programmers use a compiler that compiles the source code into bytecode on the Ethereum Virtual Machine (EVM). We will use the solidity to write the NFT smart contract. 

Hardhat

Hardhat is a powerful Ethereum dApp tutorial development framework that provides a comprehensive tooling and infrastructure ecosystem for NFT minter dApp and Ethereum smart contracts. It offers a range of features and functionalities that enhance the development experience and streamline the workflow.

Install Hardhat

Install Hardhat by using the following command:
npm install --save-dev hardhat

Create a new HardHat Project

To create the sample project, create a folder named “NFT-Project” and run npx hardhat init in your project folder.

npx hardhat init

Create a new file

Now, create a new file in the contracts folder named “NFT721.sol” and paste the code below into it. 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract NFT721 is ERC721, ERC721Burnable, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdCounter;
    mapping(uint => string) public tokenUri;
    constructor() ERC721("NFT721", "NFT") {}
    function safeMint(string memory _uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(msg.sender, tokenId);
        _setURI(tokenId, _uri);
    }

    function _setURI(uint256 _tokenId, string memory newuri) private {
        tokenUri[_tokenId] = newuri;
    }

    function uri(uint256 _tokenId) public view returns (string memory) {
        string memory currentBaseURI = tokenUri[_tokenId];
        return string(abi.encodePacked(currentBaseURI));
    }
}

Compile the contract

npx hardhat compile 

After successfully compiling the contract, deploy it to the Sepolia testnet.

Deploy Contract

Deploying the contract on the Sepolia testnet requires the Infura key that can be accessed on the Infura NFT minting dApp website by login and the Sepolia private key that you can get on your MetaMask wallet. Make sure never to put real Ether into testing accounts.
Add the network and replace the infura and private keys in the hardhat.config.js file. Use the below code.

require("@nomicfoundation/hardhat-toolbox");

// Go to https://infura.io, sign up, create a new API key
// in its dashboard, and replace "KEY" with it
const INFURA_API_KEY = "KEY";

// Replace this private key with your Sepolia account private key
// To export your private key from Coinbase Wallet, go to
// Settings > Developer Settings > Show private key
// To export your private key from Metamask, open Metamask and
// go to Account Details > Export Private Key
// Beware: NEVER put real Ether into testing accounts
const SEPOLIA_PRIVATE_KEY = "YOUR SEPOLIA PRIVATE KEY";

module.exports = {
  solidity: "0.8.19",
  networks: {
    sepolia: {
      url: `https://sepolia.infura.io/v3/${INFURA_API_KEY}`,
      accounts: [SEPOLIA_PRIVATE_KEY]
    }
  }
};

Now run the command below to deploy:

npx hardhat run scripts/deploy.js --network sepolia

We successfully deployed the contract that generated the ABI (Application Binary Interface) that will be used in the React JS with web3 integration to interact with the contract. And here is the deployed Contract Address:

0x2330E22304853cDB96893B4c801D7F5e37904Dd4

Create an Express JS (Backend - IPFS) Project 

Now, we have to create the backend server that handles the IPFS metadata generation and uploading files to the IPFS. We will use the express js backend framework and the web.storage library, which is simple file storage with IPFS. Let’s start to build a DApp backend.

To create a node js project, make a folder named “backend” and run the following commands: init.

npm init -y

Open the backend in vs. code and run the following command in the project’s root to install the dependencies.

npm install express web3.storage express-fileupload dotenv

Create a file with the name “server.js” in the root of the project and paste the following code into it.

Now, create a web.storage account to get the API Key. Then, create a new .env file and add the API Key, as in the example below.

WEB3_STORAGE_API_KEY=REPLACE_KEY_HERE

Test the backend by running the following command on the terminal of the root directory:
node server.js

We have successfully built the backend that will be used for IPFS image uploading, metadata generation, and metadata file uploading to IPFS.

Create React JS Project

Next, we must create the React JS Web3 DApp and do the web3 integration and backend API integration. Let's create a DApp with the following command.

npx create-react-app fullstack-nftdapp

It will create the React app with a JavaScript template.

Open the full-stack dApp NFT in VS Code IDE and run the following command in the project’s root to install these dependencies.

npm install web3 axios

Paste the below code into the App.js file, We have integrated the web3 and MetaMask integration along with backend API for metadata generation and file uploading to IPFS.

import { useEffect, useState } from "react";
import "./App.css";
import Web3 from "web3";
import axios from "axios";
import ABI from "./ABI.json";

const NFTContractAddress = "0x2330E22304853cDB96893B4c801D7F5e37904Dd4";

function App() {
  const [web3, setWeb3] = useState();
  const [account, setAccount] = useState();
  const [contract, setContract] = useState(null);

  const [formData, setFormData] = useState({
    name: "",
    description: "",
    image: null,
  });

  const connectToMetaMask = async () => {
    const provider = window.ethereum;
    if (!provider) {
      alert("Please install MetaMask");
      return;
    }

    const accounts = await provider.request({ method: "eth_requestAccounts" });
    const web3 = new Web3(provider);

    const contractInstance = new web3.eth.Contract(ABI.abi, NFTContractAddress);

    setContract(contractInstance);
    setWeb3(web3);
    setAccount(accounts[0]);
  };

  useEffect(() => {
    connectToMetaMask();
  });

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  const handleImageChange = (e) => {
    setFormData({ ...formData, image: e.target.files[0] });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const { name, description, image } = formData;
      const formDataUpload = new FormData();
      formDataUpload.append("name", name);
      formDataUpload.append("description", description);
      formDataUpload.append("image", image);

      const response = await axios.post(
        "http://localhost:3001/upload/web3storage",
        formDataUpload,
        {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        }
      );

      console.log("Response from server:", response.data);
      console.log("URL:", response.data.ipfsUrl.toString());

      const result = await contract.methods
        .safeMint(response.data.ipfsUrl.toString())
        .send({ from: account, gas: 2000000 });
      console.log("Minted NFT:", result);

      // You can display a success message or redirect the user after successful upload
    } catch (error) {
      console.error("Error uploading image:", error);
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        <div>
          <h1>Full Stack NFT dApp</h1>
          <form onSubmit={handleSubmit}>
            <div>
              <label>Name:</label>
              <input
                type="text"
                name="name"
                value={formData.name}
                onChange={handleInputChange}
              />
            </div>
            <div>
              <label>Description:</label>
              <input
                type="text"
                name="description"
                value={formData.description}
                onChange={handleInputChange}
              />
            </div>
            <div>
              <label>Image:</label>
              <input
                type="file"
                name="image"
                accept="image/*"
                onChange={handleImageChange}
              />
            </div>
            <button type="submit">Mint NFT</button>
          </form>
        </div>
      </header>
    </div>
  );
}

export default App;

Conclusion

We have gone through a comprehensive guide to building a dApp from scratch. First, We wrote the NFT721 contract and compiled and deployed it using the Hardhat on the Spolia testnet. We created the backend or IPFS image uploading, metadata generation, and metadata file uploading to IPFS using the web3.storage library. 

After we made the React JS App, we integrated the web3, metamask wallet, and backend APs . This is the complete flow to build the Full Stack NFT dApp. This tutorial will enable the developer to build NFT mint dApp on the EVM blockchains.

Ethereum is a layer 1 blockchain that uses the Proof of Stake consensus mechanism. It has established itself as a leading blockchain platform to create Ethereum dApps and smart contracts, gaining significant prominence. To facilitate efficient and effective Ethereum development, developers can leverage a wide range of tools and frameworks.

This article will cover building the Full Stack NFT dApp on the Ethereum Blockchain. This guide will cover several components involved in developing the NFT dApp tutorial. The main components are smart contract language Solidity, Hardhat framework for contract deployment and testing, IPFS integration on the Express JS, MetaMask Ethereum Wallet integration, and Web3 integration in React JS. 

This component makes the NFT dApps, where users can mint their cross chain NFTs by uploading their ARTs. Let’s start exploring these components individually and build the Full Stack NFT minting dApp.

Solidity

Solidity is an object-oriented, high-level language used to write smart contracts on EVM blockchains. It is a general-purpose, statically typed language that supports inheritance, libraries, and complex user-defined types. Solidity programmers use a compiler that compiles the source code into bytecode on the Ethereum Virtual Machine (EVM). We will use the solidity to write the NFT smart contract. 

Hardhat

Hardhat is a powerful Ethereum dApp tutorial development framework that provides a comprehensive tooling and infrastructure ecosystem for NFT minter dApp and Ethereum smart contracts. It offers a range of features and functionalities that enhance the development experience and streamline the workflow.

Install Hardhat

Install Hardhat by using the following command:
npm install --save-dev hardhat

Create a new HardHat Project

To create the sample project, create a folder named “NFT-Project” and run npx hardhat init in your project folder.

npx hardhat init

Create a new file

Now, create a new file in the contracts folder named “NFT721.sol” and paste the code below into it. 

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract NFT721 is ERC721, ERC721Burnable, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdCounter;
    mapping(uint => string) public tokenUri;
    constructor() ERC721("NFT721", "NFT") {}
    function safeMint(string memory _uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(msg.sender, tokenId);
        _setURI(tokenId, _uri);
    }

    function _setURI(uint256 _tokenId, string memory newuri) private {
        tokenUri[_tokenId] = newuri;
    }

    function uri(uint256 _tokenId) public view returns (string memory) {
        string memory currentBaseURI = tokenUri[_tokenId];
        return string(abi.encodePacked(currentBaseURI));
    }
}

Compile the contract

npx hardhat compile 

After successfully compiling the contract, deploy it to the Sepolia testnet.

Deploy Contract

Deploying the contract on the Sepolia testnet requires the Infura key that can be accessed on the Infura NFT minting dApp website by login and the Sepolia private key that you can get on your MetaMask wallet. Make sure never to put real Ether into testing accounts.
Add the network and replace the infura and private keys in the hardhat.config.js file. Use the below code.

require("@nomicfoundation/hardhat-toolbox");

// Go to https://infura.io, sign up, create a new API key
// in its dashboard, and replace "KEY" with it
const INFURA_API_KEY = "KEY";

// Replace this private key with your Sepolia account private key
// To export your private key from Coinbase Wallet, go to
// Settings > Developer Settings > Show private key
// To export your private key from Metamask, open Metamask and
// go to Account Details > Export Private Key
// Beware: NEVER put real Ether into testing accounts
const SEPOLIA_PRIVATE_KEY = "YOUR SEPOLIA PRIVATE KEY";

module.exports = {
  solidity: "0.8.19",
  networks: {
    sepolia: {
      url: `https://sepolia.infura.io/v3/${INFURA_API_KEY}`,
      accounts: [SEPOLIA_PRIVATE_KEY]
    }
  }
};

Now run the command below to deploy:

npx hardhat run scripts/deploy.js --network sepolia

We successfully deployed the contract that generated the ABI (Application Binary Interface) that will be used in the React JS with web3 integration to interact with the contract. And here is the deployed Contract Address:

0x2330E22304853cDB96893B4c801D7F5e37904Dd4

Create an Express JS (Backend - IPFS) Project 

Now, we have to create the backend server that handles the IPFS metadata generation and uploading files to the IPFS. We will use the express js backend framework and the web.storage library, which is simple file storage with IPFS. Let’s start to build a DApp backend.

To create a node js project, make a folder named “backend” and run the following commands: init.

npm init -y

Open the backend in vs. code and run the following command in the project’s root to install the dependencies.

npm install express web3.storage express-fileupload dotenv

Create a file with the name “server.js” in the root of the project and paste the following code into it.

Now, create a web.storage account to get the API Key. Then, create a new .env file and add the API Key, as in the example below.

WEB3_STORAGE_API_KEY=REPLACE_KEY_HERE

Test the backend by running the following command on the terminal of the root directory:
node server.js

We have successfully built the backend that will be used for IPFS image uploading, metadata generation, and metadata file uploading to IPFS.

Create React JS Project

Next, we must create the React JS Web3 DApp and do the web3 integration and backend API integration. Let's create a DApp with the following command.

npx create-react-app fullstack-nftdapp

It will create the React app with a JavaScript template.

Open the full-stack dApp NFT in VS Code IDE and run the following command in the project’s root to install these dependencies.

npm install web3 axios

Paste the below code into the App.js file, We have integrated the web3 and MetaMask integration along with backend API for metadata generation and file uploading to IPFS.

import { useEffect, useState } from "react";
import "./App.css";
import Web3 from "web3";
import axios from "axios";
import ABI from "./ABI.json";

const NFTContractAddress = "0x2330E22304853cDB96893B4c801D7F5e37904Dd4";

function App() {
  const [web3, setWeb3] = useState();
  const [account, setAccount] = useState();
  const [contract, setContract] = useState(null);

  const [formData, setFormData] = useState({
    name: "",
    description: "",
    image: null,
  });

  const connectToMetaMask = async () => {
    const provider = window.ethereum;
    if (!provider) {
      alert("Please install MetaMask");
      return;
    }

    const accounts = await provider.request({ method: "eth_requestAccounts" });
    const web3 = new Web3(provider);

    const contractInstance = new web3.eth.Contract(ABI.abi, NFTContractAddress);

    setContract(contractInstance);
    setWeb3(web3);
    setAccount(accounts[0]);
  };

  useEffect(() => {
    connectToMetaMask();
  });

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  const handleImageChange = (e) => {
    setFormData({ ...formData, image: e.target.files[0] });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const { name, description, image } = formData;
      const formDataUpload = new FormData();
      formDataUpload.append("name", name);
      formDataUpload.append("description", description);
      formDataUpload.append("image", image);

      const response = await axios.post(
        "http://localhost:3001/upload/web3storage",
        formDataUpload,
        {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        }
      );

      console.log("Response from server:", response.data);
      console.log("URL:", response.data.ipfsUrl.toString());

      const result = await contract.methods
        .safeMint(response.data.ipfsUrl.toString())
        .send({ from: account, gas: 2000000 });
      console.log("Minted NFT:", result);

      // You can display a success message or redirect the user after successful upload
    } catch (error) {
      console.error("Error uploading image:", error);
    }
  };

  return (
    <div className="App">
      <header className="App-header">
        <div>
          <h1>Full Stack NFT dApp</h1>
          <form onSubmit={handleSubmit}>
            <div>
              <label>Name:</label>
              <input
                type="text"
                name="name"
                value={formData.name}
                onChange={handleInputChange}
              />
            </div>
            <div>
              <label>Description:</label>
              <input
                type="text"
                name="description"
                value={formData.description}
                onChange={handleInputChange}
              />
            </div>
            <div>
              <label>Image:</label>
              <input
                type="file"
                name="image"
                accept="image/*"
                onChange={handleImageChange}
              />
            </div>
            <button type="submit">Mint NFT</button>
          </form>
        </div>
      </header>
    </div>
  );
}

export default App;

Conclusion

We have gone through a comprehensive guide to building a dApp from scratch. First, We wrote the NFT721 contract and compiled and deployed it using the Hardhat on the Spolia testnet. We created the backend or IPFS image uploading, metadata generation, and metadata file uploading to IPFS using the web3.storage library. 

After we made the React JS App, we integrated the web3, metamask wallet, and backend APs . This is the complete flow to build the Full Stack NFT dApp. This tutorial will enable the developer to build NFT mint dApp on the EVM blockchains.

FAQS

  1. You can create NFT minter dApp in four steps starting with:
  2. Setting up your project by creating empty files and npm initializing. 
  3. User Hardhat framework to deploy and test smart contracts.
  4. Create a front-end application to interact with deployed smart contracts.
  5. Run the application locally.

Before deploying a contract on NFT, make sure it's written smartly. After that, deploy your contract on ERC-721 and ERC-1155. Once deployed, lazy mint your NFT on Rarible; you can mint your NFT using a third web for NFT minting dApp template.

To configure and integrate IPFS, you need to install IPFS and publish on it. You can also download IPFS CLI based on your system through the installation guide in IPFS official docs. For that, you need to create a project directory and change the directory in your

Ready to make something amazing?

ready to make banner

Ready to make something amazing?

mobile banner

Related Posts