From 1696bb4ad3facf6f283ed328f20a4aed12500244 Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Tue, 22 Sep 2026 13:50:54 +0200
Subject: [PATCH 1/7] docs: add GraphQL migration guide (unlisted) and
TypeScript web client page
- unlisted migration guide at weaviate/api/graphql/migration (no sidebar entry, noindex, out of sitemap)
- new client-libraries/typescript/web-client child page; TS index and intro partial link it
- api/grpc.md: the TS web client connects through gRPC-Web
- suppress the unlisted banner while keeping the noindex meta
---
_includes/clients/ts-client-intro.mdx | 2 +-
docs/weaviate/api/graphql/migration.mdx | 241 ++++++++++++++++++
docs/weaviate/api/grpc.md | 2 +-
.../client-libraries/typescript/index.mdx | 6 +
.../typescript/web-client.mdx | 67 +++++
sidebars.js | 1 +
src/theme/ContentVisibility/Unlisted/index.js | 21 ++
7 files changed, 338 insertions(+), 2 deletions(-)
create mode 100644 docs/weaviate/api/graphql/migration.mdx
create mode 100644 docs/weaviate/client-libraries/typescript/web-client.mdx
create mode 100644 src/theme/ContentVisibility/Unlisted/index.js
diff --git a/_includes/clients/ts-client-intro.mdx b/_includes/clients/ts-client-intro.mdx
index 4f2ef7ab7..714f4e601 100644
--- a/_includes/clients/ts-client-intro.mdx
+++ b/_includes/clients/ts-client-intro.mdx
@@ -4,6 +4,6 @@ The **v3 client** is the current TypeScript client. If you have code written for
:::note
-The v3 client supports server side development (Node.js hosted). If your application is browser based, you might consider using the [TypeScript client v2](/weaviate/client-libraries/typescript#javascripttypescript-client-v2-deprecation). Keep in mind that the v2 client is outdated and no longer officially maintained.
+The v3 client runs on the server (Node.js hosted). For browsers and edge runtimes, use the [web client](/weaviate/client-libraries/typescript/web-client), which is currently an alpha.
:::
diff --git a/docs/weaviate/api/graphql/migration.mdx b/docs/weaviate/api/graphql/migration.mdx
new file mode 100644
index 000000000..ea12b5c5c
--- /dev/null
+++ b/docs/weaviate/api/graphql/migration.mdx
@@ -0,0 +1,241 @@
+---
+title: Migrating from the GraphQL API
+description: "How to express your existing GraphQL Get and Aggregate queries with the Weaviate client libraries, the browser client, or the REST Search API."
+image: og/docs/api.jpg
+unlisted: true
+---
+
+import SkipLink from '/src/components/SkipValidationLink'
+
+## Who this guide is for
+
+This guide is for developers who have working GraphQL `Get` and `Aggregate` queries and want to express the same searches through one of Weaviate's other query APIs. Typical reasons to be here:
+
+- You are moving an application to a **new Weaviate Cloud cluster**, which is created with **GraphQL disabled**.
+- You are building in the **browser** or on an edge runtime, where a plain gRPC client cannot run.
+- You maintain an **HTTP-only stack** — a no-code or BI tool, or a language without an official Weaviate client, such as PHP or Ruby.
+
+Start at the top of this table and take the first row that describes your stack.
+
+| Your application | Recommended path | Why |
+| --- | --- | --- |
+| **Server or container app** | **[Client libraries](#client-libraries-recommended)** | For Python, JavaScript/TypeScript on Node, Java, and C#. Covers every `Get` and `Aggregate` construct and gives you typed results and generated request objects. The recommended path, and the right one for the large majority of applications. |
+| **Browser or edge runtime** | **[TypeScript client for web](#typescript-client-for-web)** | For the browser and runtimes such as Cloudflare Workers or Vercel. The same client API, carried over gRPC-Web so it works where plain gRPC cannot. Currently an alpha release. |
+| **No client library available** | **[REST Search API](#rest-search-api)** | For a no-code or BI platform, or a language with no official client such as PHP or Ruby. Plain-HTTP JSON endpoints whose request bodies mirror your GraphQL queries closely.
+
+:::info Availability at a glance
+The three paths are at three different maturity levels. Check the **Status** column before you commit to one.
+:::
+
+| Path | Status | Availability |
+| --- | --- | --- |
+| **Official client libraries** | Generally available | Python, JavaScript/TypeScript, Java, and C# are generally available while the Go client v6 is currently in [beta release](https://github.com/weaviate/weaviate-go-client/releases/tag/v6.0.0-beta.3). Earlier versions of the Go client rely on GraphQL. |
+| **`@weaviate/web`** (browser and edge) | **Alpha** | `3.15.0-alpha.2`. Requires Weaviate `v1.38.3` or later, and an [install workaround](../../client-libraries/typescript/web-client.mdx). |
+| **REST Search API** | **Preview** | From `v1.39` available when [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) is set to `true`. Enabled by default from `v1.40`. See [REST Search API](#rest-search-api). |
+
+## Client libraries (recommended)
+
+The client libraries cover every `Get` and `Aggregate` construct shown on this page, so each one has a direct equivalent. They query over gRPC, and give you typed results and generated request objects.
+
+:::info The Go client and GraphQL
+The current Go client builds its queries as GraphQL under the hood. The upcoming v6 client replaces that with gRPC.
+:::
+
+### GraphQL syntax mapping
+
+| GraphQL | Client concept | How-to |
+| --- | --- | --- |
+| `Get { Collection { ... } }` | Fetch objects from a collection | [Search patterns and basics](../../search/basics.md#list-objects) |
+| `nearText` | Near-text search | [Search with text](../../search/similarity.md#search-with-text) |
+| `nearObject` | Near-object search | [Search with an existing object](../../search/similarity.md#search-with-an-existing-object) |
+| `nearVector` | Near-vector search — pass your own query vector | [Search with a vector](../../search/similarity.md#search-with-a-vector) |
+| `nearImage` and the other media operators | Multimodal search | [Search with image](../../search/similarity.md#search-with-image) |
+| `bm25` | Keyword search, including search operators and property boosts | [Keyword search](../../search/bm25.md) |
+| `hybrid` | Hybrid search, including `alpha`, fusion type, and search vectors | [Hybrid search](../../search/hybrid.md) |
+| `where` | Filters, built with the client's filter helpers | [Filters](../../search/filters.md) |
+| `limit`, `offset` | `limit` and `offset` arguments | [Paginate with limit and offset](../../search/basics.md#paginate-with-limit-and-offset) |
+| `after` (cursor) | Cursor-based iteration over all objects | [Cursor with `after`](./additional-operators.md#cursor-with-after) |
+| `autocut` | `auto_limit` / `autoLimit` | [Limit result groups](../../search/similarity.md#limit-result-groups) |
+| `sort` | Sorting on object retrieval. Sorting is not combined with a search operator — see [sorting considerations](./additional-operators.md#sorting-considerations) | [Sorting](./additional-operators.md#sorting) |
+| `_additional { ... }` | `return_metadata` / `returnMetadata`, returned as a metadata object on each result | [Retrieve metadata values](../../search/basics.md#retrieve-metadata-values) |
+| `_additional { vector }` | `include_vector` / `returnVectors` | [Retrieve the object vector](../../search/basics.md#retrieve-the-object-vector) |
+| `groupBy` | Grouped results | [Group results](../../search/similarity.md#group-results) |
+| `Aggregate { ... }` | The collection's aggregate API, including per-property statistics | [Aggregate data](../../search/aggregate.md) |
+| `_additional { generate { ... } }` | Retrieval augmented generation (single prompt and grouped task) | [Retrieval Augmented Generation (RAG)](../../search/generative.md) |
+| Cross-reference selection (`... on Target { ... }`) | `return_references` / `returnReferences` | [Retrieve cross-referenced properties](../../search/basics.md#retrieve-cross-referenced-properties) |
+| `tenant` | A tenant-scoped collection handle | [Multi-tenancy](../../search/basics.md#multi-tenancy) |
+| `consistencyLevel` | A consistency-level-scoped collection handle | [Replication](../../search/basics.md#replication) |
+
+## TypeScript client for web
+
+If your application runs in a browser or on an edge runtime such as Cloudflare Workers or Vercel, a plain gRPC client cannot run there. `@weaviate/web` is a browser and edge build of the v3 TypeScript client that carries the query path over **gRPC-Web**, so you write the same client code you would on Node against a single HTTP(S) endpoint.
+
+:::caution Alpha
+`@weaviate/web` is an alpha release and is not recommended for production use yet.
+:::
+
+We exercised the alpha against Weaviate `v1.38.3`, `v1.39.4`, `v1.39.5`, and `v1.40.0-rc.1`, with identical results on each apart from one cosmetic difference in self-distance on `v1.38.3`. Connecting, `fetchObjects` with filters, `bm25`, `hybrid`, `nearVector`, `nearObject`, metadata and vector selection, sorting, paging, aggregation, multi-tenancy, the write methods, and API-key authentication all work. `generate` (RAG) needs a generative module on the server, exactly as it does elsewhere.
+
+Install steps, the server version floor and the browser notes are on the [web client page](../../client-libraries/typescript/web-client.mdx). The alpha needs an install workaround, so start there.
+
+## REST Search API
+
+:::caution Preview
+
+The REST Search API is a **preview** feature.
+
+In **`v1.39`** they are available when [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) is set to `true`. They are enabled by default from **`v1.40`**.
+
+:::
+
+The REST Search API is a set of plain-HTTP JSON endpoints intended for stacks that cannot run a client library.
+Request and response field names may still change, so this path is not recommended for production use yet. It is a deliberate **subset** of what GraphQL offers. Read [Differences to check before you convert](#differences-to-check-before-you-convert) and [Queries without a REST equivalent yet](#queries-without-a-rest-equivalent-yet) before you convert anything.
+
+There are five available `POST` endpoints:
+
+| Endpoint | GraphQL equivalent |
+| --- | --- |
+| `POST /v1/search/{collection}/near-text` | `Get` with `nearText` |
+| `POST /v1/search/{collection}/bm25` | `Get` with `bm25` |
+| `POST /v1/search/{collection}/hybrid` | `Get` with `hybrid` |
+| `POST /v1/search/{collection}/near-object` | `Get` with `nearObject` |
+| `POST /v1/aggregate/{collection}` | `Aggregate` |
+
+### An example request
+
+The GraphQL query you are converting from is documented in the [GraphQL reference](./get.md). Its REST equivalent looks like this:
+
+**REST Search API** — `POST /v1/search/Movie/near-text`
+
+```json
+{
+ "query": ["space opera"],
+ "limit": 5,
+ "where": {
+ "operator": "And",
+ "operands": [
+ { "path": ["year"], "operator": "GreaterThanEqual", "valueInt": 1980 },
+ { "path": ["category"], "operator": "Equal", "valueText": "scifi" }
+ ]
+ },
+ "returnProperties": ["title", "year"],
+ "returnMetadata": ["distance"]
+}
+```
+
+The `where` value is the **exact same JSON** you pass in GraphQL — the same 15 operators, the same value fields, the same cross-reference paths, parsed by the same code. There is no new filter language to learn.
+
+The response is a flat envelope rather than the GraphQL `data.Get.` nesting:
+
+```json
+{
+ "results": [
+ {
+ "id": "00000000-0000-0000-0000-000000000000",
+ "properties": { "title": "Dune", "year": 2021 },
+ "metadata": { "distance": 0.12 }
+ }
+ ],
+ "tookMs": 8
+}
+```
+
+### Differences to check before you convert
+
+These are the conversions most likely to cost you time. Some return a perfectly successful response carrying a different answer from the one GraphQL gave; others change the way a failure reaches your code. The silent ones are the dangerous ones. Read this list before the feature gaps below.
+
+
+ What changes, and what to do about it
+
+| Difference | What happens | What to do |
+| --- | --- | --- |
+| **Default `limit`** | GraphQL `Get` falls back to `QUERY_DEFAULTS_LIMIT_GRAPHQL` (`100` by default). The REST endpoints fall back to `QUERY_DEFAULTS_LIMIT` (`10` by default) and never reach the GraphQL value. A query you convert without an explicit `limit` silently returns far fewer rows. | **Always set `limit` explicitly.** This is the single most common silent difference. |
+| **Unknown body fields are ignored** | The endpoints accept and discard fields they do not recognize, for platform parity with the rest of the REST API. A typo, or a field borrowed from GraphQL, produces a successful response that ignored your intent. | Check every field name against the tables on this page. |
+| **`bm25.searchOperator`** | There is no REST field for the keyword search operator (`And`, `Or`, `AndCross`) or `minimumOrTokensMatch`. Supplying one is ignored, so the search runs with the default operator. | Use a [client library](#client-libraries-recommended) if your results depend on the search operator. |
+| **`Get`'s `group` argument** | The vector-grouping `group` argument has no REST field and raises no error. Results come back **ungrouped**. | Use a [client library](#client-libraries-recommended) for grouped results. |
+| **A body field named `generate`** | Silently ignored, and the response contains no generated text. The reserved `singlePrompt` and `groupedTask` fields do return `422`. | See [Queries without a REST equivalent yet](#queries-without-a-rest-equivalent-yet). |
+| **`certainty` on a `bm25` search** | Accepted in `returnMetadata`, but certainty cannot be computed for a keyword search, so the field is simply absent from the response. | Request `score` instead for keyword searches. |
+| **`int64` precision** | Integers travel as JSON numbers and are handled as `float64`, so values beyond 2^53 lose precision. | For large integers, use a client library, or store the value as text. |
+| **`_additional` is renamed** | Retrieval metadata arrives under a **`metadata`** key, not `_additional`. Properties and cross-references are also split into their own `properties` and `references` objects rather than sitting flat on the result, and the object `id` is a top-level envelope field. | Update your response parsing. Note `id` is **not** a valid `returnMetadata` entry — supplying it returns `422`. |
+| **Error signalling** | GraphQL returns HTTP `200` with an `errors[]` array. The REST endpoints use real HTTP status codes (`400`, `401`, `403`, `404`, `422`, `429`, `500`, `503`) with a body of the form `{"error":[{"message":"..."}]}`. | Check the status code, not just the body. Client code that only inspected `errors[]` will treat failures as successes. |
+| **Embedding-provider failures** | When the vectorizer module fails, the response is `500` — never `502` — with a deliberately short message; the provider's own response goes to the server log, because it can quote credentials. | Read the server log for provider errors; do not parse the client-facing body. |
+| **The same validation, surfaced differently** | A malformed `where` filter — `And`/`Or` with no operands, a `Not` with more than one, a non-RFC3339 `valueDate`, a `ContainsAny`/`ContainsAll`/`ContainsNone` without an array value, or `IsNull` without `indexNullState` — is rejected by **both** APIs, with the same underlying message. GraphQL returns it inside `errors[]` under HTTP `200`; here it arrives as a status code. Only the missing-operands case is caught at the API layer and returns `400`; the other four are rejected deeper in the engine and return **`500`**. | Do not treat these `500`s as transient server trouble: a retry or alerting rule keyed on `5xx` will retry forever and page someone. Read the message before retrying. |
+| **`fusionType` values differ** | GraphQL's `rankedFusion` and `relativeScoreFusion` are `ranked` and `relativeScore` here. A copied GraphQL value returns `422`. | Translate the value; see the cheat sheet below. |
+
+
+
+### Queries without a REST equivalent yet
+
+These capabilities are not part of the preview. They fail in two very different ways, so check the status column before you port anything: some are **reserved** and return `422` or `400`, which shows the gap in your logs; others are simply **not recognized**, and the request returns `200` with the capability having had no effect at all. The silent ones are the ones to watch. If you need one of these, please [open a GitHub issue](https://github.com/weaviate/weaviate/issues).
+
+
+ What is missing, and what each returns
+
+| GraphQL capability | REST status | Path |
+| --- | --- | --- |
+| **`nearVector`** — supplying your own query vector | **No route exists.** There is no `near-vector` endpoint, so the request fails outright. | Any collection configured with `vectorizer: none`, or any workload that computes embeddings client-side, needs a [client library](#client-libraries-recommended) or the [web client](#typescript-client-for-web). |
+| **`hybrid.vector`** — supplying the vector leg of a hybrid search | The `hybrid` route exists but has no `vector` field, so the value is **silently ignored** (`200`) and the search runs against a server-computed vector instead of yours. | [Client libraries](#client-libraries-recommended) or the [web client](#typescript-client-for-web). |
+| **`Aggregate` property statistics** — `sum`, `mean`, `median`, `mode`, `minimum`, `maximum`, `topOccurrences`, `totalTrue`, and the rest | Aggregate is **count-only**. A `property:statistic` entry in `returnMetrics` returns `422`. | [Client libraries](#client-libraries-recommended) |
+| **Aggregate over a search** — `over` and `objectLimit` | Reserved; returns `422`. | [Client libraries](#client-libraries-recommended) |
+| **RAG** — `generate(singleResult:)` and `generate(groupedResult:)` | `singlePrompt` and `groupedTask` are reserved and return `422`. | [Client libraries](#client-libraries-recommended) |
+| **Multiple target vectors** — `targetVectors` and `targets` | `targetVector` takes a **single string**. On `near-text`, `near-object`, and `hybrid` an array returns `400`. On `bm25` there is no `targetVector` field at all, so any value — string or array — is **silently ignored** (`200`) through the [unknown-field path](#differences-to-check-before-you-convert). Multi-target weighting is not available. | [Client libraries](#client-libraries-recommended) |
+| **Media search** — `nearImage`, `nearAudio`, `nearVideo`, `nearDepth`, `nearThermal`, `nearImu` | No routes. | [Client libraries](#client-libraries-recommended) |
+| **`groupBy` in `Get`** | Reserved, but the status depends on the shape you send. The GraphQL-shaped object (`{path, groups, objectsPerGroup}`) — the form you are most likely to port — returns **`400`**, because the reserved field is typed as a string. A bare string returns `422` "groupBy is not yet supported". | [Client libraries](#client-libraries-recommended) |
+| **`rerank`** | Reserved; returns `422`. | [Client libraries](#client-libraries-recommended) |
+| **`sort`** and the `after` cursor | Not fields on the search endpoints, so both are **silently ignored** (`200`, with the result set unchanged and unsorted). Sorting is not combined with search operators — see [sorting considerations](./additional-operators.md#sorting-considerations) — and both remain available on `GET /v1/objects`. | [Client libraries](#client-libraries-recommended), or `GET /v1/objects` |
+| **`nearText` concept steering** — `moveTo`, `moveAwayFrom`, `autocorrect` | Not fields on the `near-text` route, so all three are **silently ignored** (`200`, with the result set unchanged and unsteered). | [Client libraries](#client-libraries-recommended) |
+| **Vectors in the response** — `_additional { vector }` | Vectors are never returned by these endpoints. | [Client libraries](#client-libraries-recommended), or `GET /v1/objects` |
+
+
+
+### GraphQL to REST cheat sheet
+
+Body fields are **camelCase**. Field names not listed here are not recognized, and unrecognized fields are ignored rather than rejected.
+
+
+ GraphQL to REST field map
+
+| GraphQL | REST request body |
+| --- | --- |
+| `nearText: { concepts: ["x", "y"] }` | `POST /v1/search/{c}/near-text` with `"query": ["x", "y"]` — always an array, even for one concept |
+| `nearText: { certainty: 0.7 }` or `{ distance: 0.3 }` | `"certainty": 0.7` or `"distance": 0.3` — one or the other, not both |
+| `bm25: { query: "x", properties: ["title^2"] }` | `POST /v1/search/{c}/bm25` with `"query": "x", "queryProperties": ["title^2"]` |
+| `hybrid: { query: "x", alpha: 0.5 }` | `POST /v1/search/{c}/hybrid` with `"query": "x", "alpha": 0.5` |
+| `hybrid: { fusionType: rankedFusion }` | `"fusionType": "ranked"` |
+| `hybrid: { fusionType: relativeScoreFusion }` | `"fusionType": "relativeScore"` |
+| `hybrid: { maxVectorDistance: 0.4 }` | `"maxVectorDistance": 0.4` — requires `alpha` greater than `0`, otherwise `400` |
+| `nearObject: { id: "" }` | `POST /v1/search/{c}/near-object` with `"id": ""` — required, and must be a well-formed UUID |
+| `targetVectors: ["title_vector"]` | `"targetVector": "title_vector"` — a single string |
+| `where: { ...WhereFilter... }` | `"where": { ...the same JSON... }` |
+| `limit: 5, offset: 10` | `"limit": 5, "offset": 10` |
+| `autocut: 1` | `"autoLimit": 1` |
+| A property selection such as `title year` | `"returnProperties": ["title", "year"]` — omit the field for all non-reference properties, or pass `[]` for none |
+| `hasAuthor { ... on Author { name } }` | `"returnReferences": [{ "linkOn": "hasAuthor", "targetCollection": "Author", "returnProperties": ["name"] }]` — dotted paths in `returnProperties` return `400` |
+| `_additional { distance score explainScore }` | `"returnMetadata": ["distance", "score", "explainScore"]` — response key is `metadata` |
+| `_additional { creationTimeUnix lastUpdateTimeUnix }` | `"returnMetadata": ["creationTime", "lastUpdateTime"]` |
+| `_additional { id }` | Nothing to request — `id` is always returned in the envelope. Passing `"id"` in `returnMetadata` returns `422` |
+| `tenant: "tenantA"` | `"tenant": "tenantA"` |
+| `consistencyLevel: ONE` | `"consistencyLevel": "ONE"` — uppercase only; `"one"` returns `422` |
+| `Aggregate { C(groupBy: ["category"]) { meta { count } } }` | `POST /v1/aggregate/{c}` with `"groupBy": "category", "returnMetrics": ["count"]` — a **bare property name**, not a path; a dotted value returns `422` |
+| `Aggregate { C { meta { count } } }` | `POST /v1/aggregate/{c}` with an empty body, or `"returnMetrics": ["count"]` |
+
+One more request limit worth knowing: in `v1.39`, `limit`, `offset`, and their sum are each capped by `QUERY_MAXIMUM_RESULTS`, and exceeding any of them returns `400`. A request body size cap, returning `413`, is not in `v1.39`; it is planned for `v1.40`.
+
+
+
+## Related pages
+
+- [Search (GraphQL | gRPC)](./index.md) — the GraphQL and gRPC search API reference
+- [gRPC-Web](../grpc.md#grpc-web) — the browser-reachable gRPC interface
+- [Client libraries](../../client-libraries/index.mdx) — installation and connection for each language
+- [Web client](../../client-libraries/typescript/web-client.mdx) — install steps and browser notes for `@weaviate/web`
+- [Search patterns and basics](../../search/basics.md) — the how-to family this guide links throughout
+- [Connect to a cluster](/cloud/manage-clusters/connect.mdx) — where to find your Weaviate Cloud endpoint URLs
+
+## Questions and feedback
+
+All three paths are actively being developed, and feedback on this guide — especially a query you could not convert — directly shapes what gets built next.
+
+import DocsFeedback from '/\_includes/docs-feedback.mdx';
+
+
diff --git a/docs/weaviate/api/grpc.md b/docs/weaviate/api/grpc.md
index ccba6f0ea..f0ea87e96 100644
--- a/docs/weaviate/api/grpc.md
+++ b/docs/weaviate/api/grpc.md
@@ -64,7 +64,7 @@ The gRPC-Web interface is enabled by default. **[Runtime configuration](/deploy/
This setting has no environment variable equivalent. When the interface is disabled, requests to `/v1/grpc-web/` fall through to the REST handler, so other REST endpoints keep working as usual.
-The Weaviate client libraries connect over plain gRPC, so they do not use the gRPC-Web interface yet.
+The TypeScript [web client](../client-libraries/typescript/web-client.mdx) connects through gRPC-Web. The other client libraries connect over plain gRPC.
## Questions and feedback
diff --git a/docs/weaviate/client-libraries/typescript/index.mdx b/docs/weaviate/client-libraries/typescript/index.mdx
index b797ed129..f2550fdaf 100644
--- a/docs/weaviate/client-libraries/typescript/index.mdx
+++ b/docs/weaviate/client-libraries/typescript/index.mdx
@@ -125,6 +125,12 @@ All client v3 methods, with the exception of `collection.use()`, use ES6 Promise
When there is an asynchronous code error, a promise returns the specific error message. If you use `async` and `await`, a rejected promises acts like a thrown exception
+## Web client (`@weaviate/web`)
+
+`@weaviate/web` is a browser build of the v3 client, for browsers and edge runtimes where a plain gRPC connection is not possible. It has the same API and talks to Weaviate over gRPC-Web on the HTTPS port. It is an alpha.
+
+For install steps and browser notes, see [Web client](./web-client.mdx).
+
## Releases
Go to the [GitHub releases page](https://github.com/weaviate/typescript-client/releases) to see the history of the TypeScript client library releases and change logs.
diff --git a/docs/weaviate/client-libraries/typescript/web-client.mdx b/docs/weaviate/client-libraries/typescript/web-client.mdx
new file mode 100644
index 000000000..6e43acbb0
--- /dev/null
+++ b/docs/weaviate/client-libraries/typescript/web-client.mdx
@@ -0,0 +1,67 @@
+---
+title: Web client
+sidebar_position: 3
+description: "The @weaviate/web package: a browser build of the Weaviate v3 TypeScript client that connects over gRPC-Web."
+image: og/docs/client-libraries.jpg
+---
+
+{/* MAINTENANCE: `3.15.0-alpha.2` is hardcoded in 3 places — the Alpha admonition and the
+ install command below, plus the availability table in weaviate/api/graphql/migration.mdx.
+ Update all 3 together when the package moves. */}
+
+`@weaviate/web` is a browser build of the v3 client. Use it in browsers and edge runtimes such as Cloudflare Workers and Vercel, where a plain gRPC connection is not possible.
+
+It has the same API as `weaviate-client`, but sends queries over gRPC-Web. Connect options take one HTTP(S) endpoint. There is no gRPC host or port to set, because Weaviate serves gRPC-Web on the REST port.
+
+Use a `connectTo*` helper. The low-level `weaviate.client(params)` entry point does not add the `/v1/grpc-web` prefix. `grpcHost`, `grpcPort` and `grpcSecure` are not connect options. If you pass them, they are ignored.
+
+:::caution Alpha
+
+`@weaviate/web` `3.15.0-alpha.2` is an alpha. The `latest` tag still points at `3.15.0-alpha.1`, an older alpha that cannot resolve its own dependencies, so always install the version explicitly. The API may change. Do not use it in production yet.
+
+:::
+
+## Install
+
+Install the alpha explicitly, together with `protobufjs`:
+
+```bash
+npm install @weaviate/web@3.15.0-alpha.2 protobufjs
+```
+
+Both parts matter. The package does not declare its `protobufjs` dependency yet, so the import fails without it. And the `latest` tag still points at an older alpha that cannot resolve its own dependencies, so a bare `npm install @weaviate/web` does not work.
+
+Use the ESM entry point. The package also ships a CommonJS build, but it requires an ESM-only dependency, so it loads only on Node 22.12 and later. The ESM entry works on Node 18 and later.
+
+## Server version
+
+:::info Added in `v1.38.3`
+Weaviate serves gRPC-Web by default from `v1.38.3`.
+:::
+
+So the web client needs Weaviate `v1.38.3` or later. On `v1.38.2` it fails to connect, and the error names the missing path:
+
+```text
+/grpc.health.v1.Health/Check UNIMPLEMENTED: Received HTTP 404 response:
+{"code":404,"message":"path /v1/grpc-web/grpc.health.v1.Health/Check was not found"}
+```
+
+For the server side, see [gRPC-Web](../../api/grpc.md#grpc-web).
+
+## TypeScript setup
+
+A browser-only `tsconfig.json` needs `"skipLibCheck": true`. The `@weaviate/core` type declarations refer to Node's `http` module and `Buffer`, so the project fails to typecheck without it. Most Vite and Next.js starters set it already. These are type-only references, so they do not reach the bundle.
+
+## Browser notes
+
+**Do not send custom headers.** Weaviate allows a fixed set of headers on `/v1/grpc-web/`, including `Content-Type`, `X-Grpc-Web`, `X-Weaviate-Client`, `X-User-Agent`, `Grpc-Timeout` and `Authorization`. The client's own headers are all on the list, so cross-origin calls work as shipped. A custom header is sent, then blocked by the browser preflight. Node runs no preflight, so you will not see this in Node tests.
+
+:::warning Browser credentials are visible to the user
+Anyone who loads your page can read whatever the bundle holds, including API keys. Use a read-only API key with the least [RBAC](/deploy/configuration/authorization.md) permissions the app needs. Never ship an admin key to a browser.
+:::
+
+## Questions and feedback
+
+import DocsFeedback from "/_includes/docs-feedback.mdx";
+
+
diff --git a/sidebars.js b/sidebars.js
index de6cf4e3c..2162640ba 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -822,6 +822,7 @@ const sidebars = {
id: "weaviate/client-libraries/typescript/index",
},
items: [
+ "weaviate/client-libraries/typescript/web-client",
"weaviate/client-libraries/typescript/notes-best-practices",
{
type: "link",
diff --git a/src/theme/ContentVisibility/Unlisted/index.js b/src/theme/ContentVisibility/Unlisted/index.js
new file mode 100644
index 000000000..bcf2c99a1
--- /dev/null
+++ b/src/theme/ContentVisibility/Unlisted/index.js
@@ -0,0 +1,21 @@
+import React from "react";
+import { UnlistedMetadata } from "@docusaurus/theme-common";
+
+/**
+ * Swizzled from @docusaurus/theme-classic.
+ *
+ * The upstream component renders two things for an `unlisted: true` page:
+ * (the `noindex, nofollow` robots meta) and a caution
+ * banner reading "This page is unlisted."
+ *
+ * We keep the metadata and drop the banner. Unlisted pages in this repo are
+ * finished, hand-distributed pages rather than drafts, so the banner would tell
+ * a reader who was given the link that the page is provisional. The metadata is
+ * load-bearing and must stay: besides the robots meta itself, the sitemap
+ * plugin decides what to exclude by reading the emitted `noindex` meta
+ * (@docusaurus/plugin-sitemap `isNoIndexMetaRoute`), so removing it would put
+ * unlisted pages back into sitemap.xml.
+ */
+export default function Unlisted() {
+ return ;
+}
From a956f03c985da6694d0bb568e2d1fab30567b35f Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Tue, 22 Sep 2026 14:07:39 +0200
Subject: [PATCH 2/7] docs(web-client): fold server floor into Connect section,
drop TypeScript setup
---
.../client-libraries/typescript/web-client.mdx | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/docs/weaviate/client-libraries/typescript/web-client.mdx b/docs/weaviate/client-libraries/typescript/web-client.mdx
index 6e43acbb0..128b5c45e 100644
--- a/docs/weaviate/client-libraries/typescript/web-client.mdx
+++ b/docs/weaviate/client-libraries/typescript/web-client.mdx
@@ -13,11 +13,9 @@ image: og/docs/client-libraries.jpg
It has the same API as `weaviate-client`, but sends queries over gRPC-Web. Connect options take one HTTP(S) endpoint. There is no gRPC host or port to set, because Weaviate serves gRPC-Web on the REST port.
-Use a `connectTo*` helper. The low-level `weaviate.client(params)` entry point does not add the `/v1/grpc-web` prefix. `grpcHost`, `grpcPort` and `grpcSecure` are not connect options. If you pass them, they are ignored.
-
:::caution Alpha
-`@weaviate/web` `3.15.0-alpha.2` is an alpha. The `latest` tag still points at `3.15.0-alpha.1`, an older alpha that cannot resolve its own dependencies, so always install the version explicitly. The API may change. Do not use it in production yet.
+`@weaviate/web` `3.15.0-alpha.2` is an alpha. The `latest` tag still points at `3.15.0-alpha.1`, an older alpha that cannot resolve its own dependencies, so always install the version explicitly.
:::
@@ -33,7 +31,9 @@ Both parts matter. The package does not declare its `protobufjs` dependency yet,
Use the ESM entry point. The package also ships a CommonJS build, but it requires an ESM-only dependency, so it loads only on Node 22.12 and later. The ESM entry works on Node 18 and later.
-## Server version
+## Connect to Weaviate
+
+Use a `connectTo*` helper. The low-level `weaviate.client(params)` entry point does not add the `/v1/grpc-web` prefix. `grpcHost`, `grpcPort` and `grpcSecure` are not connect options. If you pass them, they are ignored.
:::info Added in `v1.38.3`
Weaviate serves gRPC-Web by default from `v1.38.3`.
@@ -48,9 +48,6 @@ So the web client needs Weaviate `v1.38.3` or later. On `v1.38.2` it fails to co
For the server side, see [gRPC-Web](../../api/grpc.md#grpc-web).
-## TypeScript setup
-
-A browser-only `tsconfig.json` needs `"skipLibCheck": true`. The `@weaviate/core` type declarations refer to Node's `http` module and `Buffer`, so the project fails to typecheck without it. Most Vite and Next.js starters set it already. These are type-only references, so they do not reach the bundle.
## Browser notes
From 0a1ecf14aecfb240b7698684fd14d18999b34064 Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Mon, 28 Sep 2026 08:53:41 +0200
Subject: [PATCH 3/7] docs: update @weaviate/web install to 3.15.0-alpha.6;
version-scope the removed EXPERIMENTAL_REST_SEARCH_ENABLED flag
- web-client: single-line install (protobufjs now declared; package ESM-only)
- REST Search API enabled by default from v1.39.7; env var scoped to v1.39.0-v1.39.6 with a Removed-in marker
---
docs/deploy/configuration/env-vars/index.md | 2 +-
docs/weaviate/api/graphql/migration.mdx | 8 ++++----
docs/weaviate/api/index.mdx | 2 +-
.../client-libraries/typescript/web-client.mdx | 13 +++++++------
4 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/docs/deploy/configuration/env-vars/index.md b/docs/deploy/configuration/env-vars/index.md
index eb712ce9b..7e656dfe4 100644
--- a/docs/deploy/configuration/env-vars/index.md
+++ b/docs/deploy/configuration/env-vars/index.md
@@ -52,7 +52,7 @@ import APITable from '@site/src/components/APITable';
| `ENABLE_TOKENIZER_GSE` | Enable the [`GSE` tokenizer](/weaviate/config-refs/collections.mdx) for use | `boolean` | `true` |
| `ENABLE_TOKENIZER_KAGOME_JA` | Enable the [`Kagome` tokenizer for Japanese](/weaviate/config-refs/collections.mdx) for use | `boolean` | `true` |
| `ENABLE_TOKENIZER_KAGOME_KR` | Enable the [`Kagome` tokenizer for Korean](/weaviate/config-refs/collections.mdx) for use | `boolean` | `true` |
-| `EXPERIMENTAL_REST_SEARCH_ENABLED` | (EXPERIMENTAL) Enable the [REST Search API](/weaviate/api/rest): the `POST /v1/search/{collection}/near-text`, `/bm25`, `/hybrid` and `/near-object` endpoints, and the sibling `POST /v1/aggregate/{collection}` endpoint. While disabled, these endpoints reject requests with a `422` status. Default: `false` | `boolean` | `true` |
+| `EXPERIMENTAL_REST_SEARCH_ENABLED` | **Removed in `v1.39.7`.** The [REST Search API](/weaviate/api/rest) is enabled by default from `v1.39.7`, so no variable is needed. In `v1.39.0` through `v1.39.6`: enabled the `POST /v1/search/{collection}/near-text`, `/bm25`, `/hybrid` and `/near-object` endpoints, and the sibling `POST /v1/aggregate/{collection}` endpoint. While disabled, those endpoints rejected requests with a `422` status. Default was `false`.
Added in `v1.39.0` | `boolean` | `true` |
| `EXPORT_DEFAULT_BUCKET` | Storage bucket name for [collection exports](/docs/deploy/configuration/export.md). Required for S3, GCS, and Azure backends.
Added in `v1.37` | `string` | `my-export-bucket` |
| `EXPORT_DEFAULT_PATH` | Optional base path prefix for exported files within the bucket for [collection exports](/docs/deploy/configuration/export.md). Defaults to `""` (no prefix). _Changed in `v1.37.1`: previously required to be explicitly set._
Added in `v1.37` | `string` | `exports/my-cluster` |
| `EXPORT_ENABLED` | Enable the [collection export](/docs/deploy/configuration/export.md) API. Default: `false`
Added in `v1.37` | `boolean` | `true` |
diff --git a/docs/weaviate/api/graphql/migration.mdx b/docs/weaviate/api/graphql/migration.mdx
index ea12b5c5c..82b7d0dd2 100644
--- a/docs/weaviate/api/graphql/migration.mdx
+++ b/docs/weaviate/api/graphql/migration.mdx
@@ -30,8 +30,8 @@ The three paths are at three different maturity levels. Check the **Status** col
| Path | Status | Availability |
| --- | --- | --- |
| **Official client libraries** | Generally available | Python, JavaScript/TypeScript, Java, and C# are generally available while the Go client v6 is currently in [beta release](https://github.com/weaviate/weaviate-go-client/releases/tag/v6.0.0-beta.3). Earlier versions of the Go client rely on GraphQL. |
-| **`@weaviate/web`** (browser and edge) | **Alpha** | `3.15.0-alpha.2`. Requires Weaviate `v1.38.3` or later, and an [install workaround](../../client-libraries/typescript/web-client.mdx). |
-| **REST Search API** | **Preview** | From `v1.39` available when [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) is set to `true`. Enabled by default from `v1.40`. See [REST Search API](#rest-search-api). |
+| **`@weaviate/web`** (browser and edge) | **Alpha** | `3.15.0-alpha.6`. Requires Weaviate `v1.38.3` or later. See the [web client page](../../client-libraries/typescript/web-client.mdx). |
+| **REST Search API** | **Preview** | Enabled by default from `v1.39.7`. In `v1.39.0` through `v1.39.6` it needs [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) set to `true`. See [REST Search API](#rest-search-api). |
## Client libraries (recommended)
@@ -76,7 +76,7 @@ If your application runs in a browser or on an edge runtime such as Cloudflare W
We exercised the alpha against Weaviate `v1.38.3`, `v1.39.4`, `v1.39.5`, and `v1.40.0-rc.1`, with identical results on each apart from one cosmetic difference in self-distance on `v1.38.3`. Connecting, `fetchObjects` with filters, `bm25`, `hybrid`, `nearVector`, `nearObject`, metadata and vector selection, sorting, paging, aggregation, multi-tenancy, the write methods, and API-key authentication all work. `generate` (RAG) needs a generative module on the server, exactly as it does elsewhere.
-Install steps, the server version floor and the browser notes are on the [web client page](../../client-libraries/typescript/web-client.mdx). The alpha needs an install workaround, so start there.
+Install steps, the server version floor and the browser notes are on the [web client page](../../client-libraries/typescript/web-client.mdx).
## REST Search API
@@ -84,7 +84,7 @@ Install steps, the server version floor and the browser notes are on the [web cl
The REST Search API is a **preview** feature.
-In **`v1.39`** they are available when [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) is set to `true`. They are enabled by default from **`v1.40`**.
+The endpoints are enabled by default from **`v1.39.7`**. In `v1.39.0` through `v1.39.6` they need [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) set to `true`, and return `422` without it.
:::
diff --git a/docs/weaviate/api/index.mdx b/docs/weaviate/api/index.mdx
index 144b85b18..6d75e86a4 100644
--- a/docs/weaviate/api/index.mdx
+++ b/docs/weaviate/api/index.mdx
@@ -11,7 +11,7 @@ Weaviate provides multiple Application Programming Interfaces (APIs) to interact
- Includes operations for managing collections (creating, reading, updating, deleting collections and their definitions), performing basic CRUD (Create, Read, Update, Delete) operations on individual data objects, checking node status, backups, and managing cluster health.
- The machine-readable specification behind this reference is published at `https://docs.weaviate.io/openapi.json`. See [Machine-readable API specification](#machine-readable-api-specification) below.
- - The reference also lists an experimental REST Search API (the `/v1/search/{collection}/...` and `/v1/aggregate/{collection}` endpoints), which is disabled by default and rejects requests with a `422` status until [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) is set to `true`.
+ - The reference also lists an experimental REST Search API (the `/v1/search/{collection}/...` and `/v1/aggregate/{collection}` endpoints), which is enabled by default from `v1.39.7`. In `v1.39.0` through `v1.39.6` it is disabled unless [`EXPERIMENTAL_REST_SEARCH_ENABLED`](/deploy/configuration/env-vars/index.md#EXPERIMENTAL_REST_SEARCH_ENABLED) is set to `true`, and rejects requests with a `422` status.
- **[Search API - GraphQL](./graphql/index.md)**: Designed specifically for data querying and exploration.
diff --git a/docs/weaviate/client-libraries/typescript/web-client.mdx b/docs/weaviate/client-libraries/typescript/web-client.mdx
index 128b5c45e..0fcca3be3 100644
--- a/docs/weaviate/client-libraries/typescript/web-client.mdx
+++ b/docs/weaviate/client-libraries/typescript/web-client.mdx
@@ -5,8 +5,9 @@ description: "The @weaviate/web package: a browser build of the Weaviate v3 Type
image: og/docs/client-libraries.jpg
---
-{/* MAINTENANCE: `3.15.0-alpha.2` is hardcoded in 3 places — the Alpha admonition and the
+{/* MAINTENANCE: `3.15.0-alpha.6` is hardcoded in 3 places — the Alpha admonition and the
install command below, plus the availability table in weaviate/api/graphql/migration.mdx.
+ The `latest` tag lags behind, so the admonition also names the version it points at.
Update all 3 together when the package moves. */}
`@weaviate/web` is a browser build of the v3 client. Use it in browsers and edge runtimes such as Cloudflare Workers and Vercel, where a plain gRPC connection is not possible.
@@ -15,21 +16,21 @@ It has the same API as `weaviate-client`, but sends queries over gRPC-Web. Conne
:::caution Alpha
-`@weaviate/web` `3.15.0-alpha.2` is an alpha. The `latest` tag still points at `3.15.0-alpha.1`, an older alpha that cannot resolve its own dependencies, so always install the version explicitly.
+`@weaviate/web` `3.15.0-alpha.6` is an alpha. The `latest` tag still points at `3.15.0-alpha.2`, an older alpha that fails to load, so always install the version explicitly.
:::
## Install
-Install the alpha explicitly, together with `protobufjs`:
+Install the alpha explicitly:
```bash
-npm install @weaviate/web@3.15.0-alpha.2 protobufjs
+npm install @weaviate/web@3.15.0-alpha.6
```
-Both parts matter. The package does not declare its `protobufjs` dependency yet, so the import fails without it. And the `latest` tag still points at an older alpha that cannot resolve its own dependencies, so a bare `npm install @weaviate/web` does not work.
+The `latest` tag still points at an older alpha that fails to load, so name the version.
-Use the ESM entry point. The package also ships a CommonJS build, but it requires an ESM-only dependency, so it loads only on Node 22.12 and later. The ESM entry works on Node 18 and later.
+The package is ESM-only.
## Connect to Weaviate
From 47009298a2a34389fbcfeab99b781945aa68dec2 Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Mon, 28 Sep 2026 09:22:12 +0200
Subject: [PATCH 4/7] docs(web-client): rewrite the custom-headers note in
plain language
---
docs/weaviate/client-libraries/typescript/web-client.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/weaviate/client-libraries/typescript/web-client.mdx b/docs/weaviate/client-libraries/typescript/web-client.mdx
index 0fcca3be3..124847510 100644
--- a/docs/weaviate/client-libraries/typescript/web-client.mdx
+++ b/docs/weaviate/client-libraries/typescript/web-client.mdx
@@ -52,7 +52,7 @@ For the server side, see [gRPC-Web](../../api/grpc.md#grpc-web).
## Browser notes
-**Do not send custom headers.** Weaviate allows a fixed set of headers on `/v1/grpc-web/`, including `Content-Type`, `X-Grpc-Web`, `X-Weaviate-Client`, `X-User-Agent`, `Grpc-Timeout` and `Authorization`. The client's own headers are all on the list, so cross-origin calls work as shipped. A custom header is sent, then blocked by the browser preflight. Node runs no preflight, so you will not see this in Node tests.
+**Custom headers do not work in the browser.** Weaviate accepts only the headers the client itself sends on `/v1/grpc-web/`. If you add your own — a model provider API key, for example — the browser's CORS preflight fails and the request is never sent. Node skips this check, so the problem only shows up in a real browser.
:::warning Browser credentials are visible to the user
Anyone who loads your page can read whatever the bundle holds, including API keys. Use a read-only API key with the least [RBAC](/deploy/configuration/authorization.md) permissions the app needs. Never ship an admin key to a browser.
From 8ab7c897c97e92c5c664c22a515d486c706fcb9a Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Mon, 28 Sep 2026 09:53:44 +0200
Subject: [PATCH 5/7] Revise migration documentation for API changes
Updated error signaling information and clarified handling of precision for large integers. Adjusted details on metadata structure and fusion type values.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
docs/weaviate/api/graphql/migration.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/weaviate/api/graphql/migration.mdx b/docs/weaviate/api/graphql/migration.mdx
index 82b7d0dd2..ed9a63fa9 100644
--- a/docs/weaviate/api/graphql/migration.mdx
+++ b/docs/weaviate/api/graphql/migration.mdx
@@ -157,7 +157,7 @@ These are the conversions most likely to cost you time. Some return a perfectly
| **`certainty` on a `bm25` search** | Accepted in `returnMetadata`, but certainty cannot be computed for a keyword search, so the field is simply absent from the response. | Request `score` instead for keyword searches. |
| **`int64` precision** | Integers travel as JSON numbers and are handled as `float64`, so values beyond 2^53 lose precision. | For large integers, use a client library, or store the value as text. |
| **`_additional` is renamed** | Retrieval metadata arrives under a **`metadata`** key, not `_additional`. Properties and cross-references are also split into their own `properties` and `references` objects rather than sitting flat on the result, and the object `id` is a top-level envelope field. | Update your response parsing. Note `id` is **not** a valid `returnMetadata` entry — supplying it returns `422`. |
-| **Error signalling** | GraphQL returns HTTP `200` with an `errors[]` array. The REST endpoints use real HTTP status codes (`400`, `401`, `403`, `404`, `422`, `429`, `500`, `503`) with a body of the form `{"error":[{"message":"..."}]}`. | Check the status code, not just the body. Client code that only inspected `errors[]` will treat failures as successes. |
+| **Error signalling** | GraphQL returns HTTP `200` with an `errors[]` array. The REST endpoints use real HTTP status codes (`400`, `401`, `403`, `404`, `413`, `422`, `429`, `500`, `503`) with a body of the form `{"error":[{"message":"..."}]}`. | Check the status code, not just the body. Client code that only inspected `errors[]` will treat failures as successes. |
| **Embedding-provider failures** | When the vectorizer module fails, the response is `500` — never `502` — with a deliberately short message; the provider's own response goes to the server log, because it can quote credentials. | Read the server log for provider errors; do not parse the client-facing body. |
| **The same validation, surfaced differently** | A malformed `where` filter — `And`/`Or` with no operands, a `Not` with more than one, a non-RFC3339 `valueDate`, a `ContainsAny`/`ContainsAll`/`ContainsNone` without an array value, or `IsNull` without `indexNullState` — is rejected by **both** APIs, with the same underlying message. GraphQL returns it inside `errors[]` under HTTP `200`; here it arrives as a status code. Only the missing-operands case is caught at the API layer and returns `400`; the other four are rejected deeper in the engine and return **`500`**. | Do not treat these `500`s as transient server trouble: a retry or alerting rule keyed on `5xx` will retry forever and page someone. Read the message before retrying. |
| **`fusionType` values differ** | GraphQL's `rankedFusion` and `relativeScoreFusion` are `ranked` and `relativeScore` here. A copied GraphQL value returns `422`. | Translate the value; see the cheat sheet below. |
From 3f2a1cd86df81d294774a5b7794db2f85adec06e Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Mon, 28 Sep 2026 09:54:43 +0200
Subject: [PATCH 6/7] docs(migration): drop the test-log style what-works
paragraph
---
docs/weaviate/api/graphql/migration.mdx | 2 --
1 file changed, 2 deletions(-)
diff --git a/docs/weaviate/api/graphql/migration.mdx b/docs/weaviate/api/graphql/migration.mdx
index ed9a63fa9..1796e38ab 100644
--- a/docs/weaviate/api/graphql/migration.mdx
+++ b/docs/weaviate/api/graphql/migration.mdx
@@ -74,8 +74,6 @@ If your application runs in a browser or on an edge runtime such as Cloudflare W
`@weaviate/web` is an alpha release and is not recommended for production use yet.
:::
-We exercised the alpha against Weaviate `v1.38.3`, `v1.39.4`, `v1.39.5`, and `v1.40.0-rc.1`, with identical results on each apart from one cosmetic difference in self-distance on `v1.38.3`. Connecting, `fetchObjects` with filters, `bm25`, `hybrid`, `nearVector`, `nearObject`, metadata and vector selection, sorting, paging, aggregation, multi-tenancy, the write methods, and API-key authentication all work. `generate` (RAG) needs a generative module on the server, exactly as it does elsewhere.
-
Install steps, the server version floor and the browser notes are on the [web client page](../../client-libraries/typescript/web-client.mdx).
## REST Search API
From 2df313f825b162187dfe41a0d611b89075f7db53 Mon Sep 17 00:00:00 2001
From: Ivan Despot <66276597+g-despot@users.noreply.github.com>
Date: Mon, 28 Sep 2026 13:05:57 +0200
Subject: [PATCH 7/7] docs: correct browser custom-headers claim after live
e2e; punctuation sweep
- provider API keys are on Weaviate's default CORS allow-list and work in the browser (live-verified); other headers fail preflight
- note CORS_ALLOW_HEADERS replaces the default list
- remove semicolons and em dashes from authored prose
---
docs/weaviate/api/graphql/migration.mdx | 86 +++++++++----------
.../typescript/web-client.mdx | 6 +-
2 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/docs/weaviate/api/graphql/migration.mdx b/docs/weaviate/api/graphql/migration.mdx
index 1796e38ab..246c23382 100644
--- a/docs/weaviate/api/graphql/migration.mdx
+++ b/docs/weaviate/api/graphql/migration.mdx
@@ -13,7 +13,7 @@ This guide is for developers who have working GraphQL `Get` and `Aggregate` quer
- You are moving an application to a **new Weaviate Cloud cluster**, which is created with **GraphQL disabled**.
- You are building in the **browser** or on an edge runtime, where a plain gRPC client cannot run.
-- You maintain an **HTTP-only stack** — a no-code or BI tool, or a language without an official Weaviate client, such as PHP or Ruby.
+- You maintain an **HTTP-only stack**, such as a no-code or BI tool, or a language without an official Weaviate client such as PHP or Ruby.
Start at the top of this table and take the first row that describes your stack.
@@ -48,7 +48,7 @@ The current Go client builds its queries as GraphQL under the hood. The upcoming
| `Get { Collection { ... } }` | Fetch objects from a collection | [Search patterns and basics](../../search/basics.md#list-objects) |
| `nearText` | Near-text search | [Search with text](../../search/similarity.md#search-with-text) |
| `nearObject` | Near-object search | [Search with an existing object](../../search/similarity.md#search-with-an-existing-object) |
-| `nearVector` | Near-vector search — pass your own query vector | [Search with a vector](../../search/similarity.md#search-with-a-vector) |
+| `nearVector` | Near-vector search, passing your own query vector | [Search with a vector](../../search/similarity.md#search-with-a-vector) |
| `nearImage` and the other media operators | Multimodal search | [Search with image](../../search/similarity.md#search-with-image) |
| `bm25` | Keyword search, including search operators and property boosts | [Keyword search](../../search/bm25.md) |
| `hybrid` | Hybrid search, including `alpha`, fusion type, and search vectors | [Hybrid search](../../search/hybrid.md) |
@@ -56,7 +56,7 @@ The current Go client builds its queries as GraphQL under the hood. The upcoming
| `limit`, `offset` | `limit` and `offset` arguments | [Paginate with limit and offset](../../search/basics.md#paginate-with-limit-and-offset) |
| `after` (cursor) | Cursor-based iteration over all objects | [Cursor with `after`](./additional-operators.md#cursor-with-after) |
| `autocut` | `auto_limit` / `autoLimit` | [Limit result groups](../../search/similarity.md#limit-result-groups) |
-| `sort` | Sorting on object retrieval. Sorting is not combined with a search operator — see [sorting considerations](./additional-operators.md#sorting-considerations) | [Sorting](./additional-operators.md#sorting) |
+| `sort` | Sorting on object retrieval. Sorting is not combined with a search operator. See [sorting considerations](./additional-operators.md#sorting-considerations) | [Sorting](./additional-operators.md#sorting) |
| `_additional { ... }` | `return_metadata` / `returnMetadata`, returned as a metadata object on each result | [Retrieve metadata values](../../search/basics.md#retrieve-metadata-values) |
| `_additional { vector }` | `include_vector` / `returnVectors` | [Retrieve the object vector](../../search/basics.md#retrieve-the-object-vector) |
| `groupBy` | Grouped results | [Group results](../../search/similarity.md#group-results) |
@@ -103,7 +103,7 @@ There are five available `POST` endpoints:
The GraphQL query you are converting from is documented in the [GraphQL reference](./get.md). Its REST equivalent looks like this:
-**REST Search API** — `POST /v1/search/Movie/near-text`
+**REST Search API**: `POST /v1/search/Movie/near-text`
```json
{
@@ -121,7 +121,7 @@ The GraphQL query you are converting from is documented in the [GraphQL referenc
}
```
-The `where` value is the **exact same JSON** you pass in GraphQL — the same 15 operators, the same value fields, the same cross-reference paths, parsed by the same code. There is no new filter language to learn.
+The `where` value is the **exact same JSON** you pass in GraphQL. It uses the same 15 operators, the same value fields, and the same cross-reference paths, parsed by the same code. There is no new filter language to learn.
The response is a flat envelope rather than the GraphQL `data.Get.` nesting:
@@ -140,7 +140,7 @@ The response is a flat envelope rather than the GraphQL `data.Get.`
### Differences to check before you convert
-These are the conversions most likely to cost you time. Some return a perfectly successful response carrying a different answer from the one GraphQL gave; others change the way a failure reaches your code. The silent ones are the dangerous ones. Read this list before the feature gaps below.
+These are the conversions most likely to cost you time. Some return a perfectly successful response carrying a different answer from the one GraphQL gave. Others change the way a failure reaches your code. The silent ones are the dangerous ones. Read this list before the feature gaps below.
What changes, and what to do about it
@@ -154,35 +154,35 @@ These are the conversions most likely to cost you time. Some return a perfectly
| **A body field named `generate`** | Silently ignored, and the response contains no generated text. The reserved `singlePrompt` and `groupedTask` fields do return `422`. | See [Queries without a REST equivalent yet](#queries-without-a-rest-equivalent-yet). |
| **`certainty` on a `bm25` search** | Accepted in `returnMetadata`, but certainty cannot be computed for a keyword search, so the field is simply absent from the response. | Request `score` instead for keyword searches. |
| **`int64` precision** | Integers travel as JSON numbers and are handled as `float64`, so values beyond 2^53 lose precision. | For large integers, use a client library, or store the value as text. |
-| **`_additional` is renamed** | Retrieval metadata arrives under a **`metadata`** key, not `_additional`. Properties and cross-references are also split into their own `properties` and `references` objects rather than sitting flat on the result, and the object `id` is a top-level envelope field. | Update your response parsing. Note `id` is **not** a valid `returnMetadata` entry — supplying it returns `422`. |
+| **`_additional` is renamed** | Retrieval metadata arrives under a **`metadata`** key, not `_additional`. Properties and cross-references are also split into their own `properties` and `references` objects rather than sitting flat on the result, and the object `id` is a top-level envelope field. | Update your response parsing. Note `id` is **not** a valid `returnMetadata` entry. Supplying it returns `422`. |
| **Error signalling** | GraphQL returns HTTP `200` with an `errors[]` array. The REST endpoints use real HTTP status codes (`400`, `401`, `403`, `404`, `413`, `422`, `429`, `500`, `503`) with a body of the form `{"error":[{"message":"..."}]}`. | Check the status code, not just the body. Client code that only inspected `errors[]` will treat failures as successes. |
-| **Embedding-provider failures** | When the vectorizer module fails, the response is `500` — never `502` — with a deliberately short message; the provider's own response goes to the server log, because it can quote credentials. | Read the server log for provider errors; do not parse the client-facing body. |
-| **The same validation, surfaced differently** | A malformed `where` filter — `And`/`Or` with no operands, a `Not` with more than one, a non-RFC3339 `valueDate`, a `ContainsAny`/`ContainsAll`/`ContainsNone` without an array value, or `IsNull` without `indexNullState` — is rejected by **both** APIs, with the same underlying message. GraphQL returns it inside `errors[]` under HTTP `200`; here it arrives as a status code. Only the missing-operands case is caught at the API layer and returns `400`; the other four are rejected deeper in the engine and return **`500`**. | Do not treat these `500`s as transient server trouble: a retry or alerting rule keyed on `5xx` will retry forever and page someone. Read the message before retrying. |
-| **`fusionType` values differ** | GraphQL's `rankedFusion` and `relativeScoreFusion` are `ranked` and `relativeScore` here. A copied GraphQL value returns `422`. | Translate the value; see the cheat sheet below. |
+| **Embedding-provider failures** | When the vectorizer module fails, the response is `500`, never `502`, with a deliberately short message. The provider's own response goes to the server log, because it can quote credentials. | Read the server log for provider errors. Do not parse the client-facing body. |
+| **The same validation, surfaced differently** | Both APIs reject a malformed `where` filter with the same underlying message. That covers `And`/`Or` with no operands, a `Not` with more than one, a non-RFC3339 `valueDate`, a `ContainsAny`/`ContainsAll`/`ContainsNone` without an array value, and `IsNull` without `indexNullState`. GraphQL returns the error inside `errors[]` under HTTP `200`. Here it arrives as a status code. Only the missing-operands case is caught at the API layer and returns `400`. The other four are rejected deeper in the engine and return **`500`**. | Do not treat these `500`s as transient server trouble: a retry or alerting rule keyed on `5xx` will retry forever and page someone. Read the message before retrying. |
+| **`fusionType` values differ** | GraphQL's `rankedFusion` and `relativeScoreFusion` are `ranked` and `relativeScore` here. A copied GraphQL value returns `422`. | Translate the value. See the cheat sheet below. |
### Queries without a REST equivalent yet
-These capabilities are not part of the preview. They fail in two very different ways, so check the status column before you port anything: some are **reserved** and return `422` or `400`, which shows the gap in your logs; others are simply **not recognized**, and the request returns `200` with the capability having had no effect at all. The silent ones are the ones to watch. If you need one of these, please [open a GitHub issue](https://github.com/weaviate/weaviate/issues).
+These capabilities are not part of the preview. They fail in two very different ways, so check the status column before you port anything: some are **reserved** and return `422` or `400`, which shows the gap in your logs. Others are simply **not recognized**, and the request returns `200` with the capability having had no effect at all. The silent ones are the ones to watch. If you need one of these, please [open a GitHub issue](https://github.com/weaviate/weaviate/issues).
What is missing, and what each returns
| GraphQL capability | REST status | Path |
| --- | --- | --- |
-| **`nearVector`** — supplying your own query vector | **No route exists.** There is no `near-vector` endpoint, so the request fails outright. | Any collection configured with `vectorizer: none`, or any workload that computes embeddings client-side, needs a [client library](#client-libraries-recommended) or the [web client](#typescript-client-for-web). |
-| **`hybrid.vector`** — supplying the vector leg of a hybrid search | The `hybrid` route exists but has no `vector` field, so the value is **silently ignored** (`200`) and the search runs against a server-computed vector instead of yours. | [Client libraries](#client-libraries-recommended) or the [web client](#typescript-client-for-web). |
-| **`Aggregate` property statistics** — `sum`, `mean`, `median`, `mode`, `minimum`, `maximum`, `topOccurrences`, `totalTrue`, and the rest | Aggregate is **count-only**. A `property:statistic` entry in `returnMetrics` returns `422`. | [Client libraries](#client-libraries-recommended) |
-| **Aggregate over a search** — `over` and `objectLimit` | Reserved; returns `422`. | [Client libraries](#client-libraries-recommended) |
-| **RAG** — `generate(singleResult:)` and `generate(groupedResult:)` | `singlePrompt` and `groupedTask` are reserved and return `422`. | [Client libraries](#client-libraries-recommended) |
-| **Multiple target vectors** — `targetVectors` and `targets` | `targetVector` takes a **single string**. On `near-text`, `near-object`, and `hybrid` an array returns `400`. On `bm25` there is no `targetVector` field at all, so any value — string or array — is **silently ignored** (`200`) through the [unknown-field path](#differences-to-check-before-you-convert). Multi-target weighting is not available. | [Client libraries](#client-libraries-recommended) |
-| **Media search** — `nearImage`, `nearAudio`, `nearVideo`, `nearDepth`, `nearThermal`, `nearImu` | No routes. | [Client libraries](#client-libraries-recommended) |
-| **`groupBy` in `Get`** | Reserved, but the status depends on the shape you send. The GraphQL-shaped object (`{path, groups, objectsPerGroup}`) — the form you are most likely to port — returns **`400`**, because the reserved field is typed as a string. A bare string returns `422` "groupBy is not yet supported". | [Client libraries](#client-libraries-recommended) |
-| **`rerank`** | Reserved; returns `422`. | [Client libraries](#client-libraries-recommended) |
-| **`sort`** and the `after` cursor | Not fields on the search endpoints, so both are **silently ignored** (`200`, with the result set unchanged and unsorted). Sorting is not combined with search operators — see [sorting considerations](./additional-operators.md#sorting-considerations) — and both remain available on `GET /v1/objects`. | [Client libraries](#client-libraries-recommended), or `GET /v1/objects` |
-| **`nearText` concept steering** — `moveTo`, `moveAwayFrom`, `autocorrect` | Not fields on the `near-text` route, so all three are **silently ignored** (`200`, with the result set unchanged and unsteered). | [Client libraries](#client-libraries-recommended) |
-| **Vectors in the response** — `_additional { vector }` | Vectors are never returned by these endpoints. | [Client libraries](#client-libraries-recommended), or `GET /v1/objects` |
+| **`nearVector`**, supplying your own query vector | **No route exists.** There is no `near-vector` endpoint, so the request fails outright. | Any collection configured with `vectorizer: none`, or any workload that computes embeddings client-side, needs a [client library](#client-libraries-recommended) or the [web client](#typescript-client-for-web). |
+| **`hybrid.vector`**, supplying the vector leg of a hybrid search | The `hybrid` route exists but has no `vector` field, so the value is **silently ignored** (`200`) and the search runs against a server-computed vector instead of yours. | [Client libraries](#client-libraries-recommended) or the [web client](#typescript-client-for-web). |
+| **`Aggregate` property statistics** such as `sum`, `mean`, `median`, `mode`, `minimum`, `maximum`, `topOccurrences`, `totalTrue`, and the rest | Aggregate is **count-only**. A `property:statistic` entry in `returnMetrics` returns `422`. | [Client libraries](#client-libraries-recommended) |
+| **Aggregate over a search**, `over` and `objectLimit` | Reserved, returns `422`. | [Client libraries](#client-libraries-recommended) |
+| **RAG**, `generate(singleResult:)` and `generate(groupedResult:)` | `singlePrompt` and `groupedTask` are reserved and return `422`. | [Client libraries](#client-libraries-recommended) |
+| **Multiple target vectors**, `targetVectors` and `targets` | `targetVector` takes a **single string**. On `near-text`, `near-object`, and `hybrid` an array returns `400`. On `bm25` there is no `targetVector` field at all, so any value, string or array, is **silently ignored** (`200`) through the [unknown-field path](#differences-to-check-before-you-convert). Multi-target weighting is not available. | [Client libraries](#client-libraries-recommended) |
+| **Media search**, `nearImage`, `nearAudio`, `nearVideo`, `nearDepth`, `nearThermal`, `nearImu` | No routes. | [Client libraries](#client-libraries-recommended) |
+| **`groupBy` in `Get`** | Reserved, but the status depends on the shape you send. The GraphQL-shaped object (`{path, groups, objectsPerGroup}`) is the form you are most likely to port, and it returns **`400`**, because the reserved field is typed as a string. A bare string returns `422` "groupBy is not yet supported". | [Client libraries](#client-libraries-recommended) |
+| **`rerank`** | Reserved, returns `422`. | [Client libraries](#client-libraries-recommended) |
+| **`sort`** and the `after` cursor | Not fields on the search endpoints, so both are **silently ignored** (`200`, with the result set unchanged and unsorted). Sorting is not combined with search operators, as noted in [sorting considerations](./additional-operators.md#sorting-considerations), and both remain available on `GET /v1/objects`. | [Client libraries](#client-libraries-recommended), or `GET /v1/objects` |
+| **`nearText` concept steering**, `moveTo`, `moveAwayFrom` and `autocorrect` | Not fields on the `near-text` route, so all three are **silently ignored** (`200`, with the result set unchanged and unsteered). | [Client libraries](#client-libraries-recommended) |
+| **Vectors in the response**, `_additional { vector }` | Vectors are never returned by these endpoints. | [Client libraries](#client-libraries-recommended), or `GET /v1/objects` |
@@ -195,44 +195,44 @@ Body fields are **camelCase**. Field names not listed here are not recognized, a
| GraphQL | REST request body |
| --- | --- |
-| `nearText: { concepts: ["x", "y"] }` | `POST /v1/search/{c}/near-text` with `"query": ["x", "y"]` — always an array, even for one concept |
-| `nearText: { certainty: 0.7 }` or `{ distance: 0.3 }` | `"certainty": 0.7` or `"distance": 0.3` — one or the other, not both |
+| `nearText: { concepts: ["x", "y"] }` | `POST /v1/search/{c}/near-text` with `"query": ["x", "y"]`. Always an array, even for one concept |
+| `nearText: { certainty: 0.7 }` or `{ distance: 0.3 }` | `"certainty": 0.7` or `"distance": 0.3`. One or the other, not both |
| `bm25: { query: "x", properties: ["title^2"] }` | `POST /v1/search/{c}/bm25` with `"query": "x", "queryProperties": ["title^2"]` |
| `hybrid: { query: "x", alpha: 0.5 }` | `POST /v1/search/{c}/hybrid` with `"query": "x", "alpha": 0.5` |
| `hybrid: { fusionType: rankedFusion }` | `"fusionType": "ranked"` |
| `hybrid: { fusionType: relativeScoreFusion }` | `"fusionType": "relativeScore"` |
-| `hybrid: { maxVectorDistance: 0.4 }` | `"maxVectorDistance": 0.4` — requires `alpha` greater than `0`, otherwise `400` |
-| `nearObject: { id: "" }` | `POST /v1/search/{c}/near-object` with `"id": ""` — required, and must be a well-formed UUID |
-| `targetVectors: ["title_vector"]` | `"targetVector": "title_vector"` — a single string |
+| `hybrid: { maxVectorDistance: 0.4 }` | `"maxVectorDistance": 0.4`. Requires `alpha` greater than `0`, otherwise `400` |
+| `nearObject: { id: "" }` | `POST /v1/search/{c}/near-object` with `"id": ""`. Required, and must be a well-formed UUID |
+| `targetVectors: ["title_vector"]` | `"targetVector": "title_vector"`. A single string |
| `where: { ...WhereFilter... }` | `"where": { ...the same JSON... }` |
| `limit: 5, offset: 10` | `"limit": 5, "offset": 10` |
| `autocut: 1` | `"autoLimit": 1` |
-| A property selection such as `title year` | `"returnProperties": ["title", "year"]` — omit the field for all non-reference properties, or pass `[]` for none |
-| `hasAuthor { ... on Author { name } }` | `"returnReferences": [{ "linkOn": "hasAuthor", "targetCollection": "Author", "returnProperties": ["name"] }]` — dotted paths in `returnProperties` return `400` |
-| `_additional { distance score explainScore }` | `"returnMetadata": ["distance", "score", "explainScore"]` — response key is `metadata` |
+| A property selection such as `title year` | `"returnProperties": ["title", "year"]`. Omit the field for all non-reference properties, or pass `[]` for none |
+| `hasAuthor { ... on Author { name } }` | `"returnReferences": [{ "linkOn": "hasAuthor", "targetCollection": "Author", "returnProperties": ["name"] }]`. Dotted paths in `returnProperties` return `400` |
+| `_additional { distance score explainScore }` | `"returnMetadata": ["distance", "score", "explainScore"]`. The response key is `metadata` |
| `_additional { creationTimeUnix lastUpdateTimeUnix }` | `"returnMetadata": ["creationTime", "lastUpdateTime"]` |
-| `_additional { id }` | Nothing to request — `id` is always returned in the envelope. Passing `"id"` in `returnMetadata` returns `422` |
+| `_additional { id }` | Nothing to request. `id` is always returned in the envelope. Passing `"id"` in `returnMetadata` returns `422` |
| `tenant: "tenantA"` | `"tenant": "tenantA"` |
-| `consistencyLevel: ONE` | `"consistencyLevel": "ONE"` — uppercase only; `"one"` returns `422` |
-| `Aggregate { C(groupBy: ["category"]) { meta { count } } }` | `POST /v1/aggregate/{c}` with `"groupBy": "category", "returnMetrics": ["count"]` — a **bare property name**, not a path; a dotted value returns `422` |
+| `consistencyLevel: ONE` | `"consistencyLevel": "ONE"`. Uppercase only, `"one"` returns `422` |
+| `Aggregate { C(groupBy: ["category"]) { meta { count } } }` | `POST /v1/aggregate/{c}` with `"groupBy": "category", "returnMetrics": ["count"]`. Use a **bare property name**, not a path. A dotted value returns `422` |
| `Aggregate { C { meta { count } } }` | `POST /v1/aggregate/{c}` with an empty body, or `"returnMetrics": ["count"]` |
-One more request limit worth knowing: in `v1.39`, `limit`, `offset`, and their sum are each capped by `QUERY_MAXIMUM_RESULTS`, and exceeding any of them returns `400`. A request body size cap, returning `413`, is not in `v1.39`; it is planned for `v1.40`.
+One more request limit worth knowing: in `v1.39`, `limit`, `offset`, and their sum are each capped by `QUERY_MAXIMUM_RESULTS`, and exceeding any of them returns `400`. A request body size cap, returning `413`, is not in `v1.39`. It is planned for `v1.40`.
-## Related pages
+## Further resources
-- [Search (GraphQL | gRPC)](./index.md) — the GraphQL and gRPC search API reference
-- [gRPC-Web](../grpc.md#grpc-web) — the browser-reachable gRPC interface
-- [Client libraries](../../client-libraries/index.mdx) — installation and connection for each language
-- [Web client](../../client-libraries/typescript/web-client.mdx) — install steps and browser notes for `@weaviate/web`
-- [Search patterns and basics](../../search/basics.md) — the how-to family this guide links throughout
-- [Connect to a cluster](/cloud/manage-clusters/connect.mdx) — where to find your Weaviate Cloud endpoint URLs
+- [Search (GraphQL | gRPC)](./index.md) - the GraphQL and gRPC search API reference
+- [gRPC-Web](../grpc.md#grpc-web) - the browser-reachable gRPC interface
+- [Client libraries](../../client-libraries/index.mdx) - installation and connection for each language
+- [Web client](../../client-libraries/typescript/web-client.mdx) - install steps and browser notes for `@weaviate/web`
+- [Search patterns and basics](../../search/basics.md) - the how-to family this guide links throughout
+- [Connect to a cluster](/cloud/manage-clusters/connect.mdx) - where to find your Weaviate Cloud endpoint URLs
## Questions and feedback
-All three paths are actively being developed, and feedback on this guide — especially a query you could not convert — directly shapes what gets built next.
+All three paths are actively being developed, and feedback on this guide, especially a query you could not convert, directly shapes what gets built next.
import DocsFeedback from '/\_includes/docs-feedback.mdx';
diff --git a/docs/weaviate/client-libraries/typescript/web-client.mdx b/docs/weaviate/client-libraries/typescript/web-client.mdx
index 124847510..df555b8b6 100644
--- a/docs/weaviate/client-libraries/typescript/web-client.mdx
+++ b/docs/weaviate/client-libraries/typescript/web-client.mdx
@@ -5,7 +5,7 @@ description: "The @weaviate/web package: a browser build of the Weaviate v3 Type
image: og/docs/client-libraries.jpg
---
-{/* MAINTENANCE: `3.15.0-alpha.6` is hardcoded in 3 places — the Alpha admonition and the
+{/* MAINTENANCE: `3.15.0-alpha.6` is hardcoded in 3 places: the Alpha admonition and the
install command below, plus the availability table in weaviate/api/graphql/migration.mdx.
The `latest` tag lags behind, so the admonition also names the version it points at.
Update all 3 together when the package moves. */}
@@ -52,10 +52,10 @@ For the server side, see [gRPC-Web](../../api/grpc.md#grpc-web).
## Browser notes
-**Custom headers do not work in the browser.** Weaviate accepts only the headers the client itself sends on `/v1/grpc-web/`. If you add your own — a model provider API key, for example — the browser's CORS preflight fails and the request is never sent. Node skips this check, so the problem only shows up in a real browser.
+**Provider API keys work in the browser. Other custom headers do not.** Model provider keys passed through `headers`, such as `X-OpenAI-Api-Key`, are on Weaviate's default CORS allow-list, so they work cross-origin out of the box. Any other header fails the browser's CORS preflight unless the operator lists it in [`CORS_ALLOW_HEADERS`](/deploy/configuration/env-vars/index.md#CORS_ALLOW_HEADERS) on the server. Setting that variable replaces the default list, so include the defaults you still need. Node runs no preflight, so test header behavior in a real browser.
:::warning Browser credentials are visible to the user
-Anyone who loads your page can read whatever the bundle holds, including API keys. Use a read-only API key with the least [RBAC](/deploy/configuration/authorization.md) permissions the app needs. Never ship an admin key to a browser.
+Anyone who loads your page can read whatever the bundle holds, including Weaviate and model provider API keys. Use a read-only API key with the least [RBAC](/deploy/configuration/authorization.md) permissions the app needs. Never ship an admin key to a browser.
:::
## Questions and feedback