Embed a Batch of Strings in One Call
You have a list of strings to embed. They can be documents to index, queries to compare, or passages to cluster. You want one gRPC call to return one vector per input, in order. You do not want to write per-string fetch loops or queue logic. This page walks through the bounded task. You send a batch and you read the response. Callers can then focus on what to do with the vectors. They do not need to think about how to fetch them.
For the full setup with architecture and connection details, see Embed Text Using a Shared Service.
Prerequisites
-
Complete the
Embed Text Using a Shared Service
guide. You installed
@forwardimpact/librpcand@forwardimpact/libtype. The embedding service runs.createClient("embedding")connects successfully.
Connect
import { createClient, createTracer } from "@forwardimpact/librpc";
import { createLogger } from "@forwardimpact/libtelemetry";
import { embedding } from "@forwardimpact/libtype";
const logger = createLogger("my-product");
const tracer = await createTracer("my-product");
const embeddingClient = await createClient("embedding", logger, tracer);
Embed a batch
Pass every input in a single EmbeddingsRequest:
const inputs = [
"Reset the database connection pool on each restart.",
"Pool restarts force every active query to reissue.",
"Coffee beans roast best at 215 degrees Celsius.",
];
const request = embedding.EmbeddingsRequest.fromObject({ input: inputs });
const result = await embeddingClient.CreateEmbeddings(request);
The response preserves order.
result.data[i] corresponds to inputs[i].
You can zip them back together, and you do not need to track IDs:
const pairs = inputs.map((text, i) => ({
text,
vector: result.data[i].values,
}));
Why batch in one call
The service issues one HTTP request to the TEI sidecar per gRPC
call, regardless of input length. One call to
CreateEmbeddings with 50 strings is faster than 50
calls with one string each. You avoid the per-call gRPC round trip
and the per-request TEI overhead. The TEI backend also batches
internally on the inference side.
Practical batch-size guidance:
-
For typical short text (titles, queries, log lines), batches of
32-128 strings move smoothly through the default
bge-small-en-v1.5model on a CPU host. - For long documents, split into smaller batches first. TEI imposes a per-request token limit. The default model enforces that limit at 512 tokens.
- For online queries that need low tail latency, send one input at a time, even though a batch would be more throughput-efficient. The round-trip cost is small at single-input size.
Handle a partial failure
The TEI backend either returns all vectors or fails the entire request. If the call throws, none of the vectors are usable. Retry the request, or split it if a specific input is the cause.
try {
const result = await embeddingClient.CreateEmbeddings(request);
return result.data;
} catch (err) {
// Whole batch failed. Retry or split inputs to isolate the offending one.
throw err;
}
The service does not re-run individual inputs to recover. That policy belongs in the caller, because it depends on the feature that uses the embeddings.
Verify
You reach the outcome of this guide when:
-
A single
CreateEmbeddingscall returns oneEmbeddingVectorper input in the request array, in the same order. - Batches in the 32-128 range complete in a single gRPC round trip and need no client-side queue.
- A whole-batch failure surfaces as a thrown error. It does not surface as partial data.