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.
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.