Skip to content

HTTP API

Want to use Quiver as a service? The built-in HTTP API has you covered! Let's explore how to set up and use Quiver's API to sling vectors over the network. 🌐

Setting Up the API Server

Basic Setup

Setting up the Quiver API server is straightforward:

package main

import (
    "github.com/TFMV/quiver"
    "github.com/TFMV/quiver/api"
    "go.uber.org/zap"
)

func main() {
    // Create a logger
    logger, _ := zap.NewDevelopment()

    // Initialize Quiver
    idx, _ := quiver.New(quiver.Config{
        Dimension:   128,
        StoragePath: "./data",
    }, logger)
    defer idx.Close()

    // Create the API server
    server := api.NewServer(api.ServerOptions{
        Port:    "8080",
        Prefork: false,
    }, idx, logger)

    // Start the server
    server.Start()
}

This starts an HTTP server on port 8080 that exposes Quiver's functionality via REST endpoints.

Advanced Configuration

For production use, you'll want more configuration:

// Create the API server with advanced options
server := api.NewServer(api.ServerOptions{
    Host:    "0.0.0.0",     // Listen on all interfaces
    Port:    "8080",
    Prefork: true,          // Use multiple processes for better performance

    // Authentication
    Auth: api.AuthOptions{
        Enabled:  true,
        Username: "admin",
        Password: "secure-password",
    },

    // Rate limiting
    RateLimit: api.RateLimitOptions{
        Enabled:     true,
        MaxRequests: 100,
        TimeWindow:  time.Minute,
    },

    // CORS
    CORS: api.CORSOptions{
        Enabled:      true,
        AllowOrigins: []string{"https://yourdomain.com"},
    },
}, idx, logger)

TLS/HTTPS

For secure communication, enable TLS:

// Start with TLS
server.StartTLS("server.crt", "server.key")

This ensures all API traffic is encrypted.

API Endpoints

Vector Operations

Add Vector

Add a vector to the index:

POST /vectors
Content-Type: application/json

{
  "id": 1,
  "vector": [0.1, 0.2, 0.3, ...],
  "metadata": {
    "category": "science",
    "name": "black hole",
    "tags": ["astronomy", "physics"]
  }
}

Response:

{
  "success": true,
  "id": 1
}

Search for similar vectors:

POST /search
Content-Type: application/json

{
  "vector": [0.1, 0.2, 0.3, ...],
  "k": 10,
  "page": 1,
  "pageSize": 10
}

Response:

{
  "results": [
    {
      "id": 1,
      "distance": 0.0,
      "metadata": {
        "category": "science",
        "name": "black hole",
        "tags": ["astronomy", "physics"]
      }
    },
    // More results...
  ]
}

Search with metadata filtering:

POST /search/hybrid
Content-Type: application/json

{
  "vector": [0.1, 0.2, 0.3, ...],
  "k": 10,
  "filter": "category = 'science' AND json_array_contains(tags, 'physics')"
}

Response:

{
  "results": [
    {
      "id": 1,
      "distance": 0.0,
      "metadata": {
        "category": "science",
        "name": "black hole",
        "tags": ["astronomy", "physics"]
      }
    },
    // More results...
  ]
}

Metadata Operations

Query Metadata

Query metadata directly:

POST /metadata/query
Content-Type: application/json

{
  "query": "SELECT * FROM metadata WHERE category = 'science' ORDER BY created_at DESC LIMIT 10"
}

Response:

{
  "results": [
    {
      "category": "science",
      "name": "black hole",
      "tags": ["astronomy", "physics"]
    },
    // More results...
  ]
}

Management Operations

Health Check

Check if the server is healthy:

GET /health

Response:

{
  "status": "ok",
  "version": "1.0.0",
  "uptime": "3h2m15s"
}

Metrics

Get server metrics:

GET /metrics

Response:

{
  "vector_count": 10000,
  "memory_usage_mb": 256,
  "search_latency_ms": 0.5,
  "queries_per_second": 120
}

Using the API

cURL Examples

Add a Vector

curl -X POST http://localhost:8080/vectors \
  -H "Content-Type: application/json" \
  -d '{
    "id": 1,
    "vector": [0.1, 0.2, 0.3],
    "metadata": {
      "category": "example",
      "name": "test vector"
    }
  }'

Search

curl -X POST http://localhost:8080/search \
  -H "Content-Type: application/json" \
  -d '{
    "vector": [0.1, 0.2, 0.3],
    "k": 5
  }'

Python Client

Here's a simple Python client for the Quiver API:

import requests
import numpy as np

class QuiverClient:
    def __init__(self, base_url, username=None, password=None):
        self.base_url = base_url
        self.auth = None
        if username and password:
            self.auth = (username, password)

    def add_vector(self, id, vector, metadata):
        response = requests.post(
            f"{self.base_url}/vectors",
            json={
                "id": id,
                "vector": vector.tolist() if isinstance(vector, np.ndarray) else vector,
                "metadata": metadata
            },
            auth=self.auth
        )
        return response.json()

    def search(self, vector, k=10, page=1, page_size=10):
        response = requests.post(
            f"{self.base_url}/search",
            json={
                "vector": vector.tolist() if isinstance(vector, np.ndarray) else vector,
                "k": k,
                "page": page,
                "pageSize": page_size
            },
            auth=self.auth
        )
        return response.json()

    def hybrid_search(self, vector, k=10, filter=None):
        response = requests.post(
            f"{self.base_url}/search/hybrid",
            json={
                "vector": vector.tolist() if isinstance(vector, np.ndarray) else vector,
                "k": k,
                "filter": filter
            },
            auth=self.auth
        )
        return response.json()

# Usage
client = QuiverClient("http://localhost:8080", "admin", "password")

# Add a vector
client.add_vector(
    id=1,
    vector=[0.1, 0.2, 0.3],
    metadata={"category": "example", "name": "test vector"}
)

# Search
results = client.search([0.1, 0.2, 0.3], k=5)
print(results)

JavaScript Client

And here's a JavaScript client:

class QuiverClient {
  constructor(baseUrl, username = null, password = null) {
    this.baseUrl = baseUrl;
    this.headers = {
      'Content-Type': 'application/json'
    };

    if (username && password) {
      this.headers['Authorization'] = 'Basic ' + 
        btoa(`${username}:${password}`);
    }
  }

  async addVector(id, vector, metadata) {
    const response = await fetch(`${this.baseUrl}/vectors`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify({
        id,
        vector,
        metadata
      })
    });

    return response.json();
  }

  async search(vector, k = 10, page = 1, pageSize = 10) {
    const response = await fetch(`${this.baseUrl}/search`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify({
        vector,
        k,
        page,
        pageSize
      })
    });

    return response.json();
  }

  async hybridSearch(vector, k = 10, filter = null) {
    const response = await fetch(`${this.baseUrl}/search/hybrid`, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify({
        vector,
        k,
        filter
      })
    });

    return response.json();
  }
}

// Usage
const client = new QuiverClient('http://localhost:8080', 'admin', 'password');

// Add a vector
client.addVector(
  1,
  [0.1, 0.2, 0.3],
  {category: 'example', name: 'test vector'}
).then(result => console.log(result));

// Search
client.search([0.1, 0.2, 0.3], 5)
  .then(results => console.log(results));

Performance Considerations

Server Configuration

For optimal performance:

  • Enable Prefork mode for multi-core utilization
  • Use a reverse proxy like Nginx for SSL termination
  • Configure appropriate timeouts for your use case
  • Monitor server resources and scale as needed

Client Best Practices

  • Reuse HTTP connections (connection pooling)
  • Batch vector additions when possible
  • Use appropriate timeout settings
  • Implement retry logic for transient failures

Next Steps

Now that you've learned how to use Quiver's HTTP API, check out: