Custom Processing with Middleware
Learn how to use middleware to process HTTP requests and responses using the Instructor HTTP client API.
While the Instructor HTTP client API provides several built-in middleware components, you’ll often need to create custom middleware to handle specific requirements for your application. This chapter explores how to create custom middleware components and use response decoration for advanced processing.
Creating Custom Middleware
There are three main approaches to creating custom middleware:
- Implementing the
HttpMiddleware
interface directly - Extending the
BaseMiddleware
abstract class - Using anonymous classes for simple middleware
Approach 1: Implementing HttpMiddleware Interface
The most direct approach is to implement the HttpMiddleware
interface:
This approach gives you complete control over the middleware behavior, but it requires you to implement the entire logic from scratch.
Approach 2: Extending BaseMiddleware
For most cases, extending the BaseMiddleware
abstract class is more convenient:
With BaseMiddleware
, you only need to override the methods that matter for your middleware:
beforeRequest(HttpClientRequest $request): void
- Called before the request is sentafterRequest(HttpClientRequest $request, HttpClientResponse $response): HttpClientResponse
- Called after the response is receivedshouldDecorateResponse(HttpClientRequest $request, HttpClientResponse $response): bool
- Determines if the response should be decoratedtoResponse(HttpClientRequest $request, HttpClientResponse $response): HttpClientResponse
- Creates a decorated response
Approach 3: Using Anonymous Classes
For simple middleware that you only need to use once, you can use anonymous classes:
This approach is concise but less reusable than defining a named class.
Practical Middleware Examples
Retry Middleware
This middleware automatically retries failed requests:
Rate Limiting Middleware
This middleware throttles requests to respect API rate limits:
Caching Middleware
This middleware caches responses for GET requests:
Response Decoration
Response decoration is a powerful technique for wrapping HTTP responses to add functionality or transform data. It’s particularly useful for streaming responses, where you need to process each chunk as it arrives.
Creating a Response Decorator
All response decorators should implement the HttpClientResponse
interface. The library provides a BaseResponseDecorator
class that makes this easier:
Using Response Decorators in Middleware
To use a response decorator, you need to create a middleware that wraps the response:
Then add the middleware to your client:
Response Decoration for Transforming Content
You can use response decoration to transform response content on-the-fly:
And the corresponding middleware:
Advanced Middleware Examples
Here are some more advanced middleware examples that demonstrate the power and flexibility of the middleware system.
Analytics Middleware
This middleware collects analytics data about HTTP requests:
Circuit Breaker Middleware
This middleware implements the circuit breaker pattern to prevent repeated calls to failing services:
Conditional Middleware
This middleware only applies to certain requests based on a condition:
Usage example:
Request ID Middleware
This middleware adds a unique ID to each request and tracks it through the response:
OpenTelemetry Tracing Middleware
This middleware adds OpenTelemetry tracing to HTTP requests:
Customizing Middleware for LLM APIs
When working with Large Language Model (LLM) APIs, you can create specialized middleware to handle their unique requirements:
Combining Multiple Middleware Components
When building complex applications, you’ll often need to combine multiple middleware components. Here’s an example of how to set up a complete HTTP client pipeline:
With this setup, requests and responses flow through the middleware in the following order:
- Request Flow (outside → inside):
- TracingMiddleware: Starts a trace
- LoggingMiddleware: Logs the outgoing request
- CircuitBreakerMiddleware: Checks if the service is available
- CachingMiddleware: Checks if response is cached
- AuthenticationMiddleware: Adds authentication headers
- RetryMiddleware: Prepares to retry on failure
- RateLimitingMiddleware: Enforces rate limits
- AnalyticsMiddleware: Starts timing
- HTTP Driver: Sends the actual request
- Response Flow (inside → outside):
- HTTP Driver: Receives the response
- AnalyticsMiddleware: Records API stats
- RateLimitingMiddleware: Updates rate limit counters
- RetryMiddleware: Handles retries if needed
- AuthenticationMiddleware: Verifies authentication status
- CachingMiddleware: Caches the response
- CircuitBreakerMiddleware: Updates circuit state
- LoggingMiddleware: Logs the response
- TracingMiddleware: Completes the trace
- Your Application: Processes the final response
This bidirectional flow allows for powerful request/response processing capabilities.
By creating custom middleware and response decorators, you can extend the HTTP client’s functionality to handle any specialized requirements your application might have.
In the next chapter, we’ll cover troubleshooting techniques for the Instructor HTTP client API.