Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Api Error Handling
Your Progress0%
0 of 70 completed

NextJS Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication

API Error Handling and Status Codes

Building robust, production-ready APIs

Errors happen. Networks fail, users send invalid data, resources don't exist, and databases go down. Proper error handling makes the difference between a fragile API and a production-ready one. Use the right HTTP status codes, provide helpful error messages, log errors for debugging, and handle edge cases gracefully. Build APIs that fail gracefully and help developers debug issues!

HTTP Status Codes

Status Code Categories

2xx - Success

  • 200 OK: Request succeeded (GET, PUT, PATCH)
  • 201 Created: Resource created successfully (POST)
  • 204 No Content: Success with no response body (DELETE)

4xx - Client Errors

  • 400 Bad Request: Invalid request (validation errors)
  • 401 Unauthorized: Authentication required
  • 403 Forbidden: Authenticated but insufficient permissions
  • 404 Not Found: Resource doesn't exist
  • 409 Conflict: Request conflicts with current state
  • 422 Unprocessable Entity: Validation error (alternative to 400)
  • 429 Too Many Requests: Rate limit exceeded

5xx - Server Errors

  • 500 Internal Server Error: Unexpected server error
  • 502 Bad Gateway: Invalid response from upstream server
  • 503 Service Unavailable: Server temporarily unavailable

Choosing the Right Status Code

TYPESCRIPT
// ✅ 200 OK - Successful GET, PUT, PATCH
export async function GET() {
  const data = await fetchData();
  return Response.json(data); // 200 by default
}

// ✅ 201 Created - Successful POST
export async function POST(request: Request) {
  const data = await request.json();
  const created = await db.create(data);
  return Response.json(created, { status: 201 });
}

// ✅ 204 No Content - Successful DELETE (no body)
export async function DELETE() {
  await db.delete();
  return new Response(null, { status: 204 });
}

// ✅ 400 Bad Request - Invalid input
if (!email || !password) {
  return Response.json(
    { error: 'Email and password required' },
    { status: 400 }
  );
}

// ✅ 401 Unauthorized - No authentication
if (!authToken) {
  return Response.json(
    { error: 'Authentication required' },
    { status: 401 }
  );
}

// ✅ 403 Forbidden - Authenticated but no permission
if (user.role !== 'admin') {
  return Response.json(
    { error: 'Admin access required' },
    { status: 403 }
  );
}

// ✅ 404 Not Found - Resource doesn't exist
if (!resource) {
  return Response.json(
    { error: 'Resource not found' },
    { status: 404 }
  );
}

// ✅ 409 Conflict - Duplicate resource
if (await emailExists(email)) {
  return Response.json(
    { error: 'Email already registered' },
    { status: 409 }
  );
}

// ✅ 500 Internal Server Error - Unexpected errors
catch (error) {
  console.error('Unexpected error:', error);
  return Response.json(
    { error: 'Internal server error' },
    { status: 500 }
  );
}

Error Response Patterns

Simple Error Response

app/api/posts/route.ts
export async function POST(request: Request) {
  const body = await request.json();
  
  // Simple error response
  if (!body.title) {
    return Response.json(
      { error: 'Title is required' },
      { status: 400 }
    );
  }
  
  const post = await db.posts.create({ data: body });
  return Response.json(post, { status: 201 });
}

// Response for error:
// {
//   "error": "Title is required"
// }

// ✅ Simple and clear
// ✅ Single error message
// ✅ Use for simple validation

Detailed Error Response

app/api/posts/route.ts
export async function POST(request: Request) {
  const body = await request.json();
  
  // Detailed error response
  const errors: string[] = [];
  
  if (!body.title) {
    errors.push('Title is required');
  }
  if (body.title && body.title.length < 3) {
    errors.push('Title must be at least 3 characters');
  }
  if (!body.content) {
    errors.push('Content is required');
  }
  
  if (errors.length > 0) {
    return Response.json(
      {
        error: 'Validation failed',
        message: 'Please correct the following errors',
        errors,
      },
      { status: 400 }
    );
  }
  
  const post = await db.posts.create({ data: body });
  return Response.json(post, { status: 201 });
}

// Response for errors:
// {
//   "error": "Validation failed",
//   "message": "Please correct the following errors",
//   "errors": [
//     "Title is required",
//     "Content is required"
//   ]
// }

// ✅ Multiple errors
// ✅ Clear error message
// ✅ List of specific issues

Field-Specific Errors

app/api/users/route.ts
import { z } from 'zod';

const createUserSchema = z.object({
  email: z.string().email('Invalid email format'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
  age: z.number().min(18, 'Must be at least 18 years old'),
});

export async function POST(request: Request) {
  const body = await request.json();
  const result = createUserSchema.safeParse(body);
  
  if (!result.success) {
    return Response.json(
      {
        error: 'Validation failed',
        fields: result.error.flatten().fieldErrors,
      },
      { status: 400 }
    );
  }
  
  const user = await db.users.create({ data: result.data });
  return Response.json(user, { status: 201 });
}

// Response for validation errors:
// {
//   "error": "Validation failed",
//   "fields": {
//     "email": ["Invalid email format"],
//     "password": ["Password must be at least 8 characters"],
//     "age": ["Must be at least 18 years old"]
//   }
// }

// ✅ Field-specific errors
// ✅ Easy for forms to display
// ✅ Zod integration

Error with Code and Metadata

app/api/auth/login/route.ts
export async function POST(request: Request) {
  const body = await request.json();
  
  const user = await db.users.findUnique({
    where: { email: body.email },
  });
  
  if (!user) {
    return Response.json(
      {
        error: {
          code: 'INVALID_CREDENTIALS',
          message: 'Invalid email or password',
          timestamp: new Date().toISOString(),
          path: '/api/auth/login',
        },
      },
      { status: 401 }
    );
  }
  
  const validPassword = await verifyPassword(
    body.password,
    user.hashedPassword
  );
  
  if (!validPassword) {
    return Response.json(
      {
        error: {
          code: 'INVALID_CREDENTIALS',
          message: 'Invalid email or password',
          timestamp: new Date().toISOString(),
          path: '/api/auth/login',
        },
      },
      { status: 401 }
    );
  }
  
  const token = await createToken(user.id);
  return Response.json({ token });
}

// Response for error:
// {
//   "error": {
//     "code": "INVALID_CREDENTIALS",
//     "message": "Invalid email or password",
//     "timestamp": "2024-01-15T10:30:00.000Z",
//     "path": "/api/auth/login"
//   }
// }

// ✅ Error code for programmatic handling
// ✅ Timestamp for logging
// ✅ Path for context
// ✅ Consistent structure

Try-Catch Error Handling

Basic Try-Catch

app/api/posts/route.ts
export async function GET(request: Request) {
  try {
    const posts = await db.posts.findMany();
    return Response.json(posts);
  } catch (error) {
    console.error('Failed to fetch posts:', error);
    
    return Response.json(
      { error: 'Failed to fetch posts' },
      { status: 500 }
    );
  }
}

// ✅ Catch unexpected errors
// ✅ Log error details
// ✅ Return generic message
// ✅ Don't expose internal details

Specific Error Handling

app/api/posts/[id]/route.ts
export async function DELETE(
  request: Request,
  { params }: { params: { id: string } }
) {
  try {
    await db.posts.delete({
      where: { id: params.id },
    });
    
    return new Response(null, { status: 204 });
  } catch (error) {
    // Check for specific Prisma errors
    if (error.code === 'P2025') {
      // Record not found
      return Response.json(
        { error: 'Post not found' },
        { status: 404 }
      );
    }
    
    if (error.code === 'P2003') {
      // Foreign key constraint
      return Response.json(
        { error: 'Cannot delete post with existing comments' },
        { status: 409 }
      );
    }
    
    // Generic error
    console.error('Delete error:', error);
    return Response.json(
      { error: 'Failed to delete post' },
      { status: 500 }
    );
  }
}

// ✅ Handle specific database errors
// ✅ Appropriate status codes
// ✅ Helpful error messages
// ✅ Fallback to generic error

Error Handler Utility

app/lib/errors.ts
// Custom error classes
export class ValidationError extends Error {
  constructor(
    message: string,
    public fields?: Record<string, string[]>
  ) {
    super(message);
    this.name = 'ValidationError';
  }
}

export class NotFoundError extends Error {
  constructor(message: string = 'Resource not found') {
    super(message);
    this.name = 'NotFoundError';
  }
}

export class UnauthorizedError extends Error {
  constructor(message: string = 'Unauthorized') {
    super(message);
    this.name = 'UnauthorizedError';
  }
}

export class ForbiddenError extends Error {
  constructor(message: string = 'Forbidden') {
    super(message);
    this.name = 'ForbiddenError';
  }
}

// Error handler
export function handleError(error: unknown): Response {
  // Log error
  console.error('API Error:', error);
  
  // Validation error
  if (error instanceof ValidationError) {
    return Response.json(
      {
        error: error.message,
        fields: error.fields,
      },
      { status: 400 }
    );
  }
  
  // Not found error
  if (error instanceof NotFoundError) {
    return Response.json(
      { error: error.message },
      { status: 404 }
    );
  }
  
  // Unauthorized error
  if (error instanceof UnauthorizedError) {
    return Response.json(
      { error: error.message },
      { status: 401 }
    );
  }
  
  // Forbidden error
  if (error instanceof ForbiddenError) {
    return Response.json(
      { error: error.message },
      { status: 403 }
    );
  }
  
  // Generic error
  return Response.json(
    { error: 'Internal server error' },
    { status: 500 }
  );
}

// ✅ Custom error classes
// ✅ Centralized error handling
// ✅ Consistent responses
// ✅ Type-safe

Using Error Handler

app/api/posts/[id]/route.ts
import { handleError, NotFoundError, ValidationError } from '@/app/lib/errors';

export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  try {
    const post = await db.posts.findUnique({
      where: { id: params.id },
    });
    
    if (!post) {
      throw new NotFoundError('Post not found');
    }
    
    return Response.json(post);
  } catch (error) {
    return handleError(error);
  }
}

export async function PUT(
  request: Request,
  { params }: { params: { id: string } }
) {
  try {
    const body = await request.json();
    
    // Validate
    if (!body.title || body.title.length < 3) {
      throw new ValidationError('Validation failed', {
        title: ['Title must be at least 3 characters'],
      });
    }
    
    // Update
    const post = await db.posts.update({
      where: { id: params.id },
      data: body,
    });
    
    return Response.json(post);
  } catch (error) {
    return handleError(error);
  }
}

// ✅ Throw custom errors
// ✅ Single error handler
// ✅ Consistent error responses
// ✅ Clean code

Validation Error Patterns

Zod Validation with Detailed Errors

app/api/posts/route.ts
import { z } from 'zod';

const createPostSchema = z.object({
  title: z
    .string()
    .min(3, 'Title must be at least 3 characters')
    .max(200, 'Title must not exceed 200 characters'),
  content: z
    .string()
    .min(10, 'Content must be at least 10 characters'),
  published: z.boolean().default(false),
  tags: z
    .array(z.string())
    .min(1, 'At least one tag required')
    .max(5, 'Maximum 5 tags allowed')
    .optional(),
});

export async function POST(request: Request) {
  try {
    const body = await request.json();
    const result = createPostSchema.safeParse(body);
    
    if (!result.success) {
      const errors = result.error.flatten();
      
      return Response.json(
        {
          error: 'Validation failed',
          message: 'Please correct the following errors',
          fields: errors.fieldErrors,
          // Optional: include form errors
          formErrors: errors.formErrors,
        },
        { status: 400 }
      );
    }
    
    const post = await db.posts.create({
      data: result.data,
    });
    
    return Response.json(post, { status: 201 });
  } catch (error) {
    console.error('Create post error:', error);
    return Response.json(
      { error: 'Failed to create post' },
      { status: 500 }
    );
  }
}

// POST /api/posts
// Body: { "title": "Hi", "content": "Short" }
// Response:
// {
//   "error": "Validation failed",
//   "message": "Please correct the following errors",
//   "fields": {
//     "title": ["Title must be at least 3 characters"],
//     "content": ["Content must be at least 10 characters"]
//   }
// }

// ✅ Zod for validation
// ✅ Field-specific errors
// ✅ Custom error messages
// ✅ Type-safe validation

Async Validation (Database Checks)

app/api/users/route.ts
import { z } from 'zod';

const createUserSchema = z.object({
  email: z.string().email('Invalid email format'),
  username: z.string().min(3, 'Username must be at least 3 characters'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
});

export async function POST(request: Request) {
  try {
    const body = await request.json();
    
    // Schema validation
    const result = createUserSchema.safeParse(body);
    if (!result.success) {
      return Response.json(
        {
          error: 'Validation failed',
          fields: result.error.flatten().fieldErrors,
        },
        { status: 400 }
      );
    }
    
    const data = result.data;
    
    // Async validation - check uniqueness
    const existingEmail = await db.users.findUnique({
      where: { email: data.email },
    });
    
    if (existingEmail) {
      return Response.json(
        {
          error: 'Validation failed',
          fields: {
            email: ['Email already registered'],
          },
        },
        { status: 409 }
      );
    }
    
    const existingUsername = await db.users.findUnique({
      where: { username: data.username },
    });
    
    if (existingUsername) {
      return Response.json(
        {
          error: 'Validation failed',
          fields: {
            username: ['Username already taken'],
          },
        },
        { status: 409 }
      );
    }
    
    // Create user
    const user = await db.users.create({
      data: {
        ...data,
        hashedPassword: await hashPassword(data.password),
      },
    });
    
    return Response.json(
      { id: user.id, email: user.email },
      { status: 201 }
    );
  } catch (error) {
    console.error('Create user error:', error);
    return Response.json(
      { error: 'Failed to create user' },
      { status: 500 }
    );
  }
}

// ✅ Schema validation first
// ✅ Then async database checks
// ✅ 409 Conflict for duplicates
// ✅ Consistent error format

Error Handling Structure

Organization of error handling and utilities

appImportant

Select a file or folder to see details

Logging and Monitoring

Structured Logging

app/api/posts/route.ts
export async function POST(request: Request) {
  const startTime = Date.now();
  
  try {
    const body = await request.json();
    
    // Log request
    console.log('POST /api/posts', {
      timestamp: new Date().toISOString(),
      body,
    });
    
    const post = await db.posts.create({ data: body });
    
    // Log success
    const duration = Date.now() - startTime;
    console.log('POST /api/posts - Success', {
      duration: `${duration}ms`,
      postId: post.id,
    });
    
    return Response.json(post, { status: 201 });
  } catch (error) {
    // Log error with context
    const duration = Date.now() - startTime;
    console.error('POST /api/posts - Error', {
      duration: `${duration}ms`,
      error: error instanceof Error ? error.message : 'Unknown error',
      stack: error instanceof Error ? error.stack : undefined,
      timestamp: new Date().toISOString(),
    });
    
    return Response.json(
      { error: 'Failed to create post' },
      { status: 500 }
    );
  }
}

// ✅ Structured logs
// ✅ Include context
// ✅ Track duration
// ✅ Timestamp everything

Error Monitoring Service

app/lib/monitoring.ts
// Example with Sentry (or similar service)
import * as Sentry from '@sentry/nextjs';

export function logError(error: unknown, context?: Record<string, any>) {
  // Log to console
  console.error('Error:', error, context);
  
  // Send to monitoring service
  if (process.env.NODE_ENV === 'production') {
    Sentry.captureException(error, {
      extra: context,
    });
  }
}

export function logApiError(
  error: unknown,
  request: Request,
  endpoint: string
) {
  const context = {
    endpoint,
    method: request.method,
    url: request.url,
    timestamp: new Date().toISOString(),
  };
  
  logError(error, context);
}

// ✅ Centralized error logging
// ✅ Send to monitoring service
// ✅ Include request context
// ✅ Production-only

Using Monitoring in Routes

app/api/posts/route.ts
import { logApiError } from '@/app/lib/monitoring';

export async function POST(request: Request) {
  try {
    const body = await request.json();
    const post = await db.posts.create({ data: body });
    return Response.json(post, { status: 201 });
  } catch (error) {
    // Log to monitoring service
    logApiError(error, request, 'POST /api/posts');
    
    return Response.json(
      { error: 'Failed to create post' },
      { status: 500 }
    );
  }
}

// ✅ Track all errors
// ✅ Get notified of issues
// ✅ Debug production problems
// ✅ Monitor API health

Error Handling Best Practices

1. Always Use Try-Catch for Async Operations

TYPESCRIPT
// ✅ GOOD: Try-catch for database calls
export async function GET() {
  try {
    const data = await db.items.findMany();
    return Response.json(data);
  } catch (error) {
    console.error('Database error:', error);
    return Response.json(
      { error: 'Failed to fetch items' },
      { status: 500 }
    );
  }
}

// ❌ BAD: No error handling
export async function GET() {
  const data = await db.items.findMany(); // Crashes on error!
  return Response.json(data);
}

2. Never Expose Internal Errors

TYPESCRIPT
// ✅ GOOD: Generic error message
catch (error) {
  console.error('Internal error:', error); // Log details
  return Response.json(
    { error: 'Failed to process request' }, // Generic message
    { status: 500 }
  );
}

// ❌ BAD: Expose stack trace
catch (error) {
  return Response.json(
    { error: error.message, stack: error.stack }, // Reveals internals!
    { status: 500 }
  );
}

3. Return Helpful Error Messages

TYPESCRIPT
// ✅ GOOD: Actionable error messages
if (!email) {
  return Response.json(
    { error: 'Email is required. Please provide a valid email address.' },
    { status: 400 }
  );
}

// ❌ BAD: Vague error messages
if (!email) {
  return Response.json(
    { error: 'Invalid input' }, // What's invalid?
    { status: 400 }
  );
}

4. Use Consistent Error Format

TYPESCRIPT
// ✅ GOOD: Consistent structure
// All errors follow same format
{
  error: 'Error message',
  code: 'ERROR_CODE', // Optional
  fields: { ... },    // Optional for validation
}

// ❌ BAD: Inconsistent formats
// Sometimes { error: '...' }
// Sometimes { message: '...' }
// Sometimes { errors: [...] }
// Pick one format and stick to it!

5. Log Errors with Context

TYPESCRIPT
// ✅ GOOD: Log with context
catch (error) {
  console.error('API Error', {
    endpoint: '/api/posts',
    method: request.method,
    error: error.message,
    userId: currentUserId,
    timestamp: new Date().toISOString(),
  });
  
  return Response.json(
    { error: 'Failed to process request' },
    { status: 500 }
  );
}

// Logs help debug production issues

Key Takeaways

  • 400 for validation errors - client sent invalid data
  • 401 vs 403 - 401 = no auth, 403 = no permission
  • 404 for missing resources - resource doesn't exist
  • 500 for server errors - unexpected internal errors
  • Always use try-catch - handle async errors
  • Never expose internals - log details, return generic messages
  • Field-specific errors - help users fix issues
  • Consistent error format - same structure everywhere

🎉 API Routes Section Complete!

You've completed the API Routes and Route Handlers section! You've mastered:

  • ✅ Route Handlers Introduction
  • ✅ GET and POST Request Handlers
  • ✅ Dynamic API Routes
  • ✅ Request and Response Objects
  • ✅ API Error Handling and Status Codes

You now have complete mastery of building APIs in Next.js! You can create RESTful endpoints with proper HTTP methods, handle dynamic routes with parameters, work with headers and cookies, implement robust error handling with appropriate status codes, and build production-ready APIs that handle edge cases gracefully. These skills enable you to build complete full-stack applications with Next.js!

🔍 Production Checklist

  • ✅ All endpoints have try-catch error handling
  • ✅ Appropriate HTTP status codes for all responses
  • ✅ Input validation with Zod or similar
  • ✅ Authentication and authorization checks
  • ✅ Error logging with context
  • ✅ No internal details exposed in errors
  • ✅ Consistent error response format
  • ✅ Rate limiting for public endpoints

Final Quiz: Error Handling Mastery

Question 1 of 4

What status code should you return for validation errors?

Master API error handling in Next.js! Learn HTTP status codes and build robust, production-ready APIs.

Previous
Request and Response Objects
Next
Introduction to Middleware

Continue Your Next.js Journey

Join 2,000+ developers mastering Next.js. More advanced tutorials coming soon - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

NextJS Tutorials

0 of 70 completed

Your Progress0%

Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo