Blockchain Masterclass for JavaScript Developers
  • 👋Welcome
  • 🐣Hello World
  • 📰Decentralized News Service Contract
  • 🖥️Building A Frontend
  • 📖Glossary
Powered by GitBook
On this page
  • 🚀 Initiate Liftoff
  • Read From The Blockchain
  • Environment Variables
  • Displaying The Results
  • Add Some Style
  • Congratulations!

Building A Frontend

PreviousDecentralized News Service ContractNextGlossary

Last updated 1 year ago

We've got the contract setup and use to pull in the latest news article. We can see the titles, links, and dates published in Remix. That's awesome but not super user-friendly. Let's add a simplistic front end to present this data.

🚀 Initiate Liftoff

All of the code for this project can be found on

We'll be using to build out the frontend. Let's create a skeleton project.

npm create astro@latest

Read From The Blockchain

To read our data from the smart contract we deployed, we'll need to create 2 files. To do that, we must install a few NPM packages first. If your server is running, stop it with Ctrl-C, and run

npm install ethers dotenv node-fetch jsdom

to add them to our project. Now, we can move to creating our files.

Under the compiler tab, look for the copy ABI button at the bottom.

Once you've copied the code, head to your code editor, create a new file, distributed-news/src/lib/newsArchiveABI.json, and paste the ABI.

Next, we'll need to create the script to pull the data off the blockchain. Create a new file, distributed-news/src/lib/newsArchive.js and add the following code.

// Import necessary modules from ethers library
import { ethers, JsonRpcProvider } from "ethers";
// Import the dotenv module to load environment variables
import { config } from "dotenv";

// Import the ABI of the NewsArchive contract
import NewsArchiveABI from "./newsArchiveABI.json";

// Load environment variables from .env file
config();

// Get provider URL and contract address from environment variables
const providerUrl = process.env.PROVIDER_URL;
const contractAddress = process.env.CONTRACT_ADDRESS;

// Create a new JSON-RPC provider
const provider = new JsonRpcProvider(providerUrl);

// Create a new contract instance with the NewsArchive ABI
const newsArchiveContract = new ethers.Contract(
  contractAddress,
  NewsArchiveABI,
  provider
);

// Define an async function to get all articles from the contract
async function getAllArticles() {
  try {
    // Call the getAllArticles function of the contract
    const articles = await newsArchiveContract.getAllArticles();
    // Return the articles
    return articles;
  } catch (error) {
    // Log the error and return an empty array
    console.error("Error fetching articles:", error);
    return [];
  }
}

// Export the getAllArticles function as the default export
export default getAllArticles;

Environment Variables

You may have noticed that we are using dotenv to load values for PROVIDER_URL and CONTRACT_ADDRESS, you'll need to create distributed-news/.env to house those values. Be sure to replace the value for CONTRACT_ADDRESS with the contract address you deployed.

# RPC for sepolia
PROVIDER_URL="https://rpc.sepolia.org"
CONTRACT_ADDRESS="YOUR_CONTRACT_ADDRESS"

Displaying The Results

One last step! We are almost there. We need to update distributed-news/src/pages/index.astro to display the articles we've saved.

First, let's update the frontmatter.

---
import fetch from "node-fetch";
import { JSDOM } from "jsdom";
// Import the getAllArticles function from the newsArchive.js file
import getAllArticles from "../lib/newsArchive.js";

// Initialize articles variable and error flag
let articles;
let errorOccurred = false;

try {
  // Try to fetch all articles
  articles = await getAllArticles();
} catch (error) {
  // If an error occurs, log it and set the error flag
  console.error("Failed to get articles:", error);
  articles = [];
  errorOccurred = true;
}
async function fetchOGData(url) {
  try {
    const response = await fetch(url);
    const html = await response.text();
    const dom = new JSDOM(html);
    const doc = dom.window.document;
    let ogTitle =
      doc.querySelector('meta[property="og:title"]')?.content || url;
    const ogImage = doc.querySelector('meta[property="og:image"]')?.content;
    return { ogTitle, ogImage, url };
  } catch (error) {
    console.error("Error fetching OG data:", error);
    return { ogTitle: "No title", ogImage: "No image", url };
  }
}

async function articlesWithOGData(articles) {
  return await Promise.all(articles.map(fetchOGData));
}
let allArticles = await articlesWithOGData(articles);
---

Once that's updated we can use the variable articles to display them in the browser. We'll setup a conditional rendering block to check for any errors and if there are none, display the articles.

<html>
  <body>
    <h1>News Articles</h1>
    {
      // If an error occurred, display an error message
      // Otherwise, display the list of articles
      errorOccurred ? (
        <p>Sorry, we're unable to fetch articles at the moment.</p>
      ) : (
        <ul>
          {allArticles.map((article) => (
            <li>
              <article>
                <a href={article.url}>
                  <h2>{article?.ogTitle}</h2>
                </a>
                <img src={article?.ogImage} alt="" />
              </article>
            </li>
          ))}
        </ul>
      )
    }
  </body>
</html>

What it lacks in style, it makes up for in functionality.

Add Some Style

You might be cringing at the lack of style, lets fix that and make things look just a little bit nicer.

Below the </html> tag add in the following.

<style>
  body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
  }
  
  h1 {
    text-align: center;
  }
  
  ul {
    list-style-type: none;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    grid-gap: 20px;
  }
  
  article {
    background-color: #f4f4f4;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    transition: transform 0.3s ease;
  }
  
  article:hover {
    transform: translateY(-5px);
  }
  
  article a {
    text-decoration: none;
    color: #333;
  }
  
  article h2 {
    font-size: 18px;
    margin-top: 0;
  }
  
  article img {
    width: 100%;
    height: auto;
    border-radius: 4px;
    margin-top: 10px;
  }
</style>

Great, now, you've got some style!

Congratulations!

Awesome! You've successfully deployed a smart contract using Chainlink Functions and connected it to a front end!

Head to cd ./distributed-news and run npm run dev. You should now be able to access Astro at , if you visit that site, Astro should greet you. The basics are in place.

First, we'll get the ABI (Application Binary Interface) from Remix. This will allow ethers.js to know how to interact with our contract. To find the ABI, head back to .

With these two changes you should see the results at Your front end should look something like this.

🖥️
http://localhost:4321
Remix
Chainlink Functions
GitHub
Astro
http://localhost:4321