Skip to content

API Endpoint

A REST API for chat completions with SSE streaming support.

Features

  • SSE Streaming - Server-Sent Events for real-time responses
  • Rate Limiting - In-memory request throttling
  • Validation - Request body validation
  • Error Handling - Proper HTTP status codes

Quick Start

bash
export OPENAI_API_KEY=your_key
npm run recipe:api-endpoint

How It Works

This recipe creates a simple HTTP server that wraps Meloqui's ChatClient, exposing it as a REST API with SSE streaming support.

Endpoints:

  • POST /chat - Send a message and get a completion (supports streaming)
  • GET /health - Health check endpoint

Flow:

  1. Client sends a POST request with a message
  2. Server validates the request and checks rate limits
  3. Meloqui's ChatClient forwards the request to OpenAI
  4. Response streams back through SSE or returns as JSON

Architecture

API Reference

POST /chat

Send a chat message and receive a response.

Request:

json
{
  "message": "Hello!",
  "stream": false
}
FieldTypeRequiredDescription
messagestringYesThe message to send
streambooleanNoEnable SSE streaming (default: false)

Response (non-streaming):

json
{
  "content": "Hi there! How can I help you?",
  "tokensUsed": 15
}

Response (streaming):

Content-Type: text/event-stream

data: {"content": "Hi"}
data: {"content": " there"}
data: {"content": "!"}
data: [DONE]

GET /health

Health check endpoint.

json
{"content": "ok"}

Testing with curl

Non-streaming Request

bash
curl -X POST http://localhost:3000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "What is 2+2?"}'

Streaming Request

bash
curl -X POST http://localhost:3000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "Tell me a short joke", "stream": true}'

Health Check

bash
curl http://localhost:3000/health

Code Walkthrough

Server Setup

typescript
import * as http from 'http';
import { ChatClient } from 'meloqui';

const client = new ChatClient({
  provider: 'openai',
  model: 'gpt-4o-mini',
  apiKey: process.env.OPENAI_API_KEY
});

const server = http.createServer(async (req, res) => {
  if (req.url === '/chat' && req.method === 'POST') {
    await handleChat(req, res, client);
  }
});

server.listen(3000);

SSE Streaming

typescript
// Set SSE headers
res.writeHead(200, {
  'Content-Type': 'text/event-stream',
  'Cache-Control': 'no-cache',
  'Connection': 'keep-alive'
});

// Stream chunks
for await (const chunk of client.stream(message)) {
  res.write(`data: ${JSON.stringify({ content: chunk.content })}\n\n`);
}

res.write('data: [DONE]\n\n');
res.end();

Rate Limiting

typescript
const RATE_LIMIT_WINDOW_MS = 60_000; // 1 minute
const RATE_LIMIT_MAX_REQUESTS = 20;

const rateLimitMap = new Map<string, { count: number; resetTime: number }>();

function checkRateLimit(clientIP: string): boolean {
  const now = Date.now();
  const record = rateLimitMap.get(clientIP);

  if (!record || now > record.resetTime) {
    rateLimitMap.set(clientIP, { count: 1, resetTime: now + RATE_LIMIT_WINDOW_MS });
    return true;
  }

  if (record.count >= RATE_LIMIT_MAX_REQUESTS) {
    return false;
  }

  record.count++;
  return true;
}

Error Responses

StatusCodeDescription
400INVALID_REQUESTInvalid JSON body
400VALIDATION_ERRORMissing or invalid fields
401AUTH_ERRORAPI key authentication failed
404NOT_FOUNDEndpoint not found
429RATE_LIMITEDToo many requests
429PROVIDER_RATE_LIMITEDOpenAI rate limit hit
500CHAT_ERRORChat completion failed
500INTERNAL_ERRORUnexpected error

Rate Limit Headers

All responses include rate limit information:

X-RateLimit-Limit: 20
X-RateLimit-Remaining: 19
X-RateLimit-Reset: 60

Configuration

Environment variables:

VariableDefaultDescription
OPENAI_API_KEY-Required. OpenAI API key
PORT3000Server port

Full Source

View on GitHub

Released under the MIT License.