Qdrant
Lightweight, dependency-free, in-memory Qdrant HTTP REST fake for local parlel tests.
Default port: 6333
Quick Start
import { QdrantServer } from "../services/qdrant/src/server.js";
import { QdrantClient } from "@qdrant/js-client-rest";
const server = new QdrantServer(6333);
await server.start();
const client = new QdrantClient({ url: "http://127.0.0.1:6333", checkCompatibility: false });
await client.createCollection("items", { vectors: { size: 4, distance: "Cosine" } });
await client.upsert("items", {
wait: true,
points: [{ id: 1, vector: [1, 0, 0, 0], payload: { name: "demo" } }],
});
const hits = await client.search("items", { vector: [1, 0, 0, 0], limit: 1 });
await server.stop();
Implemented Operations
Service and health:
GET /GET /healthzGET /livezGET /readyzGET /metricsGET /telemetryGET /issuesDELETE /issues
Cluster:
GET /clusterGET /cluster/telemetryPOST /cluster/recoverDELETE /cluster/peer/{peer_id}
Collections and aliases:
GET /collectionsPUT /collections/{collection_name}GET /collections/{collection_name}PATCH /collections/{collection_name}DELETE /collections/{collection_name}GET /collections/{collection_name}/existsPOST /collections/aliasesGET /aliasesGET /collections/{collection_name}/aliases
Collection schema and vectors:
PUT /collections/{collection_name}/indexDELETE /collections/{collection_name}/index/{field_name}PUT /collections/{collection_name}/vectors/{vector_name}DELETE /collections/{collection_name}/vectors/{vector_name}GET /collections/{collection_name}/clusterPOST /collections/{collection_name}/clusterGET /collections/{collection_name}/optimizations
Points and payloads:
GET /collections/{collection_name}/points/{id}PUT /collections/{collection_name}/pointsPOST /collections/{collection_name}/pointsPOST /collections/{collection_name}/points/deletePUT /collections/{collection_name}/points/vectorsPOST /collections/{collection_name}/points/vectors/deletePUT /collections/{collection_name}/points/payloadPOST /collections/{collection_name}/points/payloadPOST /collections/{collection_name}/points/payload/deletePOST /collections/{collection_name}/points/payload/clearPOST /collections/{collection_name}/points/batch
Read, search, and query:
POST /collections/{collection_name}/points/scrollPOST /collections/{collection_name}/points/countPOST /collections/{collection_name}/points/searchPOST /collections/{collection_name}/points/search/batchPOST /collections/{collection_name}/points/search/groupsPOST /collections/{collection_name}/points/recommendPOST /collections/{collection_name}/points/recommend/batchPOST /collections/{collection_name}/points/recommend/groupsPOST /collections/{collection_name}/points/discoverPOST /collections/{collection_name}/points/discover/batchPOST /collections/{collection_name}/points/queryPOST /collections/{collection_name}/points/query/batchPOST /collections/{collection_name}/points/query/groupsPOST /collections/{collection_name}/facetPOST /collections/{collection_name}/points/search/matrix/pairsPOST /collections/{collection_name}/points/search/matrix/offsets
Snapshots:
GET /collections/{collection_name}/snapshotsPOST /collections/{collection_name}/snapshotsGET /collections/{collection_name}/snapshots/{snapshot_name}DELETE /collections/{collection_name}/snapshots/{snapshot_name}PUT /collections/{collection_name}/snapshots/recoverPOST /collections/{collection_name}/snapshots/uploadGET /snapshotsPOST /snapshotsGET /snapshots/{snapshot_name}DELETE /snapshots/{snapshot_name}
Shards:
GET /collections/{collection_name}/shardsPUT /collections/{collection_name}/shardsPOST /collections/{collection_name}/shards/deleteGET /collections/{collection_name}/shards/{shard_id}/snapshotGET /collections/{collection_name}/shards/{shard_id}/snapshotsPOST /collections/{collection_name}/shards/{shard_id}/snapshotsGET /collections/{collection_name}/shards/{shard_id}/snapshots/{snapshot_name}DELETE /collections/{collection_name}/shards/{shard_id}/snapshots/{snapshot_name}PUT /collections/{collection_name}/shards/{shard_id}/snapshots/recoverPOST /collections/{collection_name}/shards/{shard_id}/snapshots/upload
Supported Features
| Feature | Support | Notes |
|---|---|---|
| HTTP REST wire shape | Supported | Success responses use Qdrant-style { result, status: "ok", time }. |
@qdrant/js-client-rest high-level methods | Supported | Implemented for collection, point, payload, vector, query, search, recommend, discover, snapshot, shard, cluster telemetry, and alias methods. |
| Generated OpenAPI client endpoints | Supported where local | All generated local control-plane and data-plane routes are implemented or stubbed. |
| In-memory collections and points | Supported | State is process-local and reset with server.reset(). |
| Dense vector search | Supported | Cosine, dot, and euclidean scoring are approximated in memory. |
| Named vectors | Supported | Named vector metadata and point vector updates are stored in memory. |
| Payload filtering | Supported | must, should, must_not, min_should, match, range, values_count, has_id, is_empty, and is_null are supported. |
| Payload indexes | Supported as metadata | Index declarations are tracked but not used for performance. |
| Aliases | Supported | Create, delete, and rename alias actions are handled. |
| Snapshots | Stubbed | Metadata is stored, downloads return tiny placeholder content, recovery is a no-op success. |
| Shards and cluster | Stubbed | Single-node local responses only. No RAFT, replication, movement, or distributed persistence. |
| Authentication | Intentionally unsupported | api-key headers are accepted but not validated. |
| Persistence | Intentionally unsupported | Data is ephemeral and lost on stop() or reset(). |
| Real HNSW indexes and quantization | Intentionally unsupported | Config is accepted and returned, but search is linear scan. |
Error Shapes
Common success shape:
{
"result": {},
"status": "ok",
"time": 0.000123
}
Point read/search responses also include a lightweight usage object:
{
"result": [],
"status": "ok",
"time": 0.000123,
"usage": {
"cpu": 1,
"payload_io_read": 0,
"payload_io_write": 0,
"payload_index_io_read": 0,
"payload_index_io_write": 0,
"vector_io_read": 0,
"vector_io_write": 0
}
}
Common error shape:
{
"status": {
"error": "Collection books not found"
},
"time": 0.000123
}
Returned status codes:
| Status | When |
|---|---|
200 | Successful REST operation. |
204 | OPTIONS preflight. |
400 | Missing required fields such as field_name. |
404 | Unknown endpoint, missing collection, or missing point. |
405 | Known path with unsupported HTTP method. |
409 | Duplicate collection creation. |
500 | Unexpected server error. |
Configuration — test.env
Copy these into your test.env (used by the bridge sidecar flow). Tokens are Parlel's seeded test credentials — any non-empty value is accepted by the emulator, so you rarely need to change them. Swap in real credentials only when pointing at the live service in prod.env.
QDRANT_URL=http://parlel-bridge:6333
QDRANT_HOST=parlel-bridge:6333
QDRANT_PORT=6333
QDRANT_API_KEY=parlel
<!-- parlel:testenv:end -->