Next.js Integration Guide - QueueSaaS Blog
Relay
QueueSaaS Team

Next.js Integration Guide

Learn how to integrate QueueSaaS with Next.js App Router. Handle webhooks securely, verify signatures, and build reliable serverless applications.

nextjs webhooks serverless tutorial

Next.js Integration Guide

QueueSaaS provides first-class support for Next.js App Router with built-in webhook signature verification, type-safe handlers, and seamless integration patterns. This guide will show you how to integrate QueueSaaS into your Next.js applications.

Installation

npm install @anlyonhq/scheduler
# or
pnpm add @anlyonhq/scheduler

Environment Setup

Add your QueueSaaS credentials to your .env.local:

ANLYON_API_KEY=live_your_api_key_here
ANLYON_SIGNING_SECRET=whsec_your_signing_secret_here

Sending Messages from Next.js

Server Actions

// app/actions/send-message.ts
'use server';

import { Client } from '@anlyonhq/scheduler';

const client = new Client({
  apiKey: process.env.ANLYON_API_KEY!,
});

export async function sendWelcomeEmail(userId: string) {
  await client.messages.publish({
    url: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/welcome-email`,
    method: 'POST',
    body: { userId, type: 'welcome_email' },
  });
}

API Routes

// app/api/send-notification/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Client } from '@anlyonhq/scheduler';

const client = new Client({
  apiKey: process.env.ANLYON_API_KEY!,
});

export async function POST(request: NextRequest) {
  const { userId, message } = await request.json();

  await client.messages.publish({
    url: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/notify`,
    method: 'POST',
    body: { userId, message },
  });

  return NextResponse.json({ success: true });
}

Receiving Webhooks

The serve() function handles signature verification automatically:

// app/api/webhooks/order-processed/route.ts
import { serve } from '@anlyonhq/scheduler/nextjs';

export const { POST } = serve(async (request) => {
  const data = await request.json();

  // Signature is already verified
  await processOrder(data.orderId);

  return { success: true };
});

Using verifySignature() Middleware

For more control:

// app/api/webhooks/payment/route.ts
import { verifySignature } from '@anlyonhq/scheduler/nextjs';

export const POST = verifySignature(async (request) => {
  const data = await request.json();

  // Handle payment webhook
  await handlePayment(data);

  return Response.json({ ok: true });
});

Custom Configuration

import { serve } from '@anlyonhq/scheduler/nextjs';

export const { POST } = serve(
  async (request) => {
    const data = await request.json();
    return { processed: true };
  },
  {
    signingSecret: process.env.CUSTOM_SECRET, // Override default
    clockTolerance: 600, // 10 minutes (default: 300)
    onError: (error) =>
      Response.json({ error: error.message }, { status: 400 }),
  }
);

Real-World Examples

Example 1: User Registration Flow

// app/api/auth/register/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Client } from '@anlyonhq/scheduler';

const client = new Client({
  apiKey: process.env.ANLYON_API_KEY!,
});

export async function POST(request: NextRequest) {
  const { email, name } = await request.json();

  // Create user in database
  const user = await createUser({ email, name });

  // Send welcome email asynchronously
  await client.messages.publish({
    url: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/send-email`,
    method: 'POST',
    body: {
      type: 'welcome',
      userId: user.id,
      email: user.email,
      name: user.name,
    },
  });

  return NextResponse.json({ user });
}

// app/api/webhooks/send-email/route.ts
import { serve } from '@anlyonhq/scheduler/nextjs';
import { sendEmail } from '@/lib/email';

export const { POST } = serve(async (request) => {
  const { type, userId, email, name } = await request.json();

  if (type === 'welcome') {
    await sendEmail({
      to: email,
      subject: `Welcome, ${name}!`,
      template: 'welcome',
      data: { name },
    });
  }

  return { success: true };
});

Example 2: Scheduled Tasks

// app/api/admin/setup-daily-report/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Client } from '@anlyonhq/scheduler';

const client = new Client({
  apiKey: process.env.ANLYON_API_KEY!,
});

export async function POST(request: NextRequest) {
  // Create a daily report schedule
  await client.schedules.create({
    name: 'Daily Report',
    cronExpression: '0 9 * * *', // 9 AM every day
    timezone: 'America/New_York',
    url: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/daily-report`,
    method: 'POST',
    body: { type: 'daily_report' },
  });

  return NextResponse.json({ success: true });
}

// app/api/webhooks/daily-report/route.ts
import { serve } from '@anlyonhq/scheduler/nextjs';
import { generateDailyReport } from '@/lib/reports';

export const { POST } = serve(async (request) => {
  const report = await generateDailyReport();
  
  // Send report to stakeholders
  await sendReport(report);

  return { success: true };
});

Example 3: Image Processing

// app/api/upload/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Client } from '@anlyonhq/scheduler';

const client = new Client({
  apiKey: process.env.ANLYON_API_KEY!,
});

export async function POST(request: NextRequest) {
  const formData = await request.formData();
  const file = formData.get('file') as File;

  // Save file temporarily
  const fileUrl = await saveFile(file);

  // Process image asynchronously
  await client.messages.publish({
    url: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/process-image`,
    method: 'POST',
    body: {
      fileUrl,
      userId: request.headers.get('user-id'),
    },
  });

  return NextResponse.json({ fileUrl });
}

// app/api/webhooks/process-image/route.ts
import { serve } from '@anlyonhq/scheduler/nextjs';
import { processImage } from '@/lib/image-processing';

export const { POST } = serve(async (request) => {
  const { fileUrl, userId } = await request.json();

  // Process image (resize, optimize, etc.)
  const processedUrl = await processImage(fileUrl);

  // Update user's profile with processed image
  await updateUserProfile(userId, { imageUrl: processedUrl });

  return { success: true };
});

Error Handling

import { serve } from '@anlyonhq/scheduler/nextjs';
import { AnlyonError } from '@anlyonhq/scheduler';

export const { POST } = serve(
  async (request) => {
    try {
      const data = await request.json();
      await processWebhook(data);
      return { success: true };
    } catch (error) {
      if (error instanceof AnlyonError) {
        console.error('QueueSaaS error:', error.message);
        return Response.json(
          { error: error.message },
          { status: error.statusCode || 500 }
        );
      }
      throw error;
    }
  },
  {
    onError: (error) => {
      console.error('Webhook error:', error);
      return Response.json(
        { error: 'Internal server error' },
        { status: 500 }
      );
    },
  }
);

Type Safety

The SDK is fully typed for better developer experience:

import { serve } from '@anlyonhq/scheduler/nextjs';

interface WebhookPayload {
  userId: string;
  event: 'user.created' | 'order.completed';
  data: Record<string, unknown>;
}

export const { POST } = serve(async (request) => {
  const payload = await request.json() as WebhookPayload;

  // TypeScript knows the shape of payload
  if (payload.event === 'user.created') {
    await handleUserCreated(payload.userId, payload.data);
  } else if (payload.event === 'order.completed') {
    await handleOrderCompleted(payload.userId, payload.data);
  }

  return { success: true };
});

Best Practices

  1. Always verify signatures: Use serve() or verifySignature() for security
  2. Handle errors gracefully: Return appropriate status codes
  3. Keep handlers idempotent: Webhooks may be delivered multiple times
  4. Use environment variables: Never hardcode API keys
  5. Log webhook events: Helpful for debugging
  6. Return quickly: Webhooks should respond within 30 seconds

Testing Webhooks Locally

Use tools like ngrok to test webhooks locally:

# Start your Next.js dev server
pnpm dev

# In another terminal, expose it via ngrok
ngrok http 3000

# Use the ngrok URL in your QueueSaaS webhook configuration

Next Steps

Build reliable Next.js applications with QueueSaaS! ⚡