HTTP Gateway
The HTTP Gateway provides a RESTful HTTP/JSON interface to the Laurus search engine. It runs alongside the gRPC server and proxies requests internally:
Client (HTTP/JSON) --> HTTP Gateway (axum) --> gRPC Server (tonic) --> Engine
Enabling the HTTP Gateway
The gateway starts when http_port is configured:
# Via CLI argument
laurus serve --http-port 8080
# Via environment variable
LAURUS_HTTP_PORT=8080 laurus serve
# Via config file
laurus serve --config config.toml
# (set http_port in [server] section)
If http_port is not set, only the gRPC server starts.
Endpoints
| Method | Path | gRPC Method | Description |
|---|---|---|---|
| GET | /v1/health | HealthService/Check | Health check |
| POST | /v1/index | IndexService/CreateIndex | Create a new index |
| GET | /v1/index | IndexService/GetIndex | Get index statistics |
| GET | /v1/schema | IndexService/GetSchema | Get the index schema |
| PUT | /v1/documents/:id | DocumentService/PutDocument | Upsert a document |
| POST | /v1/documents/:id | DocumentService/AddDocument | Add a document (chunk) |
| GET | /v1/documents/:id | DocumentService/GetDocuments | Get documents by ID |
| DELETE | /v1/documents/:id | DocumentService/DeleteDocuments | Delete documents by ID |
| POST | /v1/commit | DocumentService/Commit | Commit pending changes |
| POST | /v1/search | SearchService/Search | Search (unary) |
| POST | /v1/search/stream | SearchService/SearchStream | Search (Server-Sent Events) |
API Examples
Health Check
curl http://localhost:8080/v1/health
Create an Index
curl -X POST http://localhost:8080/v1/index \
-H 'Content-Type: application/json' \
-d '{
"schema": {
"dynamic_field_policy": "dynamic",
"fields": {
"title": {"text": {"indexed": true, "stored": true, "term_vectors": true}},
"body": {"text": {"indexed": true, "stored": true, "term_vectors": true}}
},
"default_fields": ["title", "body"]
}
}'
The dynamic_field_policy key is optional. It controls how fields absent
from the schema are handled at ingest time. Accepted values: "strict",
"dynamic" (default), "ignore". See
Schema & Fields for the
full semantics and the warning about silent truncation under "dynamic".
Get Index Statistics
curl http://localhost:8080/v1/index
Get Schema
curl http://localhost:8080/v1/schema
Upsert a Document (PUT)
Replaces the document if it already exists:
curl -X PUT http://localhost:8080/v1/documents/doc1 \
-H 'Content-Type: application/json' \
-d '{
"document": {
"fields": {
"title": "Hello World",
"body": "This is a test document."
}
}
}'
Add a Document (POST)
Adds a new chunk without replacing existing documents with the same ID:
curl -X POST http://localhost:8080/v1/documents/doc1 \
-H 'Content-Type: application/json' \
-d '{
"document": {
"fields": {
"title": "Hello World",
"body": "This is a test document."
}
}
}'
Get Documents
curl http://localhost:8080/v1/documents/doc1
Delete Documents
curl -X DELETE http://localhost:8080/v1/documents/doc1
Commit
curl -X POST http://localhost:8080/v1/commit
Search
curl -X POST http://localhost:8080/v1/search \
-H 'Content-Type: application/json' \
-d '{"query": "body:test", "limit": 10}'
Search with Field Boosts
curl -X POST http://localhost:8080/v1/search \
-H 'Content-Type: application/json' \
-d '{
"query": "rust programming",
"limit": 10,
"field_boosts": {"title": 2.0}
}'
Hybrid Search
curl -X POST http://localhost:8080/v1/search \
-H 'Content-Type: application/json' \
-d '{
"query": "body:rust",
"query_vectors": [{"vector": [0.1, 0.2, 0.3], "weight": 1.0}],
"limit": 10,
"fusion": {"rrf": {"k": 60}}
}'
Streaming Search (SSE)
The /v1/search/stream endpoint returns results as Server-Sent Events (SSE). Each result is sent as a separate event:
curl -N -X POST http://localhost:8080/v1/search/stream \
-H 'Content-Type: application/json' \
-d '{"query": "body:test", "limit": 10}'
The response is a stream of SSE events:
data: {"id":"doc1","score":0.8532,"document":{...}}
data: {"id":"doc2","score":0.4210,"document":{...}}
JSON Field Value Inference
When the gateway accepts a document body (PUT /v1/documents/:id or
POST /v1/documents/:id), each value inside document.fields is converted
to the engine’s DataValue type using
the same inference rules as schema-less ingestion. This keeps the HTTP and
gRPC paths in sync.
| JSON value | Resulting field type | Notes |
|---|---|---|
null | (skipped) | The field is emitted as a NullValue and dropped during ingest. |
true / false | boolean | |
integer (fits in i64) | integer | |
| float / large integer | float | |
"text" | text | |
[1, 2, 3] (all integers) | integer with multi_valued: true | Multi-valued numeric field. |
[1.0, 2.5] (any non-integer number) | float with multi_valued: true | |
[] (empty array) | (skipped) | Element type cannot be determined, so the field is skipped. |
{"latitude": ..., "longitude": ...} | geo | |
{"lat": ..., "lon": ...} / {"lat": ..., "lng": ...} | geo | Short aliases for latitude / longitude are accepted. |
{"x": ..., "y": ..., "z": ...} | geo3d | All three keys required, finite numbers, ECEF meters. Mixing with lat/lon keys is rejected. |
The gateway returns an HTTP 400 (Bad Request) when:
- An array contains mixed types or non-numeric elements
(e.g.
[1, "x"]). - An object is not a valid geographic point (missing latitude / longitude
keys for 2D, or missing any of
x/y/zfor 3D). - A geographic latitude is outside
[-90, 90]or a longitude is outside[-180, 180]. - A 3D ECEF coordinate is non-finite (
NaN/Inf). - An object mixes 2D (
lat/lon) and 3D (x/y/z) keys.
Vector and bytes fields cannot be inferred from JSON alone and must be
declared in the schema. Numeric arrays sent against a declared vector
field are coerced to a vector of f32 values automatically, so REST
clients can post embeddings as plain JSON arrays.
3D Geographic Queries
3D ECEF queries reuse the lexical DSL string passed via query. The gateway forwards it unchanged to the engine, so the same forms work over HTTP as over gRPC:
curl -X POST http://localhost:8080/v1/search \
-H 'Content-Type: application/json' \
-d '{
"query": "position:geo3d_distance(-3955182, 3350553, 3700276, 5000)",
"limit": 10
}'
See Query DSL → 3D Geographic Queries for geo3d_bbox and geo3d_nearest syntax.
Request/Response Format
All request and response bodies use JSON. The JSON structure mirrors the gRPC protobuf messages. See gRPC API Reference for the full message definitions.