A Next.js application that allows you to configure API overrides to intercept and modify API responses. When a request matches an override, it returns the override data; otherwise, it proxies the request to your main API.
- Web UI for configuring overrides and main API settings
- Proxy API endpoint that other applications can request
- Full request matching (method, path, headers, body)
- Override management - Create, read, update, and delete overrides
- Main API configuration with authentication headers and timeout settings
- Node.js 18+
- pnpm (or npm/yarn)
- PostgreSQL database (Vercel Postgres recommended for Vercel deployments)
git clone <your-repo-url>
cd api-overridespnpm installThe project uses Prisma with PostgreSQL. You can use:
- Vercel Postgres (recommended for Vercel deployments)
- Any PostgreSQL database (Supabase, Neon, Railway, etc.)
- Create a new project on Vercel
- Go to your project → Storage → Create Database → Postgres
- Copy the connection string
Create a PostgreSQL database with any provider and get the connection string.
Create a .env file in the root directory:
# PostgreSQL connection string
DATABASE_URL="postgresql://user:password@host:port/database?schema=public"
# Base APIs configuration (format: name1:url1,name2:url2)
BASE_APIS="production:https://api.example.com,staging:https://staging-api.example.com"# Generate Prisma client
pnpm db:generate
# Create and apply migrations
pnpm prisma migrate dev
# For production, apply migrations
pnpm db:migratepnpm devThe application will be available at http://localhost:3000
Base APIs are configured via the BASE_APIS environment variable. The format is:
BASE_APIS=name1:url1,name2:url2
Example:
BASE_APIS="production:https://api.example.com,staging:https://staging-api.example.com"Each API is specified as name:url where:
nameis a unique identifier/key for the APIurlis the base URL of the API
Multiple APIs are separated by commas. The first API in the list is used as the default.
-
View Base APIs:
- Navigate to the "Configuration" tab
- View your configured base APIs (read-only)
- Base APIs are configured via the
BASE_APISenvironment variable
-
Create Overrides:
- Go to the "Overrides" tab
- Click "+ New Override"
- Fill in the form:
- HTTP Method: GET, POST, PUT, PATCH, or DELETE
- Path: URL path to match (e.g.,
/api/users/123) - Headers (optional): JSON object of headers to match
- Request Body (optional): JSON body to match
- Response Status Code: HTTP status code to return
- Response Headers (optional): JSON object of response headers
- Response Body: The response data to return (JSON or text)
- Click "Create"
-
Edit/Delete Overrides:
- Click "Edit" on any override card to modify it
- Click "Delete" to remove an override
Once configured, you can make requests to the proxy endpoint using the base API key:
/api/proxy/[base-api-key]/your/path/here
Example:
# GET request (using base API key "production-api")
curl http://localhost:3000/api/proxy/production-api/api/users/123
# POST request with body (using base API key "staging-api")
curl -X POST http://localhost:3000/api/proxy/staging-api/api/users \
-H "Content-Type: application/json" \
-d '{"name": "John"}'The proxy will:
- Find the base API by the key in the URL path
- Check for a matching override (method, path, headers, body)
- If match found: return the override response
- If no match: proxy the request to the specified base API
Note: The base API key must match a configured base API. If the key doesn't exist, you'll receive a 404 error.
Check out the api-overrides-examples repository for complete example applications demonstrating how to use API Overrides:
- Flutter Coffee App - Mobile/web application built with Flutter
- Next.js Coffee App - Server-rendered web application built with Next.js 16
These examples show how to integrate API Overrides into both mobile and web applications.
API Overrides is perfect for mobile app development where you need to test different API responses without modifying your backend or app code.
Setup:
-
Configure your production API as a base API:
BASE_APIS="production:https://api.example.com" -
Deploy API Overrides to a public URL (e.g.,
https://api-overrides.vercel.app) -
Update your mobile app's API base URL to point to the proxy:
// iOS (Swift) let baseURL = "https://api-overrides.vercel.app/api/proxy/production"
// Android (Kotlin) val baseURL = "https://api-overrides.vercel.app/api/proxy/production"
Benefits:
- Test error scenarios (404, 500, etc.) without backend changes
- Simulate slow responses by creating overrides with delayed responses
- Test edge cases with custom response data
- Share override configurations with your team
- Switch between production and staging APIs easily
Example:
Create an override for /api/users/123 that returns a 404 error to test your app's error handling:
- Method: GET
- Path:
/api/users/123 - Response Status: 404
- Response Body:
{"error": "User not found"}
API Overrides works seamlessly with web applications, supporting both server-side requests and client-side requests from any SPA framework.
Use API Overrides in server-side code, such as Next.js Server Components, API Routes, Server Actions, or any server-side rendering framework:
// app/api/users/[id]/route.ts
const API_OVERRIDES_URL = "https://api-overrides.vercel.app";
const API_TOKEN = "your-api-token";
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const proxyUrl = `${API_OVERRIDES_URL}/api/proxy/production/api/users/${params.id}`;
const response = await fetch(proxyUrl, {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
},
});
return response;
}// app/users/[id]/page.tsx (Server Component)
const API_OVERRIDES_URL = "https://api-overrides.vercel.app";
const API_TOKEN = "your-api-token";
async function getUser(id: string) {
const proxyUrl = `${API_OVERRIDES_URL}/api/proxy/production/api/users/${id}`;
const res = await fetch(proxyUrl, {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
},
cache: "no-store", // or use revalidate for ISR
});
return res.json();
}
export default async function UserPage({ params }: { params: { id: string } }) {
const user = await getUser(params.id);
return <div>{user.name}</div>;
}// app/actions.ts (Server Actions)
"use server";
const API_OVERRIDES_URL = "https://api-overrides.vercel.app";
const API_TOKEN = "your-api-token";
export async function createUser(formData: FormData) {
const proxyUrl = `${API_OVERRIDES_URL}/api/proxy/production/api/users`;
const response = await fetch(proxyUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_TOKEN}`,
},
body: JSON.stringify({
name: formData.get("name"),
email: formData.get("email"),
}),
});
return response.json();
}Use API Overrides in client-side code from any SPA framework (React, Vue, Angular, Svelte, etc.) or Next.js Client Components:
Next.js Client Components:
// app/components/UserProfile.tsx (Client Component)
"use client";
import { useEffect, useState } from "react";
const API_OVERRIDES_URL = "https://api-overrides.vercel.app";
const API_TOKEN = "your-api-token";
export default function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
useEffect(() => {
const fetchUser = async () => {
const proxyUrl = `${API_OVERRIDES_URL}/api/proxy/production/api/users/${userId}`;
const response = await fetch(proxyUrl, {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
},
});
const data = await response.json();
setUser(data);
};
fetchUser();
}, [userId]);
return <div>{user?.name}</div>;
}React (with Fetch API):
// lib/api-client.ts (Reusable client)
const API_OVERRIDES_URL = "https://api-overrides.vercel.app";
export async function fetchFromProxy(path: string, options?: RequestInit) {
const proxyUrl = `${API_OVERRIDES_URL}/api/proxy/production${path}`;
return fetch(proxyUrl, {
...options,
headers: {
"Content-Type": "application/json",
...options?.headers,
},
});
}
// Usage in components
const response = await fetchFromProxy("/api/users/123");
const user = await response.json();React (with Axios):
import axios from "axios";
const API_OVERRIDES_URL = "https://api-overrides.vercel.app";
const API_TOKEN = "your-api-token";
const apiClient = axios.create({
baseURL: `${API_OVERRIDES_URL}/api/proxy/production`,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${API_TOKEN}`,
},
});
// Usage
const response = await apiClient.get("/api/users/123");Vue:
const API_OVERRIDES_URL = "https://api-overrides.vercel.app";
const API_TOKEN = "your-api-token";
export async function fetchUser(userId: string) {
const response = await fetch(
`${API_OVERRIDES_URL}/api/proxy/production/api/users/${userId}`,
{
headers: {
Authorization: `Bearer ${API_TOKEN}`,
},
}
);
return response.json();
}Angular:
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
@Injectable({
providedIn: "root",
})
export class ApiService {
// Use environment.ts for configuration
private baseUrl = "https://api-overrides.vercel.app/api/proxy/production";
constructor(private http: HttpClient) {}
getUser(id: string) {
return this.http.get(`${this.baseUrl}/api/users/${id}`);
}
}Benefits:
- Test API responses during development without backend changes
- Simulate different API states (loading, error, success)
- Share override configurations across team members
- Test both SSR and client-side rendering scenarios
- Use the same proxy URL for both server and client requests
- Works with any HTTP client library (fetch, axios, etc.)
GET /api/overrides- List all overridesPOST /api/overrides- Create a new overrideGET /api/overrides/[id]- Get a specific overridePUT /api/overrides/[id]- Update an overrideDELETE /api/overrides/[id]- Delete an overrideGET /api/overrides/export- Export all overrides as JSONPOST /api/overrides/import- Import overrides from JSON
GET /api/config- Get API configuration (read-only, for backward compatibility)GET /api/base-apis- List all base APIs (read-only, from BASE_APIS environment variable)GET /api/base-apis/[id]- Get a specific base API (read-only)GET /api/base-apis/export- Export base APIs as JSON
Note: Base APIs are configured via the BASE_APIS environment variable. The API endpoints are read-only and cannot be used to modify configurations.
GET /api/proxy/[key]/[...path]- Proxy endpoint with base API key (handles all HTTP methods)GET /api/proxy/[...path]- Legacy proxy endpoint (uses default routing, handles all HTTP methods)
Overrides are matched based on:
- HTTP Method - Must match exactly (case-insensitive)
- Path - Must match exactly (normalized, trailing slashes removed)
- Headers (optional) - If specified in override, request must include all matching headers
- Request Body (optional) - If specified in override, request body must match exactly (deep equality)
The first matching override is returned. If no override matches, the request is proxied to the main API.
The application uses the following models:
- Override: Stores override rules and responses
- ApiConfig: Legacy model (deprecated, kept for backward compatibility)
- BaseApi: Legacy model (deprecated, base APIs now configured via
BASE_APISenvironment variable)
You can view and edit the schema in prisma/schema.prisma.
Note: Base APIs are now configured via the BASE_APIS environment variable instead of the database. The BaseApi and ApiConfig models are kept for backward compatibility but are no longer actively used for configuration.
# View database in Prisma Studio
pnpm prisma studio
# Create a new migration
pnpm prisma migrate dev --name migration_name
# Apply migrations to production
pnpm db:migrate
# Generate Prisma client
pnpm db:generateapi-overrides/
├── app/
│ ├── api/
│ │ ├── config/ # API configuration endpoints
│ │ ├── overrides/ # Override CRUD endpoints
│ │ └── proxy/ # Proxy endpoint
│ ├── components/ # React components
│ ├── page.tsx # Main UI page
│ └── layout.tsx # Root layout
├── lib/
│ ├── prisma.ts # Prisma client
│ ├── matching.ts # Request matching logic
│ └── proxy.ts # Proxy utility
├── types/ # TypeScript type definitions
├── prisma/
│ └── schema.prisma # Database schema
└── README.md
git add .
git commit -m "Prepare for Vercel deployment"
git push origin main- Go to Vercel and sign in
- Click "Add New Project"
- Import your GitHub repository
- Configure the project:
- Framework Preset: Next.js
- Root Directory:
./(default) - Build Command:
pnpm build(ornpm run build) - Output Directory:
.next(default) - Install Command:
pnpm install(ornpm install)
- In your Vercel project dashboard, go to Storage
- Click Create Database → Postgres
- Choose a name and region
- The connection string will be automatically added as an environment variable
In your Vercel project settings → Environment Variables, add:
DATABASE_URL: Your PostgreSQL connection string- If using Vercel Postgres: The connection string is automatically provided
- If using another PostgreSQL provider: Use your PostgreSQL connection string
After deployment, run migrations on your production database:
# Using Vercel CLI
npx vercel env pull .env.production
pnpm prisma migrate deploy
# Or using direct connection
DATABASE_URL="your-production-url" pnpm prisma migrate deployAlternatively, you can run migrations automatically during build by adding a build script.
Vercel will automatically deploy on every push to your main branch. You can also trigger manual deployments from the dashboard.
# Build the application
pnpm build
# Start production server
pnpm startIf you see a TypeScript error about PrismaClient not being exported:
- Run
pnpm db:generateto regenerate the client - Restart your TypeScript server/IDE
If you get a database connection error:
- Verify your
DATABASE_URLenvironment variable is set correctly - Check that your PostgreSQL database is running and accessible
- For Vercel deployments, ensure
DATABASE_URLpoints to your database - Verify network access if using a hosted database (check IP whitelist if applicable)
- Verify the base API URL is configured correctly in
BASE_APISenvironment variable - Check that the base URL is accessible from your server
- Ensure the base API key in the proxy URL matches a configured base API
- Review network connectivity and firewall settings
MIT