AI Gateway
NextShip includes an AI API gateway that proxies requests to AI providers (OpenAI, Anthropic, etc.) with built-in credit-based billing and usage tracking.
Features
- Proxy requests to multiple AI providers
- Credit-based billing per request
- Usage logging and analytics
- API key management for users
- Rate limiting
- Request/response logging
Architecture
Client Request
↓
API Key Validation
↓
Credit Balance Check
↓
AI Provider (OpenAI/Anthropic)
↓
Deduct Credits
↓
Log Usage
↓
Response to Client
Configuration
Environment Variables
# AI Providers
OPENAI_API_KEY=sk-xxx
ANTHROPIC_API_KEY=sk-ant-xxx
# Optional: Custom base URLs
OPENAI_BASE_URL=https://api.openai.com/v1
ANTHROPIC_BASE_URL=https://api.anthropic.comPricing Configuration
API pricing is managed in the admin dashboard (/api-pricing):
// src/lib/db/schema.ts
export const apiPricing = pgTable("api_pricing", {
id: text("id").primaryKey(),
provider: text("provider").notNull(), // "openai", "anthropic"
model: text("model").notNull(), // "gpt-4", "claude-3"
inputPricePerToken: integer("input_price"), // credits per 1K tokens
outputPricePerToken: integer("output_price"),
isActive: boolean("is_active").default(true),
});API Routes
Chat Completions
// POST /api/v1/chat/completions
// Compatible with OpenAI API format
const response = await fetch("/api/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},
body: JSON.stringify({
model: "gpt-4",
messages: [
{ role: "user", content: "Hello!" }
],
}),
});Route Handler
// src/app/api/v1/chat/completions/route.ts
export async function POST(req: Request) {
// 1. Validate API key
const apiKey = req.headers.get("authorization")?.replace("Bearer ", "");
const keyData = await validateApiKey(apiKey);
if (!keyData) {
return Response.json({ error: "Invalid API key" }, { status: 401 });
}
// 2. Check credit balance
const balance = await getUserCredits(keyData.userId);
if (balance <= 0) {
return Response.json({ error: "Insufficient credits" }, { status: 402 });
}
// 3. Forward to AI provider
const body = await req.json();
const response = await forwardToProvider(body);
// 4. Calculate and deduct credits
const usage = response.usage;
const cost = calculateCost(body.model, usage);
await deductCredits(keyData.userId, cost, {
model: body.model,
inputTokens: usage.prompt_tokens,
outputTokens: usage.completion_tokens,
});
// 5. Log usage
await logApiUsage({
userId: keyData.userId,
apiKeyId: keyData.id,
model: body.model,
inputTokens: usage.prompt_tokens,
outputTokens: usage.completion_tokens,
creditsUsed: cost,
});
return Response.json(response);
}API Key Management
Users can create and manage API keys in /api-keys:
// src/server/actions/api-keys.ts
// Create new API key
export async function createApiKey(name: string) {
const session = await requireAuth();
const key = `sk-${generateRandomString(48)}`;
await db.insert(apiKeys).values({
id: crypto.randomUUID(),
userId: session.user.id,
name,
key: hashApiKey(key), // Store hashed
lastUsed: null,
});
return { key }; // Return plain key only once
}
// List user's API keys
export async function getMyApiKeys() {
const session = await requireAuth();
return db.query.apiKeys.findMany({
where: eq(apiKeys.userId, session.user.id),
columns: {
id: true,
name: true,
lastUsed: true,
createdAt: true,
// key is not returned (security)
},
});
}
// Delete API key
export async function deleteApiKey(keyId: string) {
const session = await requireAuth();
await db.delete(apiKeys).where(
and(
eq(apiKeys.id, keyId),
eq(apiKeys.userId, session.user.id)
)
);
}Usage Tracking
Usage Logs Schema
export const apiUsageLogs = pgTable("api_usage_logs", {
id: text("id").primaryKey(),
userId: text("user_id").references(() => users.id),
apiKeyId: text("api_key_id").references(() => apiKeys.id),
model: text("model").notNull(),
inputTokens: integer("input_tokens"),
outputTokens: integer("output_tokens"),
creditsUsed: integer("credits_used"),
createdAt: timestamp("created_at").defaultNow(),
});Viewing Usage
Users can view their API usage in /api-usage:
// src/server/actions/api-keys.ts
export async function getMyApiUsageLogs(params: {
page: number;
limit: number;
startDate?: Date;
endDate?: Date;
}) {
const session = await requireAuth();
const logs = await db.query.apiUsageLogs.findMany({
where: and(
eq(apiUsageLogs.userId, session.user.id),
// date filters...
),
orderBy: desc(apiUsageLogs.createdAt),
limit: params.limit,
offset: (params.page - 1) * params.limit,
});
return { items: logs, total };
}Admin Dashboard
The API pricing page (/api-pricing) allows admins to:
- Configure pricing per model
- View total API usage across users
- Enable/disable specific models
- Set rate limits
Credit Calculation
// src/lib/ai-gateway/pricing.ts
export async function calculateCost(
model: string,
usage: { prompt_tokens: number; completion_tokens: number }
) {
const pricing = await getModelPricing(model);
const inputCost = Math.ceil(
(usage.prompt_tokens / 1000) * pricing.inputPricePerToken
);
const outputCost = Math.ceil(
(usage.completion_tokens / 1000) * pricing.outputPricePerToken
);
return inputCost + outputCost;
}Best Practices
- Hash API keys - Never store plain text API keys
- Rate limiting - Implement per-user rate limits
- Monitor usage - Set up alerts for unusual activity
- Graceful degradation - Handle provider outages
- Audit logging - Log all API requests for debugging