Typesense Installation and Configuration

Typesense is a fast, typo-tolerant search engine built in C++ that provides a simple REST API for indexing and searching documents with millisecond latency. This guide covers installing Typesense on Linux, creating collections, indexing documents, configuring search parameters, faceted search, geo-search, and Docker deployment.

Prerequisites

  • Ubuntu 20.04+ or CentOS 8+ / Rocky Linux 8+
  • 1 GB RAM minimum (RAM = index size for best performance)
  • SSD storage recommended
  • Root or sudo access

Installing Typesense

# Ubuntu/Debian - install via package
TYPESENSE_VERSION="26.0"

# Download package
wget https://dl.typesense.org/releases/${TYPESENSE_VERSION}/typesense-server-${TYPESENSE_VERSION}-amd64.deb
sudo apt-get install -y ./typesense-server-${TYPESENSE_VERSION}-amd64.deb

# CentOS/Rocky - RPM package
wget https://dl.typesense.org/releases/${TYPESENSE_VERSION}/typesense-server-${TYPESENSE_VERSION}-1.x86_64.rpm
sudo rpm -i typesense-server-${TYPESENSE_VERSION}-1.x86_64.rpm

# Create data directory
sudo mkdir -p /var/lib/typesense
sudo chown typesense:typesense /var/lib/typesense

# Configure Typesense
sudo cat > /etc/typesense/typesense-server.ini << 'EOF'
[server]
api-key = your-admin-api-key-here
data-dir = /var/lib/typesense
log-dir = /var/log/typesense
enable-cors = true
EOF

# Generate a strong API key
openssl rand -hex 32  # Use this as api-key above

# Start and enable the service
sudo systemctl enable --now typesense-server
sudo systemctl status typesense-server

# Test it's running
curl http://localhost:8108/health

Running Typesense with Docker

# Create a directory for data persistence
mkdir -p /opt/typesense/data

# Run Typesense with Docker
docker run -d \
    --name typesense \
    --restart unless-stopped \
    -p 8108:8108 \
    -v /opt/typesense/data:/data \
    typesense/typesense:26.0 \
    --data-dir /data \
    --api-key=your-admin-api-key \
    --enable-cors

# Verify
curl http://localhost:8108/health
# Expected: {"ok":true}

With Docker Compose for production:

# docker-compose.yml
version: "3.9"
services:
  typesense:
    image: typesense/typesense:26.0
    container_name: typesense
    restart: unless-stopped
    ports:
      - "8108:8108"
    volumes:
      - typesense-data:/data
    command: >
      --data-dir /data
      --api-key=${TYPESENSE_API_KEY}
      --enable-cors
      --thread-pool-size=8

volumes:
  typesense-data:

Creating Collections

Collections are schema-defined indexes. Define fields with their types upfront:

# Create a products collection
curl -X POST http://localhost:8108/collections \
  -H 'Content-Type: application/json' \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -d '{
    "name": "products",
    "fields": [
      {"name": "id",          "type": "string"},
      {"name": "name",        "type": "string"},
      {"name": "description", "type": "string"},
      {"name": "category",    "type": "string",  "facet": true},
      {"name": "brand",       "type": "string",  "facet": true},
      {"name": "price",       "type": "float",   "facet": true},
      {"name": "rating",      "type": "float"},
      {"name": "in_stock",    "type": "bool",    "facet": true},
      {"name": "tags",        "type": "string[]","facet": true},
      {"name": "location",    "type": "geopoint", "optional": true}
    ],
    "default_sorting_field": "rating"
  }'

Field types: string, int32, int64, float, bool, string[], int32[], float[], geopoint, geopoint[], auto (schema-less).

Mark fields with "facet": true to enable filtering and aggregation on them.

Indexing Documents

Index a single document:

curl -X POST http://localhost:8108/collections/products/documents \
  -H 'Content-Type: application/json' \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -d '{
    "id": "1",
    "name": "Wireless Mechanical Keyboard",
    "description": "Compact 75% layout with Cherry MX switches and Bluetooth 5.0",
    "category": "Electronics",
    "brand": "Keychron",
    "price": 99.99,
    "rating": 4.7,
    "in_stock": true,
    "tags": ["keyboard", "bluetooth", "mechanical"],
    "location": [37.7749, -122.4194]
  }'

Bulk import documents (JSONL format - newline-delimited JSON):

# Create a JSONL file
cat > products.jsonl << 'EOF'
{"id": "2", "name": "USB-C Hub 7-in-1", "category": "Electronics", "brand": "Anker", "price": 39.99, "rating": 4.5, "in_stock": true, "tags": ["hub", "usb-c"]}
{"id": "3", "name": "Ergonomic Mouse", "category": "Electronics", "brand": "Logitech", "price": 79.99, "rating": 4.6, "in_stock": false, "tags": ["mouse", "ergonomic"]}
{"id": "4", "name": "Monitor Stand", "category": "Accessories", "brand": "Vivo", "price": 29.99, "rating": 4.3, "in_stock": true, "tags": ["stand", "desk"]}
EOF

# Import (action=create, upsert, or update)
curl -X POST "http://localhost:8108/collections/products/documents/import?action=upsert" \
  -H 'Content-Type: text/plain' \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  --data-binary @products.jsonl

Update a document:

curl -X PATCH http://localhost:8108/collections/products/documents/1 \
  -H 'Content-Type: application/json' \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -d '{"price": 89.99, "in_stock": false}'

Searching with the API

# Basic search
curl "http://localhost:8108/collections/products/documents/search?q=keyboard&query_by=name,description" \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key'

# Advanced search with sorting and pagination
curl "http://localhost:8108/collections/products/documents/search" \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -G \
  --data-urlencode "q=wireless keyboard" \
  --data-urlencode "query_by=name,description,tags" \
  --data-urlencode "query_by_weights=3,2,1" \
  --data-urlencode "sort_by=rating:desc,price:asc" \
  --data-urlencode "per_page=20" \
  --data-urlencode "page=1" \
  --data-urlencode "num_typos=2" \
  --data-urlencode "prefix=true"

Key search parameters:

  • query_by - which fields to search (comma-separated, ordered by priority)
  • query_by_weights - relative importance of each field
  • sort_by - sort order (field:asc or field:desc, up to 3 fields)
  • num_typos - typos allowed (0-2 per token)
  • prefix - allow prefix matching for autocomplete
  • filter_by - filter results before ranking

Faceted Search and Filtering

# Search with facets and filters
curl "http://localhost:8108/collections/products/documents/search" \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -G \
  --data-urlencode "q=wireless" \
  --data-urlencode "query_by=name,description" \
  --data-urlencode "filter_by=in_stock:=true && price:<100 && category:=[Electronics,Accessories]" \
  --data-urlencode "facet_by=category,brand,price(ranges:[0,50],[50,100],[100,500])" \
  --data-urlencode "max_facet_values=10" \
  --data-urlencode "sort_by=price:asc"

Filter syntax:

  • price:<100 - numeric comparison
  • in_stock:=true - boolean exact match
  • category:=[Electronics,Accessories] - include multiple values
  • category:!=[Clothing] - exclude values
  • && - AND, || - OR

The response includes a facet_counts array showing counts for each facet value, which you use to render filter checkboxes.

Search and sort by geographic proximity:

# Find products near San Francisco, sorted by distance
curl "http://localhost:8108/collections/products/documents/search" \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -G \
  --data-urlencode "q=*" \
  --data-urlencode "query_by=name" \
  --data-urlencode "filter_by=location:(37.7749,-122.4194,50 km)" \
  --data-urlencode "sort_by=location(37.7749,-122.4194):asc"

The location filter takes (lat,lng,radius). Sort by location(lat,lng):asc to sort nearest first.

API Keys and Security

Create scoped API keys for frontend use:

# Create a search-only key (can't create/delete documents)
curl -X POST http://localhost:8108/keys \
  -H 'Content-Type: application/json' \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -d '{
    "description": "Frontend search key",
    "actions": ["documents:search"],
    "collections": ["products"]
  }'

# Create a key scoped to specific filter (multi-tenant)
curl -X POST http://localhost:8108/keys \
  -H 'Content-Type: application/json' \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key' \
  -d '{
    "description": "Tenant A search key",
    "actions": ["documents:search"],
    "collections": ["products"],
    "embedded_operations": [{"filter_by": "tenant_id:=tenant_a"}]
  }'

Restrict the API to localhost and use a reverse proxy (Nginx) for HTTPS in production:

# /etc/nginx/sites-available/typesense
server {
    listen 443 ssl;
    server_name search.example.com;

    ssl_certificate /etc/letsencrypt/live/search.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/search.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8108;
        proxy_set_header Host $host;
    }
}

Troubleshooting

Check Typesense logs:

sudo journalctl -u typesense-server -f
tail -f /var/log/typesense/typesense-server.log

Verify collection schema:

curl http://localhost:8108/collections/products \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key'

Slow import:

# Increase batch size for bulk import
curl "http://localhost:8108/collections/products/documents/import?action=upsert&batch_size=1000" ...

Memory usage:

# Check stats
curl http://localhost:8108/stats.json \
  -H 'X-TYPESENSE-API-KEY: your-admin-api-key'
# Look at memory_used_bytes

Search returning unexpected results:

  • Check query_by_weights - higher weight = more influence
  • Use ?debug=true to see search scoring details
  • Increase num_typos if too restrictive

Conclusion

Typesense delivers sub-millisecond search with typo tolerance out of the box, making it an excellent choice for product search, documentation search, and autocomplete features that need to feel instant. Its simple REST API, clear schema definition, and scoped API keys make integration straightforward, and the built-in faceting covers most e-commerce filtering needs without additional infrastructure. For production, run it behind Nginx with TLS and use search-only API keys in your frontend code.