Typesense es un motor de búsqueda de código abierto, tolerante a errores tipográficos y pensado para búsquedas instantáneas mientras el usuario escribe. Mantiene el índice en memoria, se configura con un único fichero y expone una API REST sencilla, lo que lo convierte en una alternativa ligera a Elasticsearch o Algolia. En este tutorial instalarás Typesense en Ubuntu 24.04 con el paquete oficial, crearás una colección de productos, harás búsquedas con filtros y facetas, generarás una clave de solo búsqueda para el frontend y publicarás la API con HTTPS detrás de Nginx.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS de 64 bits (x86_64 o arm64), por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Al menos 1 GB de RAM. Typesense guarda todo el índice en memoria, así que calcula entre dos y tres veces el tamaño de los campos que indexes.
  • Para el paso 7: un dominio o subdominio (en esta guía, search.your_domain) con un registro DNS A apuntando a la IP del servidor.

Paso 1: Instalar Typesense

Typesense publica paquetes .deb oficiales para cada versión. Consulta la última versión estable en la página de descargas y guárdala en una variable; esta guía usa la 30.2:

TYPESENSE_VERSION=30.2
cd /tmp
curl -fLO "https://dl.typesense.org/releases/${TYPESENSE_VERSION}/typesense-server-${TYPESENSE_VERSION}-amd64.deb"
sudo apt install -y "./typesense-server-${TYPESENSE_VERSION}-amd64.deb"

En servidores arm64, sustituye amd64 por arm64 en el nombre del fichero.

El paquete instala el binario, crea el fichero de configuración /etc/typesense/typesense-server.ini con una clave de administración aleatoria y registra el servicio typesense-server. Habilítalo y arráncalo:

sudo systemctl enable --now typesense-server

Comprueba el estado del servicio:

systemctl status typesense-server --no-pager

La salida debe mostrar Active: active (running).

Paso 2: Revisar la configuración y probar la API

Abre el fichero de configuración:

sudo nano /etc/typesense/typesense-server.ini

Tendrá un contenido parecido a este:

[server]

api-address = 0.0.0.0
api-port = 8108
data-dir = /var/lib/typesense
api-key = aB3dE5fG7hJ9kL1mN3pQ5rS7tU9vW1xY
log-dir = /var/log/typesense

Cambia api-address a 127.0.0.1. Así Typesense solo acepta conexiones locales y, en el paso 7, Nginx se encargará de publicarlo con HTTPS. Guarda el fichero y reinicia el servicio:

sudo systemctl restart typesense-server

La clave api-key es la clave de administración: permite crear y borrar colecciones y claves, así que nunca debe salir del servidor ni de tu backend. Guárdala en una variable para los siguientes comandos:

export TYPESENSE_API_KEY=$(sudo awk -F' *= *' '/^api-key/ {print $2}' /etc/typesense/typesense-server.ini)

Comprueba que el servidor responde. El endpoint /health no necesita clave:

curl http://127.0.0.1:8108/health
{"ok":true}

Instala jq, que usarás para leer las respuestas JSON con comodidad:

sudo apt install -y jq

Paso 3: Crear una colección

Una colección es el equivalente a una tabla: tiene un esquema con los campos que se indexan y su tipo. Los campos con "facet": true permiten contar y filtrar resultados por valor, y default_sorting_field debe ser numérico. Crea una colección de productos:

curl -s -X POST http://127.0.0.1:8108/collections \
  -H "Content-Type: application/json" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -d '{
    "name": "productos",
    "fields": [
      {"name": "nombre",      "type": "string"},
      {"name": "descripcion", "type": "string"},
      {"name": "categoria",   "type": "string",   "facet": true},
      {"name": "marca",       "type": "string",   "facet": true},
      {"name": "etiquetas",   "type": "string[]", "facet": true},
      {"name": "precio",      "type": "float"},
      {"name": "stock",       "type": "int32"}
    ],
    "default_sorting_field": "stock"
  }' | jq '.name, .num_documents'
"productos"
0

No hace falta declarar el campo id: Typesense lo usa como identificador si viene en el documento y lo genera si no.

Paso 4: Indexar documentos

Para cargas masivas, el endpoint de importación acepta un documento JSON por línea (formato JSONL). Crea un fichero de ejemplo:

nano ~/productos.jsonl
{"id": "1", "nombre": "Camiseta básica de algodón", "descripcion": "Camiseta unisex de algodón 100 %", "categoria": "Ropa", "marca": "BasicWear", "etiquetas": ["ropa", "algodón"], "precio": 19.99, "stock": 150}
{"id": "2", "nombre": "Zapatillas de running", "descripcion": "Zapatillas ligeras con amortiguación", "categoria": "Calzado", "marca": "SpeedRun", "etiquetas": ["deporte", "running"], "precio": 89.99, "stock": 45}
{"id": "3", "nombre": "Pantalón vaquero slim", "descripcion": "Vaquero ajustado de corte moderno", "categoria": "Ropa", "marca": "DenimPro", "etiquetas": ["ropa", "vaquero"], "precio": 49.99, "stock": 80}
{"id": "4", "nombre": "Zapatillas de montaña", "descripcion": "Calzado impermeable para trail", "categoria": "Calzado", "marca": "TrailMax", "etiquetas": ["deporte", "montaña"], "precio": 119.00, "stock": 20}

Impórtalo. action=upsert crea los documentos nuevos y reemplaza los que ya existen con el mismo id, de modo que puedes repetir la importación sin errores:

curl -s -X POST "http://127.0.0.1:8108/collections/productos/documents/import?action=upsert" \
  -H "Content-Type: text/plain" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  --data-binary @"$HOME/productos.jsonl"
{"success":true}
{"success":true}
{"success":true}
{"success":true}

Typesense devuelve una línea por documento. Si alguna muestra "success":false, el campo error indica qué campo no cumple el esquema.

Para actualizar solo algunos campos de un documento, usa PATCH:

curl -s -X PATCH http://127.0.0.1:8108/collections/productos/documents/1 \
  -H "Content-Type: application/json" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -d '{"precio": 17.99}' | jq '.precio'
17.99

Paso 5: Buscar con filtros, orden y facetas

Una búsqueda indica el texto (q) y los campos en los que buscar (query_by). Prueba con una palabra mal escrita para ver la tolerancia a errores:

curl -s -G http://127.0.0.1:8108/collections/productos/documents/search \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  --data-urlencode "q=zapatilas" \
  --data-urlencode "query_by=nombre,descripcion" \
  | jq '.found, [.hits[].document.nombre]'
2
[
  "Zapatillas de running",
  "Zapatillas de montaña"
]

Ahora combina filtros, orden y facetas. filter_by restringe los resultados, sort_by los ordena y facet_by devuelve cuántos resultados hay por cada valor, que es lo que se usa para construir los filtros laterales de una tienda:

curl -s -G http://127.0.0.1:8108/collections/productos/documents/search \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  --data-urlencode "q=*" \
  --data-urlencode "query_by=nombre" \
  --data-urlencode "filter_by=precio:<100" \
  --data-urlencode "sort_by=precio:asc" \
  --data-urlencode "facet_by=categoria" \
  | jq '[.hits[].document | {nombre, precio}], .facet_counts[0].counts'
[
  { "nombre": "Camiseta básica de algodón", "precio": 17.99 },
  { "nombre": "Pantalón vaquero slim", "precio": 49.99 },
  { "nombre": "Zapatillas de running", "precio": 89.99 }
]
[
  { "count": 2, "highlighted": "Ropa", "value": "Ropa" },
  { "count": 1, "highlighted": "Calzado", "value": "Calzado" }
]

q=* devuelve todos los documentos, útil para listados filtrados. Para dar más peso a un campo que a otro, añade query_by_weights con un valor por cada campo de query_by.

Paso 6: Crear una clave de solo búsqueda

El navegador de tus usuarios nunca debe ver la clave de administración. Crea una clave que solo pueda buscar en la colección productos:

curl -s -X POST http://127.0.0.1:8108/keys \
  -H "Content-Type: application/json" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -d '{
    "description": "Búsqueda pública en productos",
    "actions": ["documents:search"],
    "collections": ["productos"]
  }' | jq -r '.value'

El comando imprime la clave nueva. Typesense solo la muestra en esta respuesta, así que guárdala. Comprueba que sirve para buscar pero no para administrar, sustituyendo your_search_key por su valor:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8108/collections \
  -H "X-TYPESENSE-API-KEY: your_search_key"
401

Es la clave que usarás en el frontend con el cliente oficial typesense de JavaScript o con el adaptador typesense-instantsearch-adapter.

Paso 7: Publicar la API con Nginx y HTTPS

Para que un frontend consulte Typesense desde el navegador, la API debe estar disponible por HTTPS. Instala Nginx y Certbot:

sudo apt install -y nginx certbot python3-certbot-nginx

Crea un sitio para el subdominio de búsqueda:

sudo nano /etc/nginx/sites-available/typesense
server {
    listen 80;
    server_name search.your_domain;

    location / {
        proxy_pass http://127.0.0.1:8108;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Actívalo, comprueba la sintaxis y recarga Nginx:

sudo ln -s /etc/nginx/sites-available/typesense /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Si usas UFW, permite HTTP y HTTPS (y SSH, antes de activarlo):

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Obtén el certificado de Let's Encrypt. Certbot modifica el sitio para servir HTTPS y redirigir HTTP:

sudo certbot --nginx -d search.your_domain

Comprueba el acceso desde fuera del servidor:

curl https://search.your_domain/health
{"ok":true}

El puerto 8108 sigue cerrado al exterior, porque Typesense solo escucha en 127.0.0.1.

Solución de problemas

El servicio no arranca. Revisa el journal y el log propio de Typesense:

sudo journalctl -u typesense-server -n 50 --no-pager
sudo tail -n 50 /var/log/typesense/typesense.log

Un error típico es un valor mal escrito en el .ini o un directorio de datos sin permisos después de moverlo.

Las peticiones devuelven 401 Forbidden - a valid x-typesense-api-key header must be sent. La cabecera falta o la clave no es correcta. Comprueba que echo "$TYPESENSE_API_KEY" muestra la misma clave que el .ini; la variable se pierde al cerrar la sesión.

Could not find a field named ... in the schema. El campo usado en query_by, filter_by o facet_by no está en el esquema, o no tiene "facet": true si lo usas en facet_by. Añade campos a una colección existente con PATCH /collections/productos.

El proceso consume mucha memoria o lo mata el OOM killer. El índice no cabe en la RAM. Indexa solo los campos que realmente buscas o filtras, o amplía la memoria del servidor.

Conclusión

Tienes Typesense funcionando en Ubuntu 24.04, con una colección de productos, búsquedas con tolerancia a errores, filtros y facetas, una clave pública limitada a búsquedas y la API publicada con HTTPS. Como siguientes pasos, conecta tu frontend con typesense-instantsearch-adapter, programa copias de seguridad con el endpoint de snapshots (POST /operations/snapshot) y, si necesitas alta disponibilidad, despliega un clúster de tres nodos siguiendo la guía de alta disponibilidad de Typesense.