Reputation: 1
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?
Tested Locally:
Deployed on Vercel:
Testing on Vercel:
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