Error Handling and Logging

Introduction

Error handling and logging are essential for building robust and maintainable frontend applications. They help identify, debug, and address issues effectively. In this project, built with Next.js, TailwindCSS, and TypeScript, proper error handling and structured logging can ensure a smoother development process and a better user experience.


Error Handling

Types of Errors

  1. Client-Side Errors: Errors occurring in the browser, often due to invalid user input or code issues.
  2. Server-Side Errors: Errors originating from backend services, such as API failures or database issues.
  3. Network Errors: Errors caused by failed requests, slow connections, or timeouts.

Handling Errors in Next.js

Error Boundaries

Use React error boundaries to catch JavaScript errors in components.

Example:

import React from "react";

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, info: React.ErrorInfo) {
    console.error("Error caught by boundary:", error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

export default ErrorBoundary;

Wrap critical components with the ErrorBoundary component:

<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

Custom Error Pages

Create a custom _error.js file in the pages directory to handle errors globally.

Example:

import React from "react";

function CustomError({ statusCode }: { statusCode?: number }) {
  return (
    <p>
      {statusCode
        ? `An error ${statusCode} occurred on the server`
        : "An error occurred on the client"}
    </p>
  );
}

CustomError.getInitialProps = ({ res, err }: any) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
  return { statusCode };
};

export default CustomError;

Handling API Errors

Catch and handle errors when making API calls.

Example:

async function fetchData(url: string) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error("API call failed:", error);
    throw error;
  }
}

Logging

Why Logging is Important

Logging provides visibility into application behavior, helping developers identify and debug issues efficiently.


Tools for Logging

  1. Console Logging: Use console.log, console.warn, and console.error during development. Remove or minimize these in production.
  2. Logging Libraries: Use libraries like winston or pino for structured logging.

Logging in Next.js

Use next-logger for a simple logging setup in a Next.js project.

Installation

npm install next-logger

Basic Usage

import logger from "next-logger";

logger.info("This is an info message");
logger.error("This is an error message");

Structured Logging with debug

Install the debug library:

npm install debug

Use it in your project:

import debug from "debug";

const log = debug("app:component");
log("This is a debug message");

Enable debug logs in development:

DEBUG=app:* npm run dev

Monitoring Tools

Sentry

Sentry is a popular error tracking and monitoring tool.

Installation

npm install @sentry/nextjs

Configuration

Add the following to sentry.server.config.js and sentry.client.config.js:

import * as Sentry from "@sentry/nextjs";

Sentry.init({
  dsn: "your-dsn-url",
  tracesSampleRate: 1.0,
});

LogRocket

LogRocket captures logs, errors, and user sessions for advanced debugging.

Installation

npm install logrocket

Usage

import LogRocket from "logrocket";

LogRocket.init("your-app-id");

Best Practices for Error Handling and Logging

  1. Catch and handle errors gracefully without exposing sensitive information to users.
  2. Use structured logging to provide context about errors.
  3. Monitor logs and errors in real-time using tools like Sentry or LogRocket.
  4. Use error boundaries to prevent the app from crashing due to unexpected errors.
  5. Regularly review and improve logging practices as the application evolves.

With effective error handling and logging, you can build a more resilient and maintainable Next.js application. Always strive for meaningful logs and robust error management. Happy coding!