Codapult
FeaturesPricingAPIHelpChangelog
Codapult

Ship Your SaaS Faster

Product

  • Features
  • Pricing
  • Plugins
  • API Reference
  • Help Center
  • Feature Requests
  • Changelog

Company

  • Contact
  • GitHub

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Codapult. All rights reserved.

All articles

Getting Started

  • Introduction
  • Quick Start
  • Project Structure

Configuration

  • Environment Variables
  • App Configuration

Authentication

  • Authentication

Database

  • Database

Teams

  • Teams & Organizations

Payments

  • Payments & Billing

Api

  • API Layer

Ai

  • AI Features

Email

  • Email

Infrastructure

  • Infrastructure

Ui

  • UI & Theming

I18n

  • Internationalization

Content Management

  • Content Management

Admin

  • Admin Panel

Security

  • Security

Monitoring

  • Analytics & Monitoring

Modules

  • Module Architecture

Plugins

  • Plugin System

Deployment

  • Deployment
  • Troubleshooting

Upgrading

  • Upgrading Codapult

Developer Tools

  • MCP Server
  • Testing
Infrastructure

Infrastructure

Configure file storage, background jobs, and real-time notifications using the adapter pattern.

Codapult ships three infrastructure modules — file storage, background jobs, and notifications — each using the adapter pattern. Switch implementations via a single environment variable; no code changes required.

File Storage

Upload and serve files through a unified API. The storage adapter is selected by STORAGE_PROVIDER.

ProviderEnv ValueBest For
Local filesystemlocal (default)Development
Amazon S3s3Production (AWS)
Cloudflare R2r2Production (Cloudflare)

Upload API

POST /api/upload accepts multipart form data. Authentication is required.

const form = new FormData();
form.append('file', file);

const res = await fetch('/api/upload', {
  method: 'POST',
  body: form,
});

const { url } = await res.json();

Image uploads are automatically optimized (resized, compressed) before storage.

Switching to S3 or R2

  1. Install the AWS SDK (required only for S3/R2):
pnpm add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
  1. Set the provider and credentials in .env.local:
STORAGE_PROVIDER="s3"   # or "r2"

S3_BUCKET="my-bucket"
S3_REGION="us-east-1"   # use "auto" for R2
S3_ENDPOINT=""           # required for R2 (e.g. https://<account>.r2.cloudflarestorage.com)
S3_ACCESS_KEY_ID="..."
S3_SECRET_ACCESS_KEY="..."
S3_PUBLIC_URL=""         # optional — public URL prefix for uploaded files

Production: Always use S3 or R2. The local adapter stores files on disk and is not suitable for multi-instance deployments.

Programmatic Usage

import { uploadFile, deleteFile } from '@/lib/storage';

const url = await uploadFile('avatars/user-123.webp', buffer, 'image/webp');
await deleteFile('avatars/user-123.webp');

Background Jobs

Offload work to background jobs for email sending, webhook delivery, credit resets, and RAG indexing. The job adapter is selected by JOB_PROVIDER.

ProviderEnv ValueBest For
In-memory queuememory (default)Development, single-instance
BullMQ (Redis)bullmqProduction, multi-instance

Enqueuing Jobs

import { enqueue, enqueueEmail } from '@/lib/jobs';

// Generic job
await enqueue('webhook-retry', { webhookId: 'wh_123', attempt: 1 });

// Shorthand for emails
await enqueueEmail('[email protected]', 'Welcome!', emailHtml);

Built-in Jobs

Job NameDescription
send-emailSends transactional email via Resend
webhook-retryRetries failed webhook deliveries with exponential backoff
credit-resetResets monthly AI/usage credits for all organizations
rag-indexIndexes documents for AI RAG pipeline

Built-in Cron Jobs

ScheduleTask
DailySession cleanup (remove expired sessions)
MonthlyCredit reset (reset usage quotas)

Switching to BullMQ

Set the provider and Redis connection in .env.local:

JOB_PROVIDER="bullmq"
REDIS_URL="redis://localhost:6379"

For production Kubernetes or Docker deployments, run the worker as a separate process alongside your Next.js server. The Helm chart includes a dedicated worker Deployment.


Notifications

In-app notifications with real-time delivery. The transport is selected by NOTIFICATION_TRANSPORT.

TransportEnv ValueDescription
Pollingpoll (default)Periodic HTTP requests — works everywhere, zero config
Server-Sent EventssseOne-way real-time stream from server to client
WebSocketwsFull-duplex real-time — requires a separate WS server

Dashboard Integration

The NotificationBell component in the dashboard header shows unread count and a dropdown list. It works with all three transports automatically.

Creating Notifications

import { createNotification } from '@/lib/notifications';

await createNotification({
  userId: 'user_abc',
  type: 'info', // 'info' | 'success' | 'warning' | 'error'
  title: 'Deployment complete',
  message: 'Your app was deployed to production.',
  link: '/dashboard/deployments/42',
});

Operations

FunctionDescription
createNotification()Create and deliver a notification
getUserNotifications()List notifications (newest first)
getUnreadCount()Count unread notifications
markAsRead()Mark a single notification as read
markAllAsRead()Mark all notifications as read

WebSocket Configuration

When using the ws transport, configure the WebSocket server URL and port:

NOTIFICATION_TRANSPORT="ws"
NEXT_PUBLIC_WS_URL="ws://localhost:3001"
WS_PORT="3001"

Choosing a Transport

  • Polling — simplest option, no infrastructure dependencies. Good for low-traffic apps.
  • SSE — real-time without extra servers. One-way (server → client). Good default for production.
  • WebSocket — full-duplex, lowest latency. Requires running a separate WS server process.
EmailUI & Theming