diff --git a/packages/proxy/README.md b/packages/proxy/README.md index ab4e39c..dcc295e 100644 --- a/packages/proxy/README.md +++ b/packages/proxy/README.md @@ -18,7 +18,7 @@ We offer built in integrations for the following libraries: - [Express](./src/express/README.md) - [Next.js](./src/nextjs/README.md) -You can also create your own custom integration. See docs here. +You can also [create your own custom adapter](#custom-adapters). ## Supported Endpoints @@ -33,4 +33,38 @@ The proxy supports all model endpoints, apart from the realtime models. 4. Proxy forwards the request to `https://api.decart.ai` 5. Proxy returns the response to the client +## Custom Adapters +You can create a proxy adapter for any HTTP framework by implementing the `ProxyBehavior` interface with the framework specific implementation, and passing it to `handleRequest()`. + +### Example: Fastify Adapter + +```typescript +import { + handleRequest, + type DecartProxyOptions, + type ProxyBehavior, +} from "@decartai/proxy"; +import type { FastifyRequest, FastifyReply } from "fastify"; + +export function createDecartProxy(options?: DecartProxyOptions) { + return async (request: FastifyRequest, reply: FastifyReply) => { + const behavior: ProxyBehavior = { + integration: "fastify", + baseUrl: options?.baseUrl, + method: request.method, + getHeaders: () => request.headers as Record, + getHeader: (name) => request.headers[name], + getRequestBody: async () => JSON.stringify(request.body), + sendHeader: (name, value) => reply.header(name, value), + respondWith: (status, data) => reply.status(status).send(data), + getRequestPath: () => request.url, + sendResponse: async (response) => { + reply.status(response.status).send(response.body); + }, + }; + + await handleRequest(behavior); + }; +} +``` diff --git a/packages/proxy/src/core/proxy-handler.ts b/packages/proxy/src/core/proxy-handler.ts index 798fff9..96333d0 100644 --- a/packages/proxy/src/core/proxy-handler.ts +++ b/packages/proxy/src/core/proxy-handler.ts @@ -64,10 +64,10 @@ export async function handleRequest(behavior: ProxyBehavior { - id: string; +export interface ProxyBehavior { + /** + * Internal identifier for built-in adapters. Custom adapters should use + * `integration` instead to identify themselves. + */ + id?: string; + + /** + * HTTP method of the incoming request (GET, POST, etc.) + */ method: string; + + /** + * Optional API key for authenticating with the Decart API. + * If not provided, falls back to the DECART_API_KEY environment variable. + */ apiKey?: string; + + /** + * Optional base URL for the Decart API. + * Defaults to "https://api.decart.ai" + */ baseUrl?: string; + + /** + * Optional integration identifier included in the User-Agent header. + */ integration?: string; + + /** + * Send an error response to the client. + * Called when the proxy encounters an error (e.g., missing API key). + * + * @param status - HTTP status code + * @param data - Error message or data to send + * @returns The framework's response type + */ // biome-ignore lint/suspicious/noExplicitAny: data can be any type respondWith(status: number, data: string | any): ResponseType; + + /** + * Forward the successful proxy response to the client. + * Handle the response body as appropriate for your framework. + * + * @param response - The fetch Response from the Decart API + * @returns Promise resolving to the framework's response type + */ sendResponse(response: Response): Promise; + + /** + * Get all request headers as a record. + * Header names should be lowercase for consistent matching. + * + * @returns Record of header names to values + */ getHeaders(): Record; + + /** + * Get a specific request header value. + * + * @param name - Header name + * @returns The header value, or undefined if not present + */ getHeader(name: string): HeaderValue; + + /** + * Set a response header to forward to the client. + * + * @param name - Header name + * @param value - Header value + */ sendHeader(name: string, value: string): void; + + /** + * Get the request body. + * Return as ArrayBuffer to preserve binary data (e.g., for FormData/multipart). + * + * @returns Promise resolving to the request body, or undefined if no body + */ getRequestBody(): Promise; + + /** + * Get the request path to proxy to the Decart API. + * Should return the path after your proxy route (e.g., "/v1/generate/lucy-pro-t2i"). + * + * @returns The request path + */ getRequestPath(): string; } diff --git a/packages/proxy/src/index.ts b/packages/proxy/src/index.ts index 6491434..be41d54 100644 --- a/packages/proxy/src/index.ts +++ b/packages/proxy/src/index.ts @@ -1,2 +1,2 @@ -export { handleRequest } from "./core/proxy-handler"; -export type { DecartProxyOptions } from "./core/types"; +export { DEFAULT_PROXY_ROUTE, fromHeaders, handleRequest } from "./core/proxy-handler"; +export type { DecartProxyOptions, HeaderValue, ProxyBehavior } from "./core/types";