Skip to content

feat(supabase): W3C/OpenTelemetry trace context propagation#2163

Merged
mandarini merged 14 commits into
masterfrom
feat/trace-propagation
May 13, 2026
Merged

feat(supabase): W3C/OpenTelemetry trace context propagation#2163
mandarini merged 14 commits into
masterfrom
feat/trace-propagation

Conversation

@grdsdev

@grdsdev grdsdev commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds opt-in W3C / OpenTelemetry trace context propagation for HTTP requests to Supabase services (Auth, Storage, PostgREST, Functions). Enables end-to-end distributed tracing from client applications through Supabase services.

Design principles:

  • Opt-in by default — zero behavior change for existing consumers; trace headers are never attached unless explicitly enabled.
  • Security-scoped — when enabled, headers are propagated only to Supabase domains (*.supabase.co, *.supabase.in, localhost) so trace context never leaks to third-party services.
  • Optional peer dep — uses @opentelemetry/api via runtime dynamic import; if it's not installed, the SDK silently no-ops.
  • Zero breaking changes — existing API untouched; one new optional field on SupabaseClientOptions.
  • Centralized injection — single fetchWithAuth injection point covers Auth, Storage, PostgREST, and Functions.

How to opt in

The simplest form is a boolean:

import { createClient } from '@supabase/supabase-js'

const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-key', {
  tracePropagation: true,
})

Once enabled, requests issued inside an active OTel span automatically carry traceparent / tracestate / baggage:

import { trace } from '@opentelemetry/api'

const tracer = trace.getTracer('my-app')
await tracer.startActiveSpan('fetch-users', async (span) => {
  // traceparent / tracestate / baggage are injected on this request
  const { data, error } = await supabase.from('users').select('*')
  span.end()
})

For advanced configuration, pass an object:

const supabase = createClient(url, key, {
  tracePropagation: {
    enabled: true,
    respectSamplingDecision: false, // always propagate, even for non-sampled traces
  },
})

TracePropagationOptions

interface TracePropagationOptions {
  // Enable trace propagation. Default: false (opt-in).
  enabled?: boolean

  // When true, skip propagation if the upstream traceparent's sampled flag is 0.
  // Default: true.
  respectSamplingDecision?: boolean
}

Changes

New package: @supabase/tracing (internal)

Shared package in packages/shared/tracing/ providing:

  • extractTraceContext() — extracts W3C traceparent, tracestate, and baggage from the active OpenTelemetry context. The dynamic import to @opentelemetry/api is cached at module scope (one resolution per process) and routed through a variable specifier with bundler-ignore comments so webpack / turbopack / vite / rollup don't try to statically resolve the optional peer dep at build time.
  • parseTraceParent() — validates and parses W3C traceparent format.
  • shouldPropagateToTarget() — URL validation against allowed targets (string exact, wildcard, RegExp, or function matcher). Accepts either a string or a pre-parsed URL to avoid double-parsing on the hot path.
  • getDefaultPropagationTargets() — returns the allowlist for a given project URL: the exact project hostname, the wildcard strings *.supabase.co / *.supabase.in, and the localhost loopback addresses. Wildcards are linear-time string suffix checks (no regex, no ReDoS surface).

@supabase/supabase-js

  • SupabaseClient.ts — threads tracePropagation settings through to fetchWithAuth.
  • fetch.ts — when enabled is true, propagation targets are computed once at client construction (not per request). When disabled (the default), the per-request path skips all tracing work behind a single truthy check.
  • types.ts — adds tracePropagation?: TracePropagationOptions | boolean to SupabaseClientOptions.
  • helpers.tsapplySettingDefaults normalizes boolean{ enabled } and falls back to the disabled-by-default object.

Architecture

User code (with active OTel span)
  ↓
SupabaseClient (tracePropagation enabled?)
  ↓
Auth / Storage / PostgREST / Functions
  ↓
fetchWithAuth  ← single injection point, targets pre-computed
  ↓
HTTP request with traceparent / tracestate / baggage (when enabled and matched)

Per-request overhead

  • Disabled (default): sub-microsecond — one boolean check.
  • Enabled, no OTel installed: under 10 µs — hostname suffix check; the OTel import() is cached after the first attempt.
  • Enabled, OTel active, Supabase target: ~100–300 µs — dominated by propagation.inject(). Negligible compared to a typical 5–50 ms network RTT.

Testing

  • 60 unit tests in packages/shared/tracing/test/.
  • 10 fetch / trace-propagation tests in packages/core/supabase-js/test/unit/fetch.test.ts (covers default-off, boolean shorthand, non-Supabase domains, sampling decisions, existing-header preservation).
  • All unit tests passing.

Test plan

npx nx build supabase-js
npx nx test @supabase/tracing
npx nx test supabase-js

References

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Mar 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 2cfd0095-8111-44c8-b7d2-e794cb4a3ab1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new

pkg-pr-new Bot commented Mar 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@supabase/auth-js

npm i https://pkg.pr.new/@supabase/auth-js@2163

@supabase/functions-js

npm i https://pkg.pr.new/@supabase/functions-js@2163

@supabase/postgrest-js

npm i https://pkg.pr.new/@supabase/postgrest-js@2163

@supabase/realtime-js

npm i https://pkg.pr.new/@supabase/realtime-js@2163

@supabase/storage-js

npm i https://pkg.pr.new/@supabase/storage-js@2163

@supabase/supabase-js

npm i https://pkg.pr.new/@supabase/supabase-js@2163

commit: 21a3186

@mandarini mandarini added the do-not-merge Do not merge this PR. label Mar 11, 2026
Comment thread package.json Outdated
Comment thread packages/core/supabase-js/README.md Outdated
Comment thread packages/core/supabase-js/README.md Outdated
Comment thread packages/core/supabase-js/README.md Outdated
Comment thread packages/core/supabase-js/README.md Outdated
Comment thread packages/core/supabase-js/README.md Outdated
Comment thread packages/core/supabase-js/README.md Outdated
Comment thread packages/core/supabase-js/src/SupabaseClient.ts Outdated
@grdsdev grdsdev requested a review from Ziinc March 16, 2026 18:01
@mandarini mandarini force-pushed the feat/trace-propagation branch 2 times, most recently from 38b0bee to 0490747 Compare April 21, 2026 14:36
@grdsdev grdsdev changed the title [POC] W3C/OpenTelemetry Trace Context Propagation feat(supabase-js): W3C/OpenTelemetry trace context propagation Apr 22, 2026
@grdsdev grdsdev force-pushed the feat/trace-propagation branch 2 times, most recently from 96c4e64 to a289e5e Compare April 22, 2026 08:51
@mandarini mandarini changed the title feat(supabase-js): W3C/OpenTelemetry trace context propagation feat(supabase): W3C/OpenTelemetry trace context propagation May 4, 2026
grdsdev and others added 10 commits May 13, 2026 14:20
Implements automatic trace context propagation for distributed tracing
across all Supabase services. When enabled, the SDK automatically
attaches trace context headers (traceparent, tracestate, baggage) to
outgoing requests, enabling end-to-end request tracing from client
applications through Supabase services.

- Created shared package for trace context utilities
- Implements W3C Trace Context specification parsing
- Supports OpenTelemetry API integration with graceful fallback
- Provides URL target validation (string, RegExp, function matchers)
- Includes 63 unit tests with 85%+ coverage

- Updated fetchWithAuth to inject trace headers automatically
- Added TracePropagationOptions configuration API
- Updated SupabaseClient to support trace propagation
- Added Realtime WebSocket trace context via query parameters
- Added 9 integration tests for trace propagation scenarios
- Added comprehensive documentation to README

- **Auto-detection**: Automatically detects active trace context from
  OpenTelemetry API
- **Custom extractors**: Support for custom tracing systems
- **Security-first**: Only propagates to Supabase domains by default
- **Configurable targets**: Support for custom allowed domains
- **Sampling aware**: Respects upstream sampling decisions
- **Zero breaking changes**: Fully backward compatible

- `auto` (default): Automatically detect and propagate trace context
- `off`: Disable trace propagation
- `manual`: Only propagate manually set headers

- All builds pass (nx build supabase-js)
- All type checks pass
- 63 unit tests pass in trace-propagation package
- 95 unit tests pass in supabase-js (including new tests)
- 85%+ code coverage across all modules

Related: Linear SDK-578

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Rename @supabase/trace-propagation to @supabase/tracing for consistency
with naming conventions across the codebase.

Changes:
- Rename packages/shared/trace-propagation to packages/shared/tracing
- Update package name from @supabase/trace-propagation to @supabase/tracing
- Update all imports in supabase-js package
- Update workspace configuration
- Rename test-trace-propagation to test-tracing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove the customExtractor option from trace propagation configuration.
Users should rely on OpenTelemetry API for trace context extraction.

Changes:
- Remove customExtractor field from TracePropagationOptions interface
- Remove customExtractor parameter from extractTraceContext function
- Update all code that used customExtractor
- Remove custom extractor tests
- Update documentation in READMEs

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Simplify trace propagation API by changing from string-based mode
('auto' | 'off' | 'manual') to a simple boolean flag (enabled).

Changes:
- Replace TracePropagationMode type with boolean enabled field
- Remove mode field from TracePropagationOptions interface
- Add enabled?: boolean field (defaults to true)
- Update all code to check enabled flag instead of mode
- Remove 'manual' mode functionality
- Update tests to use boolean flag
- Update documentation in README

BREAKING CHANGE: The tracePropagation.mode option has been replaced with
tracePropagation.enabled. Update from { mode: 'auto' } to { enabled: true }
and from { mode: 'off' } to { enabled: false }.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove the ability for users to customize which domains receive trace
context headers. Trace propagation is now restricted to Supabase domains
only (*.supabase.co, *.supabase.in, localhost) for security.

Changes:
- Remove targets field from TracePropagationOptions interface
- Remove TracePropagationTarget import from types
- Update code to always use default Supabase domains
- Remove custom targets test case
- Update documentation to reflect fixed target domains

BREAKING CHANGE: The tracePropagation.targets option has been removed.
Trace context is now only propagated to Supabase domains for security.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove trace context propagation for Realtime WebSocket connections.
Trace propagation is now only applied to HTTP requests (REST API, Storage,
Functions, Auth) and not to WebSocket connections.

Changes:
- Remove trace context extraction from _initRealtimeClient
- Remove extractTraceContext import from SupabaseClient.ts
- Simplify Realtime client initialization

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mandarini mandarini force-pushed the feat/trace-propagation branch from a289e5e to 75b40da Compare May 13, 2026 12:21
@mandarini mandarini marked this pull request as ready for review May 13, 2026 12:22
@mandarini mandarini requested review from a team as code owners May 13, 2026 12:22
@mandarini mandarini force-pushed the feat/trace-propagation branch from 82371b1 to 8fd3f59 Compare May 13, 2026 12:26
@mandarini mandarini merged commit 2fe1801 into master May 13, 2026
26 checks passed
@mandarini mandarini deleted the feat/trace-propagation branch May 13, 2026 15:14
mandarini pushed a commit to supabase/ssr that referenced this pull request May 18, 2026
This PR updates `@supabase/supabase-js` to v2.106.0.

**Source**: supabase-js-stable-release

---

## Release Notes

## v2.106.0

## 2.106.0 (2026-05-18)

### 🚀 Features

- **supabase:** W3C/OpenTelemetry trace context propagation
([#2163](supabase/supabase-js#2163))

### 🩹 Fixes

- **auth:** return null user and session for email_change
single-confirmation verifyOtp
([#2378](supabase/supabase-js#2378))
- **release:** mark @supabase/tracing private and snapshot it for JSR
([#2370](supabase/supabase-js#2370))
- **storage:** make StreamDownloadBuilder implement Promise and memoize
executor ([#2367](supabase/supabase-js#2367))

### ❤️ Thank You

- Claude Sonnet 4.5
- Guilherme Souza
- Katerina Skroumpelou @mandarini
- oniani1

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
@Tofandel

Tofandel commented May 19, 2026

Copy link
Copy Markdown

This PR breaks the build on react native ios builds (metro bundler), specifically this

otelModulePromise = import(/* webpackIgnore: true */
                                                        ^ Invalid expression encountered
(warning truncated)

I believe a
/* @metro-ignore */ is needed as well

@mandarini

Copy link
Copy Markdown
Contributor

Working on it! @Tofandel

mandarini pushed a commit to supabase/supabase that referenced this pull request May 19, 2026
This PR updates @supabase/*-js libraries to version 2.106.0.

**Source**: supabase-js-stable-release

**Changes**:
- Updated @supabase/supabase-js to 2.106.0
- Updated @supabase/auth-js to 2.106.0
- Updated @supabase/realtime-js to 2.106.0
- Updated @supabase/postgest-js to 2.106.0
- Refreshed pnpm-lock.yaml

---

## Release Notes

## v2.106.0

## 2.106.0 (2026-05-18)

### 🚀 Features

- **supabase:** W3C/OpenTelemetry trace context propagation
([#2163](supabase/supabase-js#2163))

### 🩹 Fixes

- **auth:** return null user and session for email_change
single-confirmation verifyOtp
([#2378](supabase/supabase-js#2378))
- **release:** mark @supabase/tracing private and snapshot it for JSR
([#2370](supabase/supabase-js#2370))
- **storage:** make StreamDownloadBuilder implement Promise and memoize
executor ([#2367](supabase/supabase-js#2367))

### ❤️ Thank You

- Claude Sonnet 4.5
- Guilherme Souza
- Katerina Skroumpelou @mandarini
- oniani1

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
@mandarini

Copy link
Copy Markdown
Contributor

@Tofandel Can you please test 2.106.1-beta.0? It's a beta release from #2381

@mandarini

Copy link
Copy Markdown
Contributor

Ref: #2380

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.

6 participants