WDD 430: Web Full-Stack Development

Handling Errors

Overview

Next.js provides several mechanisms for handling errors in your application, including error boundaries and error pages. Handling errors gracefully is essential for providing a good user experience. The focus of this learning activity will be on handling errors using JavaScript's try...catch statements and Next.js APIs for uncaught exceptions.

Prepare

Adding try...catch to Server Actions

When working with Server Actions, it's important to handle potential errors that may occur during data mutations. You can use try...catch blocks to catch and handle errors gracefully. For example:

export async function deleteProject(id: string) {
  try { 
    await sql`DELETE FROM projects WHERE id = ${id}`;
    revalidatePath('/projects');
  } catch (error) {
    console.error('Error deleting project:', error);
    throw new Error('Failed to delete project. Please try again later.');
  }
  revalidatePath('/projects');
  redirect('/projects');
}

In this example, if the database operation fails, the error is caught and logged, and a new error is thrown with a user-friendly message. This allows you to handle the error on the client side and provide feedback to the user.

Handling all errors with error.tsx

Next.js allows you to create a special error.tsx file in your app directory to handle uncaught exceptions. This file will be rendered whenever an error is thrown that is not caught by a try...catch block. You can customize this page to display a user-friendly error message and provide options for the user to navigate back to safety.

// /projects/error.tsx
'use client';

import Link from 'next/link';
import { useEffect } from 'react';

export default function Error({ 
  error,
  reset
}: { 
  error: Error & { digest: string };
  reset: () => void 
}) {
  useEffect(() => {
    // optionally log the error
    console.error('An uncaught error occurred:', error);
  }, [error]);

  return (
    <div className="mx-auto mt-16 max-w-xl rounded-lg border border-slate-200 bg-white p-6 text-center shadow-sm">
      <h1 className="text-2xl font-bold text-slate-900">Something went wrong!</h1>
      <p className="mt-3 text-slate-600">An unexpected error occurred. Please try again later.</p>
      <div className="mt-6 flex flex-col items-center justify-center gap-3 sm:flex-row">
        <button
          onClick={reset}
          className="rounded-md bg-blue-600 px-4 py-2 font-semibold text-white transition hover:bg-blue-700"
        >
          Try Again
        </button>
        <Link
          href="/projects"
          className="rounded-md border border-slate-300 px-4 py-2 font-semibold text-slate-700 transition hover:bg-slate-50"
        >
          Go Back to Projects
        </Link>
      </div>
    </div>
  );
}

Note the following:

notFound function

Next.js also provides a notFound function that you can call to render a 404 page when a resource is not found. This is useful for handling cases where a user tries to fetch a resource that does not exist.

import { notFound } from 'next/navigation';

export default async function Page({ params }: { params: { id: string } }) {
  const project = await getProjectById(params.id);
  if (!project) {
    notFound();
  } 
  return <div>...</div>;
}

In this example, if the project with the specified ID is not found, the notFound function is called, which will render the 404 page.

To use the notFound function, make sure you have a not-found.tsx file in your directory. The not-found.tsx can look like this:

// /projects/[id]/edit/not-found.tsx
import Link from 'next/link';

export default function NotFound() {
  return (
    <div className="mx-auto mt-16 max-w-xl rounded-lg border
      border-slate-200 bg-white p-6 text-center shadow-sm">
      <h1 className="text-2xl font-bold text-slate-900">Project Not Found</h1>
      <p className="mt-3 text-slate-600">The project you are looking for does not exist.</p>
      <div className="mt-6 flex flex-col items-center justify-center gap-3 sm:flex-row">
        <Link
          href="/projects"
          className="rounded-md border border-slate-300 px-4 py-2 font-semibold text-slate-700 transition hover:bg-slate-50"
        >
          Back to Projects
        </Link>
      </div>
    </div>
  );
}

In this example, if a user tries to access a project that does not exist, they will see a friendly message and a link to navigate back to the projects page.

Note that the notFound function will take precedence over error.tsx, which is useful when you want to display a specific 404 page for missing resources.