Skip to content

feat(es): replace @elastic/transport with native fetch EsClient#281

Open
MattDevy wants to merge 7 commits into
mainfrom
feat/remove-elastic-transport-273
Open

feat(es): replace @elastic/transport with native fetch EsClient#281
MattDevy wants to merge 7 commits into
mainfrom
feat/remove-elastic-transport-273

Conversation

@MattDevy
Copy link
Copy Markdown
Contributor

@MattDevy MattDevy commented May 6, 2026

Summary

Closes #273.

  • Removes @elastic/transport entirely. The CLI always exits after one HTTP request, so connection pooling, node sniffing, and dead-node resurrection are wasted overhead. Native globalThis.fetch (Node 18+, backed by undici) is sufficient.
  • Adds x-elastic-client-meta to all outgoing HTTP requests. Previously only Elasticsearch requests carried the header (via @elastic/transport's constructor injection). Kibana and Cloud requests now include it as well.
  • Updates the meta header format per spec v1.6.0: when there is no separate transport library, t= equals the CLI version. Drops the t=<transportVersion> and un=<undiciVersion> keys.

Changes

New

  • src/lib/es-client.tsEsClient class: thin fetch wrapper with ApiKey/Basic auth, JSON/NDJSON body handling, EsResponseError, EsConnectionError, and a getEsClient() singleton
  • test/lib/es-client.test.ts — full coverage of EsClient (auth, URL composition, body types, error mapping, meta headers)
  • test/lib/kibana-client.test.ts — new tests verifying x-elastic-client-meta and user-agent on every Kibana request

Modified

  • src/lib/meta.ts — updated header format; result memoized at module load (was recomputed on every request)
  • src/lib/kibana-client.ts, src/lib/cloud-client.ts — spread clientHeaders() into every request
  • src/es/errors.tstransportError() now matches EsResponseError / EsConnectionError instead of @elastic/transport error classes
  • src/es/handler.ts, src/es/helpers/* — swap TransportEsClient; rename getTransport dep to getEsClient
  • src/es/request-builder.ts — replace TransportRequestParams with local EsRequestParams
  • test/lib/cloud-client.test.ts, test/lib/meta.test.ts — updated expectations
  • NOTICE.txt — regenerated (removes @elastic/transport and undici entries)

Deleted

  • src/lib/transport.ts, test/lib/transport.test.ts

Test plan

  • npm run build — no TypeScript errors
  • npm test — 1046/1046 pass
  • Spot-check ES request: elastic es cat health sends x-elastic-client-meta with correct et=, js=, t= keys
  • Spot-check Kibana request: verify header is present (previously absent)
  • Spot-check Cloud request: verify header is present (previously absent)
  • Verify grep -r '@elastic/transport' src/ returns no matches

@MattDevy MattDevy force-pushed the feat/remove-elastic-transport-273 branch from fbff93c to 2e749dc Compare May 6, 2026 15:11
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

MegaLinter analysis: Success

Descriptor Linter Files Fixed Errors Warnings Elapsed time
✅ COPYPASTE jscpd yes no no 8.18s
✅ REPOSITORY gitleaks yes no no 56.76s
✅ REPOSITORY git_diff yes no no 0.48s
✅ REPOSITORY secretlint yes no no 28.51s
✅ REPOSITORY trivy yes no no 19.24s
✅ TYPESCRIPT eslint 21 0 0 5.82s

See detailed reports in MegaLinter artifacts
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

@MattDevy MattDevy marked this pull request as ready for review May 13, 2026 10:45
@MattDevy MattDevy requested review from JoshMock and margaretjgu May 13, 2026 10:45
MattDevy added 6 commits May 14, 2026 12:57
Remove @elastic/transport and replace ES HTTP calls with a lightweight
EsClient backed by globalThis.fetch. The CLI always exits after one
request, so connection pooling, node sniffing, and dead-node
resurrection provide no value.

Extend x-elastic-client-meta coverage to all outgoing HTTP requests
(Elasticsearch, Kibana, Cloud). Previously only ES requests carried
the header via @elastic/transport's constructor-time injection.

- src/lib/es-client.ts: new EsClient class with ApiKey/Basic auth,
  NDJSON bulkBody support, EsResponseError, EsConnectionError, and
  singleton getEsClient()
- src/lib/meta.ts: update clientHeaders() -- drop t=<transportVersion>
  and un=<undiciVersion>; per spec, t= equals the CLI version when
  there is no separate transport library; memoize at module load
- src/lib/kibana-client.ts, cloud-client.ts: spread clientHeaders()
  into every request
- src/es/errors.ts, handler.ts, request-builder.ts: swap Transport
  types/imports for EsClient equivalents
- src/es/helpers/*: same swap; rename getTransport dep to getEsClient
- package.json: remove @elastic/transport from dependencies
- NOTICE.txt: regenerated
getEsClient() and EsClient now handle auth being absent from the
config (ES clusters with security disabled). The old @elastic/transport
code guarded auth != null before accessing it; the replacement code
did not, causing a TypeError that surfaced as a misleading
missing_config error in all functional tests.
… POST

Two bugs caused functional test failures:

1. handler.ts was injecting `content-type: application/json` via reqOpts for
   string bodies, which EsClient also sets as `Content-Type`. Object.assign
   kept both (different case), and fetch joined them as a comma-separated
   duplicate value — rejected by ES with 406 Not Acceptable.

2. Native fetch() enforces the Fetch spec: GET/HEAD requests cannot have a
   body. @elastic/transport bypassed this via undici directly. Several ES
   APIs (mget, msearch, scroll, analyze, etc.) send a body on GET requests.
   EsClient now auto-upgrades GET→POST when a body is present; ES REST APIs
   accept POST for all such endpoints.
The native fetch EsClient threw EsResponseError on 404 for HEAD endpoints,
breaking indices.exists and similar APIs that rely on @elastic/transport's
convention of returning true for 2xx and false for 404. Non-404 error
statuses still throw.
The watch helper merged in from main still imported Transport from
@elastic/transport and getTransport from lib/transport.ts, both of
which were removed in this branch. Swap to EsClient and getEsClient
to match the rest of the codebase, and update the test mocks. Also
regen NOTICE.txt and reconcile package-lock.
@MattDevy MattDevy force-pushed the feat/remove-elastic-transport-273 branch from 46e2bda to f0f89b5 Compare May 14, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add x-elastic-client-meta headers to all HTTP requests

1 participant