@equinor/fusion-framework-vite-plugin-api-service
Version:
Vite plugin for proxying service discovery and mocking
111 lines (99 loc) • 4.21 kB
text/typescript
import { responseInterceptor } from 'http-proxy-middleware';
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { IncomingRequest, JsonData, PluginLogger, ProxyListener } from './types.js';
type ResponseInterceptorCallback<TResponse, TResult> = (
data: TResponse,
) => TResult | Promise<TResult>;
/**
* Intercepts the API response and transforms it using the provided callback function.
*
* This function is designed to work with `http-proxy-middleware`'s `responseInterceptor`.
* It allows you to modify the response data before it is sent to the client by applying
* a transformation callback.
*
* **Note:** This function assumes that the response from the API is valid JSON. If the
* response is not JSON, attempting to parse it will throw an error, causing the returned
* promise to reject, which may result in the proxy sending an error response to the client.
*
* @template TResponse - The type of the original response data, which must be JSON-serializable.
* @template TResult - The type of the transformed response data, which must also be JSON-serializable. Defaults to `TResponse`.
*
* @param callback - A function that takes the original response data of type `TResponse`
* and returns the transformed data of type `TResult`.
*
* @returns A function that intercepts the response, applies the callback transformation,
* and sends the transformed response to the client. This function is intended to be used
* as the `onProxyRes` handler in `http-proxy-middleware`.
*
* @param proxyRes - The original response from the proxy server.
* @param req - The original incoming request from the client.
* @param res - The server response to be sent to the client.
*
* @example
* ```typescript
* import { apiResponseInterceptor } from './api-response-interceptor';
*
* type OriginalResponseType = { foo: string };
* type TransformedResponseType = { bar: string };
*
* const transformResponse = (data: OriginalResponseType): TransformedResponseType => {
* return { bar: data.foo };
* };
*
* const interceptor = apiResponseInterceptor(transformResponse);
*
* // Use the interceptor in your server setup
* app.use('/api', createProxyMiddleware({
* target: 'http://example.com',
* changeOrigin: true,
* selfHandleResponse: true,
* onProxyRes: interceptor,
* }));
* ```
*/
export function createResponseInterceptor<
TResponse extends JsonData,
TResult extends JsonData = TResponse,
>(
callback: ResponseInterceptorCallback<TResponse, TResult>,
options?: { logger?: PluginLogger },
): ProxyListener {
const { logger } = options ?? {};
// Callback function for standard proxy handler
return async (
proxyRes: IncomingMessage,
req: IncomingRequest,
res: ServerResponse,
): Promise<void> => {
// @ts-ignore
logger?.debug(`intercepted response from ${req.originalUrl}`);
const { headers, statusCode = 500 } = proxyRes;
// Check if the response status code indicates an error
// If the status code is 2xx, we can proceed with the transformation
// If the status code is 4xx or 5xx, we should skip the transformation
// and return the original response to the client
if (statusCode >= 400) {
proxyRes.pipe(res);
return;
}
// Check if the response content type is JSON
// If the content type is not JSON, we should skip the transformation
// and return the original response to the client
if (!headers['content-type']?.includes('application/json')) {
logger?.debug('response is not JSON, skipping transformation');
proxyRes.pipe(res);
return;
}
// Apply the response interceptor
const interceptor = responseInterceptor(async (responseBuffer): Promise<string> => {
// Parse the response data and apply the callback transformation
const response = JSON.parse(responseBuffer.toString()) as TResponse;
// Apply the callback transformation
const result = await Promise.resolve(callback(response));
// Convert the transformed data to a JSON string
return JSON.stringify(result);
});
// Return the transformed response
await interceptor(proxyRes, req, res);
};
}