Skip to content

WebAssembly SDK

qql-wasm makes the parser and planner available without a server round-trip. Use analyze for editor feedback and offline route inspection; construct a Client only when your application is ready to call a Qdrant REST endpoint.

Install
npm install qql-wasm
QQLA browser-safe query sourceTry in playground
QUERY TEXT 'browser retrieval'
FROM docs
USING dense
LIMIT 5;

Initialize once per JavaScript realm. analyze returns strict validity, tokens, the AST, compiled routes, an explanation, and a structured error with byte offsets.

import init, { analyze, isValid, parse } from "qql-wasm";
await init();
const info = analyze(query);
if (!info.valid) {
console.error(info.error.code, info.error.message);
} else {
console.log(info.routes, info.explain);
}
console.log(isValid(query));
console.log(parse(query));

The analyze result is one object with every diagnostic and compiled route in a single pass:

interface AnalysisResult {
valid: boolean;
statements_count: number;
tokens: Token[]; // [{ kind, text, pos, end, len }]
ast: unknown[] | null; // parsed statements, null when invalid
route: CompiledRoute | null; // first statement's route
routes: CompiledRoute[]; // every statement's route
explain: string | null; // human-readable plan, null when invalid
error: AnalysisError | null; // null when valid
}
interface Token {
kind: string;
text: string;
pos: number; // start byte offset
end: number; // end byte offset
len: number;
}
interface CompiledRoute {
stmt_type: string;
method: string;
path: string;
payload: unknown | null;
}
interface AnalysisError {
code: string;
message: string;
start: number | null;
end: number | null;
}

route is the compiled route of the first statement; routes lists every statement in a script. error carries the validation code plus byte offsets into the source for editor underlining.

When a route or explanation must cross a worker or process boundary, use the byte variants instead of building JavaScript objects. Both are safe, JS-owned Uint8Array buffers that transfer to a Worker or over IPC with zero JS-object overhead.

import init, { compileBytes, explainBytes } from "qql-wasm";
await init();
// JSON-encoded CompiledRoute.
const routeBytes = compileBytes(query);
worker.postMessage(routeBytes, [routeBytes.buffer]);
// UTF-8 explain text.
const explainBytesUtf8 = explainBytes(query);
const explainText = new TextDecoder().decode(explainBytesUtf8);

new Stmt(source) parses exactly one statement and returns an owned handle. Mutate the handle locally — inject a trusted filter, assign routing — then compile or execute it. The class is a JavaScript realm object; the parsed AST stays in WASM memory.

import init, { Stmt } from "qql-wasm";
await init();
const stmt = new Stmt(query);
// Trusted predicate injection (void; throws on invalid operator or value).
stmt.injectFilter("tenant_id", "=", "acme");
// Routing only — never a security boundary.
stmt.shardKey = "acme";
console.log(stmt.shardKey);
const route = stmt.compileRoute(); // { stmt_type, method, path, payload }
const astJson = stmt.toJSON(); // string
const astObject = stmt.toObject(); // plain JS object
// Transferable byte compile.
const routeBytes = stmt.compileRouteBytes();
stmt.free(); // or use `using stmt = new Stmt(query)` with [Symbol.dispose]

Methods:

MemberReturnsUse it for
new Stmt(source)owned handleParse exactly one statement
injectFilter(field, op, value)voidInject a trusted predicate (mutates in place)
shardKey getter / setterstring or nullAssign or read QQL SHARD routing
toJSON()stringSerialize the AST to JSON
toObject()objectSerialize the AST to a JS object
compileRoute(){stmt_type, method, path, payload}Compile this statement to a Qdrant REST route
compileRouteBytes()Uint8ArrayTransferable route compile
free() / [Symbol.dispose]()Release WASM memory (using works in TypeScript)

The WASM client is REST/fetch only. new Client(url?, apiKey?) takes the Qdrant REST URL and an optional API key; both default to null, and the URL defaults to http://localhost:6333.

import { Client } from "qql-wasm";
const client = new Client("http://localhost:6333", null);
try {
const report = await client.execute(query);
console.log(report.ok, report.results);
} finally {
client.free();
}

execute(query | string[], options) accepts a single statement, a semicolon-delimited script, or an array of independent sources. options.onError is "stop" (default) or "continue". executeStmt(stmt) runs a pre-parsed Stmt handle. compile(query) and explain(query) inspect a route or plan without touching the network.

Before executing, the client fetches the collection topology to resolve USING vector roles, then embeds text through the configured embedder. Execution is REST-only — there is no gRPC or edge backend in WASM.

Use an OpenAI-compatible HTTP endpoint or supply a JavaScript callback. The callback receives a batch of strings and returns one dense vector per string.

import { Client } from "qql-wasm";
const client = new Client("http://localhost:6333", null);
client.setEmbedder(async (texts) => {
const response = await fetch("/api/embed", {
method: "POST",
body: JSON.stringify({ texts }),
});
return response.json();
});
// Or configure a hosted OpenAI-compatible endpoint.
client.setHttpEmbedder(
"https://embeddings.example.com/v1/embeddings",
"text-embedding-3-small",
1536,
null,
);
client.hasEmbedder(); // true

setEmbedder(fn) takes a JS callback (texts: string[]) => Promise<number[][]> — compatible with Transformers.js pipelines and @huggingface/transformers. The callback is called once with the full batch, so prefer a model that embeds batches. setHttpEmbedder(endpoint, model, dimension, apiKey?) configures any OpenAI-compatible {"model", "input": [...]} endpoint and sends the whole batch in one request. setRemoteEmbedder is an alias with the same signature. hasEmbedder() reports whether either is configured.

Every Client and Stmt owns WASM allocations. Call free() when a handle is no longer needed, especially in long-lived pages or when replacing connection settings. Stmt also implements [Symbol.dispose], so using in TypeScript releases it automatically.

APIReturnsUse it for
init / initSyncinitialized WASM moduleOne-time module initialization
parse, isValid, tokenizeAST values, boolean, token listStrict local parser primitives
analyze(source)validity, tokens, AST, routes, explain, errorEditors and offline diagnostics
compile, explain, inject_filterroute, text, rewritten ASTOne-shot offline planning helpers
compileBytes, explainBytesUint8ArrayTransferable worker or IPC payloads
new Stmt(source)owned statement handleinjectFilter, route compile, and shard assignment
Stmt.injectFilter, Stmt.shardKeymutated statement handleTrusted filtering and optional routing
Stmt.toObject, toJSON, compileRouteAST or routeInspect a single owned statement
Stmt.compileRouteBytesUint8ArrayTransferable single-statement route
new Client, execute, executeStmtexecution report promiseDirect browser-to-Qdrant REST calls
Client.compile, Client.explainroute, plan textInspect work before sending traffic
setEmbedder, setHttpEmbedder, hasEmbedderconfigured clientResolve text input from JavaScript or HTTP
Client.setRemoteEmbedderconfigured clientAlias for setHttpEmbedder
free() / [Symbol.dispose]released WASM allocationDispose Client and Stmt handles