Home >Web Front-end >JS Tutorial >Building a WebRL shortener with Next.js on Fleek

Building a WebRL shortener with Next.js on Fleek

DDD
DDDOriginal
2024-12-18 21:19:09955browse

Building a WebRL shortener with Next.js on Fleek

Traditional URL shorteners rely on centralized services, making them vulnerable to censorship, data breaches, and single points of failure. A decentralized, Web3-driven URL shortener addresses these issues by storing link mappings on the blockchain, ensuring immutability, transparency, and censorship resistance.
In this guide, we’ll build a fully decentralized URL-shortening service using Next.js, Ethereum smart contracts, and Fleek’s edge-optimized hosting. By the end, you’ll have a streamlined Next.js app that enables users to shorten, store, and resolve URLs seamlessly.


Why a Web3 URL Shortener?

Key Benefits:

  • Decentralization: Store shortened URLs on a blockchain for resilience and immutability.
  • Censorship Resistance: No single authority can arbitrarily remove links.
  • Transparency: Users can verify that shortened URLs map to the correct destination.

Prerequisites

Ensure you have:

  1. Frontend Skills: Familiarity with React or Next.js.
  2. Node.js & npm: Installed on your system.
  3. Fleek Account & CLI: Sign up at Fleek and install the Fleek CLI.
  4. Reown Project: Create one at Reown.
  5. Test Crypto Wallet: Required for contract interactions.
  6. Web3 Basics: Understanding of smart contracts and blockchain fundamentals.

Step 1: Project Setup

  • Initialize a Next.js Project:
npx create-next-app@latest
  • Answer the prompts as follows:
Project name? web3-url-shortener
Use TypeScript? No
Use ESLint? No
Use Tailwind CSS? Yes
Use `src/` directory? Yes
Use App Router? No
Use Turbopack? No
Customize import alias? No
  • Install Dependencies:
npm install wagmi ethers @tanstack/react-query @rainbow-me/rainbowkit

# fleek-next adapter
npm install @fleek-platform/next
  • Ensure @fleek-platform/next is v2 or above.
  • Login to Fleek:
fleek login
  • Follow the on-screen instructions.
  • Create Directories: In src/, create directories lib and abi.
  • Run Development Server:
npm run dev

Smart Contract Setup

  • Contract source code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract UrlShortener {
    // Maps a short code (e.g. "abc123") to a full URL
    mapping(string => string) private shortToLong;

    event URLShortened(string indexed shortCode, string longUrl);

    /**
     * @notice Create a shortened URL by mapping a short code to a long URL.
     * @param shortCode The short code (unique identifier)
     * @param longUrl The long URL to map to
     */
    function setURL(string calldata shortCode, string calldata longUrl) external {
        require(bytes(shortCode).length > 0, "Short code cannot be empty");
        require(bytes(longUrl).length > 0, "Long URL cannot be empty");
        // In a production scenario, you'd probably want some uniqueness checks,
        // or handle collisions differently. For now we allow overwriting.

        shortToLong[shortCode] = longUrl;
        emit URLShortened(shortCode, longUrl);
    }

    /**
     * @notice Retrieve the long URL for a given short code.
     * @param shortCode The short code to look up
     * @return longUrl The long URL that the short code points to
     */
    function getURL(string calldata shortCode) external view returns (string memory) {
        return shortToLong[shortCode];
    }
}

The above UrlShortener smart contract allows users to create and manage shortened URLs. It maps unique short codes to long URLs, enabling efficient URL storage and retrieval. Users can set a mapping using the setURL function and retrieve the original URL with getURL. The contract includes basic validations and emits an event when a new URL is shortened. I deployed my contract already and the address is: 0x2729D62B3cde6fd2263dF5e3c6509F87C6C05892

  • Chain: Arbitrum Sepolia Testnet
  • ABI Source Code: URLShortener
  • RPC URL: Obtain from Alchemy or another provider.
  • Arbitrum Sepolia Faucet: Faucet

.env Setup:

Create a .env in the project root:

npx create-next-app@latest

Configuring the ABI and Contract

  • Add ABI:

Create src/abi/URLShortener.json with:

Project name? web3-url-shortener
Use TypeScript? No
Use ESLint? No
Use Tailwind CSS? Yes
Use `src/` directory? Yes
Use App Router? No
Use Turbopack? No
Customize import alias? No
  • Contract Config File:

In src/lib/contract.js:

npm install wagmi ethers @tanstack/react-query @rainbow-me/rainbowkit

# fleek-next adapter
npm install @fleek-platform/next
  • Wagmi Config:
fleek login

Replace {{REOWN-PROJECT-ID}} and {{REOWN-APP-NAME}} with your details from Reown.


Step 2: Building the Frontend

Providers Setup:

Below, I show how to set up web3 providers properly in a Next.js application to handle client-side rendering correctly.

The key is splitting the providers into two parts to safely handle web3 functionality that must run only in the browser.

Create src/lib/providers.js:

npm run dev

Create src/lib/Web3Providers.jsx:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract UrlShortener {
    // Maps a short code (e.g. "abc123") to a full URL
    mapping(string => string) private shortToLong;

    event URLShortened(string indexed shortCode, string longUrl);

    /**
     * @notice Create a shortened URL by mapping a short code to a long URL.
     * @param shortCode The short code (unique identifier)
     * @param longUrl The long URL to map to
     */
    function setURL(string calldata shortCode, string calldata longUrl) external {
        require(bytes(shortCode).length > 0, "Short code cannot be empty");
        require(bytes(longUrl).length > 0, "Long URL cannot be empty");
        // In a production scenario, you'd probably want some uniqueness checks,
        // or handle collisions differently. For now we allow overwriting.

        shortToLong[shortCode] = longUrl;
        emit URLShortened(shortCode, longUrl);
    }

    /**
     * @notice Retrieve the long URL for a given short code.
     * @param shortCode The short code to look up
     * @return longUrl The long URL that the short code points to
     */
    function getURL(string calldata shortCode) external view returns (string memory) {
        return shortToLong[shortCode];
    }
}

Modify _app.js:

In pages/_app.js:

NEXT_PUBLIC_CONTRACT_ADDRESS=0x2729D62B3cde6fd2263dF5e3c6509F87C6C05892
NEXT_PUBLIC_RPC_URL={{YOUR-ARBITRUM-SEPOLIA-RPC-URL}}

Main UI (pages/index.js):

This page handles connecting a wallet, entering a long URL and short code, and writing to the blockchain. We will do a similar split to what we did above. The key reasons for this split:

  • Web3 code needs window.ethereum which only exists in the browser
  • ssr: false prevents server-side rendering of web3 code
  • Main page component can still be server-rendered for better performance
  • Prevents "window is not defined" errors
  • Cleanly separates browser-only code from server-compatible code

In pages/index.js:

{
  "abi": [
    {
      "anonymous": false,
      "inputs": [
        { "indexed": true, "internalType": "string", "name": "shortCode", "type": "string" },
        { "indexed": false, "internalType": "string", "name": "longUrl", "type": "string" }
      ],
      "name": "URLShortened",
      "type": "event"
    },
    {
      "inputs": [{ "internalType": "string", "name": "shortCode", "type": "string" }],
      "name": "getURL",
      "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
      "stateMutability": "view",
      "type": "function"
    },
    {
      "inputs": [
        { "internalType": "string", "name": "shortCode", "type": "string" },
        { "internalType": "string", "name": "longUrl", "type": "string" }
      ],
      "name": "setURL",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }
  ]
}

Create src/lib/URLShortenerApp.jsx:

import { ethers } from "ethers";
import urlShortenerJson from "../abi/URLShortener.json";

export function getSignerContract(signer) {
  if (!signer) {
    console.error("No signer provided to getSignerContract");
    throw new Error("No signer available");
  }

  const address = process.env.NEXT_PUBLIC_CONTRACT_ADDRESS;
  if (!address) {
    throw new Error("Contract address not configured");
  }

  return new ethers.Contract(address, urlShortenerJson.abi, signer);
}

One final thing is to ensure that your tailwind.config.js matches the below:

import { http} from "wagmi";
import { arbitrumSepolia } from "wagmi/chains";
import { getDefaultConfig } from "@rainbow-me/rainbowkit";

const projectId = {{REOWN-PROJECT-ID}};

export const config = getDefaultConfig({
  appName: {{REOWN-APP-NAME}},
  projectId: projectId,
  chains: [arbitrumSepolia],
  transports: {
    [arbitrumSepolia.id]: http(),
  },
  ssr: false,
});

Step 3: Deploying to Fleek

  • Adjust Edge Runtime:

For server-side and dynamic routes, ensure you have: export const runtime = 'edge' within the files.

  • Build with Fleek:

1.Build the Application:

"use client";
import dynamic from "next/dynamic";
import { useEffect, useState } from "react";

const Web3Providers = dynamic(() => import("./Web3Providers"), {
  ssr: false,
});

export default function Providers({ children }) {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) {
    return <>{children}</>;
  }

  return <Web3Providers>{children}</Web3Providers>;
}

This generates a .fleek directory.

2.Create a Fleek Function:

// Web3Providers.jsx
"use client";

import { WagmiProvider } from "wagmi";
import "@rainbow-me/rainbowkit/styles.css";
import { RainbowKitProvider, darkTheme } from "@rainbow-me/rainbowkit";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { config } from "../lib/wagmi";

export default function Web3Providers({ children }) {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
        refetchOnWindowFocus: false,
      },
    },
  });

  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={queryClient}>
        <RainbowKitProvider
          theme={darkTheme({
            accentColor: "#0E76FD",
            accentColorForeground: "white",
            borderRadius: "large",
            fontStack: "system",
            overlayBlur: "small",
          })}
        >
          {children}
        </RainbowKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
}

3.Name your function (e.g., web3-url-shortener-next-js).

4.Deploy to Fleek:

import "../styles/globals.css";
import "@rainbow-me/rainbowkit/styles.css";

import Providers from "../lib/providers";

function App({ Component, pageProps }) {
  return (
    <Providers>
      <Component {...pageProps} />
    </Providers>
  );
}

export default App;

After a successful deployment, Fleek will provide a URL to access your application.


Conclusion

You’ve successfully built and deployed a decentralized URL shortener that:

  • Stores mappings on-chain.
  • Enables trustless, censorship-resistant link shortening.
  • Uses Fleek for edge deployment and a streamlined Web3 experience.

This foundation can be extended or integrated into larger Next.js Apps. Experiment with custom UI, track analytics, or integrate other smart contracts to enhance your Web3 URL shortener. View what the final result should look like here: https://shortener.on-fleek.app/

You can go to the Github repo to view the full code: https://github.com/tobySolutions/shortener

This was originally published on the Fleek blog

The above is the detailed content of Building a WebRL shortener with Next.js on Fleek. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn