SDK: Server Proxy Pattern
Keep your Flapjack API key secure in production by proxying requests through your server. Recommended pattern for React apps.
In production, never expose your fj_live_ API key in client-side JavaScript. Instead, create server-side proxy routes that add the API key and forward requests to Flapjack.
Architecture
Browser β Your Server (adds API key) β Flapjack API
The client calls your server routes. Your server adds the API key and proxies to Flapjack. The API key never leaves your server.
Next.js Implementation
Proxy: List Agents
// app/api/flapjack/agents/route.ts
import { FlapjackClient } from '@flapjack/sdk';
const client = new FlapjackClient({
apiKey: process.env.FLAPJACK_API_KEY!,
baseUrl: process.env.FLAPJACK_BASE_URL!,
});
export async function GET() {
const agents = await client.listAgents();
return Response.json(agents);
}
Proxy: Create Thread
// app/api/flapjack/threads/route.ts
import { FlapjackClient } from '@flapjack/sdk';
const client = new FlapjackClient({
apiKey: process.env.FLAPJACK_API_KEY!,
baseUrl: process.env.FLAPJACK_BASE_URL!,
});
export async function POST(req: Request) {
const { agentId } = await req.json();
const thread = await client.createThread(agentId);
return Response.json(thread);
}
Proxy: Stream Messages
// app/api/flapjack/threads/[threadId]/messages/route.ts
import { FlapjackClient } from '@flapjack/sdk';
const client = new FlapjackClient({
apiKey: process.env.FLAPJACK_API_KEY!,
baseUrl: process.env.FLAPJACK_BASE_URL!,
});
export async function POST(
req: Request,
{ params }: { params: Promise<{ threadId: string }> }
) {
const { threadId } = await params;
const { content } = await req.json();
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
const write = (type: string, data: unknown) =>
controller.enqueue(
encoder.encode(`event: ${type}\ndata: ${JSON.stringify(data)}\n\n`)
);
try {
for await (const event of client.sendMessage(threadId, content)) {
// Forwards the full SDK event object as data.
// The `type` field in data is redundant with the SSE event line
// but harmless β clients can use either for routing.
write(event.type, event);
}
} catch (err) {
write('error', {
code: 'STREAM_FAILURE',
detail: err instanceof Error ? err.message : 'Unknown error',
});
} finally {
controller.close();
}
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream; charset=utf-8',
'Cache-Control': 'no-cache, no-transform',
Connection: 'keep-alive',
},
});
}
π Copy as prompt
Create Next.js API proxy routes for Flapjack: one for listing agents (GET), one for creating threads (POST), and one for streaming messages (POST with SSE). Use
FlapjackClientserver-side with the API key from environment variables. The message streaming route should forward SSE events from Flapjack to the client.
Client-Side Usage
Your React components call your proxy routes instead of Flapjack directly:
// No API key needed client-side!
const agents = await fetch('/api/flapjack/agents').then(r => r.json());
const thread = await fetch('/api/flapjack/threads', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ agentId: agents[0].id }),
}).then(r => r.json());
// Stream from your proxy
const response = await fetch(`/api/flapjack/threads/${thread.id}/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: 'Hello!' }),
});
// Parse SSE from response.body (same format as Flapjack API)
π Copy as prompt
Write client-side fetch calls that use my Flapjack proxy routes instead of calling the Flapjack API directly. No API key needed on the client.
Adding Your Own Auth
Layer your own authentication on top of the proxy routes:
// app/api/flapjack/agents/route.ts
import { getServerSession } from 'next-auth';
export async function GET() {
const session = await getServerSession();
if (!session) return new Response('Unauthorized', { status: 401 });
const agents = await client.listAgents();
return Response.json(agents);
}
Express Implementation
import express from 'express';
import { FlapjackClient } from '@flapjack/sdk';
const app = express();
app.use(express.json());
const client = new FlapjackClient({ apiKey: process.env.FLAPJACK_API_KEY! });
app.get('/api/flapjack/agents', async (req, res) => {
const agents = await client.listAgents();
res.json(agents);
});
app.post('/api/flapjack/threads/:threadId/messages', async (req, res) => {
const { content } = req.body;
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
for await (const event of client.sendMessage(req.params.threadId, content)) {
// Forwards full SDK event; `type` in data is redundant with SSE event line
res.write(`event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`);
}
res.end();
});
π Copy as prompt
Create Express proxy routes for Flapjack that list agents and stream messages. Keep the API key server-side only.
Next Steps
- React Hooks β build the frontend
- Examples β full proxy + React examples
- Concepts: Authentication β security model