Rashmit Mhatre
Rashmit Mhatre

Reputation: 1

Why does my Next.js URL shortener application fail to redirect after deployment on Vercel?

I am building a Next.js application that serves as a URL shortener. The application works perfectly in my local development environment. I can shorten a URL, copy the shortened link, and paste it into a new tab, where it redirects to the correct webpage as expected.

However, after deploying the application to Vercel, the shortened URLs no longer work. While I can still generate and copy the shortened links, pasting them into a new tab results in an error stating, "This site cannot be reached."

What could be causing this issue, and how can I fix it to ensure the shortened URLs redirect properly on the deployed version?

What I Tried:

  1. Tested Locally:

    • Ran the application on my local machine, where everything worked perfectly. I was able to generate shortened URLs, copy them, and paste them into a new tab. They redirected to the correct destination as expected.
  2. Deployed on Vercel:

    • Deployed the application to Vercel. The deployment was successful, and the application loads correctly. I can generate shortened URLs and copy them without any issues.
  3. Testing on Vercel:

    • Pasted the shortened URL into a new tab, expecting it to redirect to the original URL. Instead, I encountered an error message saying, "This site cannot be reached."

What I Was Expecting:

After deploying to Vercel, I expected the same behavior as on my local machine:

Instead, the redirection fails, and the URLs are inaccessible in the browser after deployment.

import prisma from "@/lib/db";
import { redirect } from "next/navigation";

// Define the type for params as a Promise
interface RedirectPageProps {
  params: Promise<{ shortcode: string }>;
}

export default async function RedirectPage({ params }: RedirectPageProps) {
  // Await params as it's a promise
  const { shortcode } = await params; // Destructure after awaiting the promise
  let newUrl: string | undefined;

  try {
    // Log shortcode for debugging
    console.log("ShortCode:", shortcode);

    // Fetch the URL from the database
    const url = await prisma.url.findUnique({
      where: { ShortCode: shortcode },
    });

    // Log URL details for debugging
    console.log("Fetched URL:", url);

    if (!url) {
      // Render a 404 page if the shortcode does not exist
      return <h1>404 - URL Not Found</h1>;
    }

    // Increment the visit count
    await prisma.url.update({
      where: { id: url.id },
      data: { visits: { increment: 1 } },
    });

    newUrl = url.OrignalUrl; // Set newUrl only if no errors occurred
  } catch (error) {
    console.error("Error during redirect:", error);
    return <h1>Something went wrong</h1>;
  }

  // Only perform the redirect if newUrl is set
  if (newUrl) {
    console.log("Redirecting to:", newUrl);
    return redirect(newUrl); // Redirect after try-catch block
  }

  // Default return in case newUrl wasn't set
  return <h1>Something went wrong</h1>;
}

.env

DATABASE_URL="neon.tech-database-url" NEXT_PUBLIC_BASE_URL = "http://localhost:3000"

"use client";

import { CheckIcon, CopyIcon, EyeIcon } from "lucide-react";
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { Button } from "./ui/button";
import { Skeleton } from "./ui/skeleton";

type Url = {
  id: string;
  OrignalUrl: string;
  ShortCode: string;
  visits: number;
};

export default function UrlListComponent() {
  const [urls, setUrls] = useState<Url[]>([]);
  const [copiedUrl, setCopiedUrl] = useState<string | null>(null); // Updated to track the copied URL specifically
  const [loading, setLoading] = useState<boolean>(false);

  // Function to create a shortened URL
  const shortenedUrl = (code: string) => {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "https://localhost:3000"; // Added fallback for safety
    return `${baseUrl}/${code}`;
  };

  // Fetch URLs from API
  const fetchUrls = async () => {
    setLoading(true);
    try {
      const response = await fetch("/api/urls/");
      if (!response.ok) throw new Error("Failed to fetch URLs");
      const data = await response.json();
      console.log("URL Data:", data);
      setUrls(data);
    } catch (error) {
      console.error("Error fetching URLs:", error);
    } finally {
      setLoading(false);
    }
  };

  // Fetch URLs on component mount
  useEffect(() => {
    fetchUrls();
  }, []);

  // Handle copying the URL
  const handleCopyUrl = (code: string) => {
    const fullUrl = shortenedUrl(code);
    navigator.clipboard.writeText(fullUrl).then(() => {
      setCopiedUrl(code); // Track which URL was copied
      setTimeout(() => {
        setCopiedUrl(null); // Reset copied state after 1 second
      }, 1000);
    });
  };

  return (
    <div>
      <h2 className="text-2xl font-bold mb-2">Recent URLs</h2>
      
      {/* Render skeletons if loading */}
      {loading ? (
        <ul className="space-y-2">
          {[...Array(5)].map((_, i) => (
            <li key={i} className="flex items-center gap-2 justify-between border p-2 rounded">
              <Skeleton className="w-[200px] h-[20px] rounded-full" />
              <div className="flex items-center gap-4">
                <Skeleton className="w-[40px] h-[20px] rounded-full" />
                <Skeleton className="w-[50px] h-[20px] rounded-full" />
              </div>
            </li>
          ))}
        </ul>
      ) : (
        <ul className="space-y-2">
          {urls.map((url) => (
            <li
              key={url.id}
              className="flex items-center gap-2 justify-between border p-2 rounded"
            >
              {/* Display the shortened URL */}
              <Link
                href={`/${url.ShortCode}`}
                className="text-blue-500 hover:underline"
                target="_blank"
              >
                {shortenedUrl(url.ShortCode)}
              </Link>
              <div className="flex items-center gap-4">
                {/* Copy Button */}
                <Button
                  variant="ghost"
                  size="icon"
                  className="text-muted-foreground hover:bg-muted"
                  onClick={() => handleCopyUrl(url.ShortCode)}
                >
                  {copiedUrl === url.ShortCode ? (
                    <CheckIcon className="w-4 h-4" />
                  ) : (
                    <CopyIcon className="w-4 h-4" />
                  )}
                  <span className="sr-only">
                    {copiedUrl === url.ShortCode
                      ? "Copied to clipboard"
                      : "Copy this URL"}
                  </span>
                </Button>
                {/* Display visit count */}
                <span className="flex items-center gap-2">
                  <EyeIcon className="h-4 w-4" />
                  {url.visits}
                </span>
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Upvotes: 0

Views: 34

Answers (0)

Related Questions