Addis AI

Server-side Integration

Build secure backend integrations using Node.js, Python, Go, and PHP.

Server-side integration is the only secure way to use Addis AI in production. By routing requests through your own server, you keep your API keys hidden from users and gain control over rate limiting and logging.

Architecture Overview

Your server acts as a secure Middleman. The client talks to your server, and your server talks to Addis AI.

Secure request pathPhase 01 / 05
Client app
Sends data
Secure zone
Your backend
+ API key
Addis AI
Addis AI
Waiting for a client request...

Client Request: The user's device sends a simple payload (e.g., { "message": "Hello" }) to your backend. It does not send the API key.

Server Authentication: Your server receives the request, validates the user's session, and injects the X-API-Key header from your server-side environment variables.

AI Processing: Your server forwards the authenticated request to Addis AI. The AI processes it and sends the response back to your server.

Response: Your server sends the final answer back to the client app.


Why Server-Side?

Direct client-side calls (from a browser or mobile app) exposes your credentials. This approach offers critical advantages:

Security

Keep your API keys secure on your server. Never expose sk_ keys in frontend code.

CORS & Network

Avoid Cross-Origin (CORS) errors that occur when calling APIs directly from a browser.

Request Validation

Validate and sanitize user inputs on your backend before they ever reach the Addis AI API.

Caching & Cost

Cache common responses (e.g. FAQs) in Redis/Database to reduce API calls and save money.


Security Essentials

Why a Proxy?

If you put your API key in a mobile app or website, anyone can find it. A server-side proxy injects the key securely away from the client's eyes.


Integration Patterns

Choose the pattern that matches your data requirements.

Standard Implementation (Text)

Use this for chatbots, translation, or simple text generation where waiting 2-3 seconds for a response is acceptable.

import AddisAI from "addisai";
import express from "express";

const app = express();
app.use(express.json());
const addis = new AddisAI();

app.post('/api/chat', async (req, res) => {
  const { message } = req.body;

  try {
    const response = await addis.chat.completions.create({
      messages: [{ role: "user", content: message }],
      language: "am",
      temperature: 0.7,
    });

    res.json({
      response_text: response.choices[0].message.content,
      usage: response.usage,
    });
  } catch (error) {
    res.status(500).json({ error: "Internal Server Error" });
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));
from addisai import AddisAI
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()
addis = AddisAI()

class ChatRequest(BaseModel):
    message: str

@app.post("/api/chat")
def chat_handler(req: ChatRequest):
    response = addis.chat.completions.create(
        messages=[{"role": "user", "content": req.message}],
        language="am",
        temperature=0.7,
    )
    return {
        "response_text": response["choices"][0]["message"]["content"],
        "usage": response.get("usage"),
    }
package main

import (
	"bytes"
	"encoding/json"
	"net/http"
	"os"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	apiKey := os.Getenv("ADDIS_AI_KEY")

	r.POST("/api/chat", func(c *gin.Context) {
		var input struct {
			Message string `json:"message"`
		}
		if err := c.BindJSON(&input); err != nil { return }

		payload := map[string]interface{}{
			"model": "Addis-፩-አሌፍ",
			"prompt": input.Message,
			"target_language": "am",
		}
		jsonValue, _ := json.Marshal(payload)

		req, _ := http.NewRequest("POST", "https://api.addisassistant.com/api/v1/chat_generate", bytes.NewBuffer(jsonValue))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("X-API-Key", apiKey)

		client := &http.Client{}
		resp, err := client.Do(req)
        // Error handling omitted for brevity
		defer resp.Body.Close()

		var result map[string]interface{}
		json.NewDecoder(resp.Body).Decode(&result)
		c.JSON(resp.StatusCode, result)
	})
	r.Run()
}
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Request;

Route::post('/api/chat', function (Request $request) {
    $response = Http::withHeaders([
        'X-API-Key' => env('ADDIS_AI_KEY'),
        'Content-Type' => 'application/json',
    ])->post('https://api.addisassistant.com/api/v1/chat_generate', [
        'model' => 'Addis-፩-አሌፍ',
        'prompt' => $request->input('message'),
        'target_language' => 'am'
    ]);

    return response()->json($response->json(), $response->status());
});

Streaming Implementation

When using stream: true, the API sends data in chunks. Your server must pipe these chunks to the client immediately to avoid latency.

app.post('/api/chat/stream', async (req, res) => {
  const { message } = req.body;

  try {
    const stream = await addis.chat.completions.create({
      messages: [{ role: "user", content: message }],
      language: "am",
      stream: true,
    });

    res.setHeader('Content-Type', 'application/x-ndjson');
    res.setHeader('Transfer-Encoding', 'chunked');

    for await (const event of stream) {
      res.write(JSON.stringify(event) + "\n");
    }
    res.end();
  } catch (error) {
    res.status(500).end();
  }
});
import json
from fastapi.responses import StreamingResponse

@app.post("/api/chat/stream")
def chat_stream(req: ChatRequest):
    stream = addis.chat.completions.create(
        messages=[{"role": "user", "content": req.message}],
        language="am",
        stream=True,
    )

    def iter_stream():
        for event in stream:
            yield json.dumps(event) + "\n"

    return StreamingResponse(
        iter_stream(),
        media_type="application/x-ndjson",
    )

File Uploads (Multipart)

When using the Vision features (images) with the chat endpoint, your server must handle the file upload and forward it as multipart/form-data.

Node.js Example (using multer):

import AddisAI from "addisai";
import multer from "multer";

const upload = multer(); // Memory storage
const addis = new AddisAI();

// Route: /api/upload
app.post('/api/upload', upload.single('image'), async (req, res) => {
  try {
    if (!req.file) return res.status(400).json({ error: "Image required" });

    const image = new File(
      [req.file.buffer],
      req.file.originalname,
      { type: req.file.mimetype },
    );

    const response = await addis.chat.completions.create({
      messages: [{ role: "user", content: "Describe this image" }],
      language: "am",
      attachments: [{ file: image }],
    });

    res.json({
      response_text: response.choices[0].message.content,
      uploaded_attachments: response.uploaded_attachments,
    });
  } catch (error) {
    res.status(500).json({ error: "Upload failed" });
  }
});

Production Readiness

Moving from localhost to production requires handling security, scalability, and deployment.

Security & Architecture

API Key Security

Environment Variables: Store keys in ENV vars, never in code.

Access Control: Limit which servers/processes can access the keys and rotate them regularly.

Input Validation

Sanitization: Validate all user inputs on your backend to prevent malicious prompts or excessively long inputs before forwarding.

Rate Limiting

Throttling: Implement per-user rate limiting (e.g., using Redis) to prevent abuse and control your billing costs.

Error Handling

Graceful Failures: Log errors internally for debugging but return generic, safe error messages to the client to avoid leaking stack traces.

Deployment & Containerization

To scale your integration, use horizontal scaling and containerization.

Example Dockerfile for Node.js Proxy:

Dockerfile
FROM node:18-alpine

WORKDIR /app

# Install dependencies first (caching)
COPY package*.json ./
RUN npm install --production

# Copy source code
COPY . .

# Environment variables should be injected at runtime, not build time
ENV PORT=3000
EXPOSE 3000

CMD ["node", "app.js"]

Best Practices Checklist

Go-Live Checklist

  • Security: API Key is stored in ENV, not Git.
  • Validation: Inputs are checked for length and content.
  • Auth: Your proxy endpoint is protected (JWT/Session).
  • Reliability: Timeout is set to 60s+ to handle AI generation time.
  • HTTPS: All traffic is encrypted via TLS/SSL.

Next Steps

Now that your server is secure, expand your integration:

On this page