Skip to main content

Intrig Middleware

The Intrig Middleware is a specialized Next.js middleware system that enables server-side request preprocessing, header injection, and authentication handling for Intrig-powered applications. It runs at the edge before requests reach API routes or server components, providing centralized control over request/response processing.

Overview

Intrig Middleware serves as the preprocessing layer for all Intrig functionality, managing:

  • Dynamic header injection with secure prefix handling
  • Authentication token processing and forwarding
  • Request preprocessing before reaching server functions
  • Server-side configuration and environment variable access
  • Axios instance caching and management for server functions
  • State hydration coordination between server and client

Core Functions

createIntrigMiddleware

The primary function for creating Intrig-compatible Next.js middleware.

Type Signature:

function createIntrigMiddleware(
headersFunction: HeadersFunction
): (request: NextRequest) => Promise<NextResponse>

type HeadersFunction = (request: NextRequest) => Promise<Record<string, string>>

Parameters:

ParameterTypeDescription
headersFunctionHeadersFunctionAsync function that receives the Next.js request and returns headers to inject

Returns: A Next.js middleware function that can be exported as middleware

getHeaders

Server-side utility to retrieve Intrig-injected headers within API routes and server components.

Type Signature:

async function getHeaders(): Promise<Record<string, string>>

Returns: Object containing all headers that were injected by the middleware (without intrig- prefix)

getAxiosInstance

Creates and caches Axios instances for server-side API calls with environment-based configuration.

Type Signature:

async function getAxiosInstance(key: string): Promise<AxiosInstance>

Parameters:

ParameterTypeDescription
keystringAPI source identifier that maps to environment variable {KEY}_API_URL

Returns: Configured Axios instance for the specified API source

Basic Usage

Simple Authentication Middleware

// middleware.ts
import { createIntrigMiddleware } from '@intrig/next';

export const middleware = createIntrigMiddleware(async (request) => {
return {
'Authorization': `Bearer ${process.env.API_TOKEN}`,
'Content-Type': 'application/json',
};
});

export const config = {
matcher: '/api/:path*',
};

Server-Side Integration

Using Headers in API Routes

// app/api/users/route.ts
import { getHeaders } from '@intrig/next';

export async function GET() {
const headers = await getHeaders();

// Headers injected by middleware are now available
const authToken = headers['Authorization'];
const userId = headers['X-User-ID'];

// Use in your API logic
const response = await fetch('https://api.example.com/users', {
headers: {
'Authorization': authToken,
'X-User-ID': userId,
},
});

return Response.json(await response.json());
}

Advanced Configuration

Environment Variable Management

// .env.local
API_TOKEN=your-secret-token
USERS_API_URL=https://users-api.example.com
PRODUCTS_API_URL=https://products-api.example.com
ANALYTICS_API_URL=https://analytics-api.example.com

# Development overrides
DEV_API_URL=http://localhost:8080
// middleware.ts
export const middleware = createIntrigMiddleware(async (request) => {
const isDev = process.env.NODE_ENV === 'development';
const baseToken = process.env.API_TOKEN;

return {
'Authorization': `Bearer ${baseToken}`,
'X-Environment': isDev ? 'development' : 'production',
'X-Debug': isDev ? 'true' : 'false',
};
});

Header Processing

Automatic Prefix Management

Intrig middleware automatically manages header prefixing to avoid conflicts:

// In middleware.ts - you return clean headers
return {
'Authorization': 'Bearer token123',
'X-User-ID': 'user456',
};

// Internally stored as:
// 'intrig-Authorization': 'Bearer token123'
// 'intrig-X-User-ID': 'user456'

// Retrieved via getHeaders() as:
{
'Authorization': 'Bearer token123',
'X-User-ID': 'user456'
}

Error Handling

Graceful Fallbacks

// middleware.ts
export const middleware = createIntrigMiddleware(async (request) => {
try {
const token = await getAuthToken(request);
return {
'Authorization': `Bearer ${token}`,
'X-Status': 'authenticated',
};
} catch (error) {
console.error('Auth middleware error:', error);

// Continue with limited headers instead of failing
return {
'X-Status': 'anonymous',
'X-Error': 'auth-failed',
};
}
});

Performance Considerations

// Axios instances are automatically cached
const userApi = await getAxiosInstance('users'); // Creates new instance
const userApi2 = await getAxiosInstance('users'); // Returns cached instance

// middleware.ts - Minimize overhead with specific matchers
export const config = {
matcher: '/api/:path*', // Only run on API routes
};

Integration Patterns

With IntrigLayout

// middleware.ts
export const middleware = createIntrigMiddleware(async (request) => {
return {
'Authorization': `Bearer ${process.env.API_TOKEN}`,
'X-Request-ID': crypto.randomUUID(),
};
});

// app/layout.tsx - Headers are automatically available
import { IntrigLayout } from '@intrig/next';

export default function RootLayout({ children }) {
return (
<IntrigLayout
configs={{
baseURL: process.env.NEXT_PUBLIC_API_URL,
timeout: 5000,
}}
>
{children}
</IntrigLayout>
);
}

Best Practices

1. Environment Security

// Never expose sensitive data to client-side
return {
'Authorization': `Bearer ${process.env.API_SECRET}`, // ✅ Server-only
'X-Public-Key': process.env.NEXT_PUBLIC_API_KEY, // ✅ Public env var
};

2. Matcher Configuration

// Be specific with matchers to reduce overhead
export const config = {
matcher: [
'/api/:path*', // API routes
'/server-actions/:path*', // Server actions
],
};

3. Header Naming

// Use consistent header naming conventions
return {
'Authorization': `Bearer ${token}`, // Standard header
'X-User-ID': userId, // Custom headers with X- prefix
'X-Request-ID': requestId, // Unique identifiers
'X-Environment': environment, // Environment indicators
};

4. Error Boundaries

// Always handle errors gracefully
export const middleware = createIntrigMiddleware(async (request) => {
try {
return await getProductionHeaders(request);
} catch (error) {
// Log but don't throw - allow request to continue
console.error('Middleware error:', error);
return {}; // Return empty headers instead of failing
}
});