Skip to content

feat(storage): expose purgeCache for buckets and single objects#2429

Merged
mandarini merged 5 commits into
masterfrom
feat/storage-purge-cache
Jun 29, 2026
Merged

feat(storage): expose purgeCache for buckets and single objects#2429
mandarini merged 5 commits into
masterfrom
feat/storage-purge-cache

Conversation

@mandarini

@mandarini mandarini commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Description

Adds storage.from(bucket).purgeCache(path) mapping to the Storage API's DELETE /cdn/{bucket}/{path} endpoint. Calling it issues a CDN invalidation for the named object.

What changed?

  • New method purgeCache(path, parameters?) on StorageFileApi. Returns { data: { message }, error: null } on success or { data: null, error } on failure. Threads parameters through to the underlying fetch (e.g. AbortController signal), matching the shape used by download().
  • Uses the existing handleOperation + _getFinalPath + remove helper plumbing. No new error types, no new dependencies, no new imports.
  • Three mock-based tests covering: happy path (DELETE on /cdn/{bucket}/{path} returns { message }), server 404 surfacing as StorageApiError, and signal forwarding through parameters.

Why was this change needed?

Cache purging has been a long-standing ask. This supersedes #1742 (originally proposed by @mansueli in supabase/storage-js#229).

What this PR deliberately leaves out vs #1742:

  • No purgeCacheByPrefix. The Storage server has no native prefix-purge endpoint. A client-side list() + N individual DELETEs loop is silently non-recursive (the storage list endpoint is not recursive), unpaginated past 1000 objects, and sequential per object even with a batchSize knob. If users need bulk invalidation, that should be solved server-side.
  • No client-side wildcard or empty-path validation. The server's error responses are the source of truth.

Two operational gotchas:

  1. The endpoint enforces service_role JWT (enforceJwtRoles: [dbServiceRole] in storage/src/http/routes/cdn/index.ts). Anon or user JWT calls will be rejected.
  2. The endpoint is gated behind the purgeCache tenant feature, and self-hosted deployments need CDN_PURGE_ENDPOINT_URL configured.

Supersedes #1742.

Screenshots/Examples

// purge entire bucket cache
const { data, error } = await storage.purgeBucketCache('bucket-name')

// purge individual object cache
const { data, error } = await supabase.storage
  .from('avatars')
  .purgeCache('folder/avatar1.png' )

// purge only transformations:
await supabase.storage
  .from('avatars')
  .purgeCache('folder/avatar1.png', { transformations: true })

// Cancellation:
const controller = new AbortController()
await supabase.storage
  .from('avatars')
  .purgeCache('folder/avatar1.png', undefined, { signal: controller.signal })

Breaking changes

  • This PR contains no breaking changes

Additive: a new public method, no changes to any existing API.

Checklist

  • I have read the Contributing Guidelines
  • My PR title follows the conventional commit format: <type>(<scope>): <description>
  • I have run pnpm nx format to ensure consistent code formatting
  • I have added tests for new functionality (if applicable)
  • I have updated documentation (if applicable)

Additional notes

Server-side cross-reference for reviewers:

  • Route: src/http/routes/cdn/purgeCache.ts in supabase/storage. The route is fastify.delete('/:bucketName/*') mounted under /cdn.
  • Auth gate: src/http/routes/cdn/index.ts registers the route with enforceJwtRoles: [dbServiceRole] and requireTenantFeature('purgeCache').
  • CDN delegation: src/storage/cdn/cdn-cache-manager.ts forwards to CDN_PURGE_ENDPOINT_URL (or errors out if unset).

No integration test against the local Docker storage stack is included. The CDN purge endpoint requires CDN_PURGE_ENDPOINT_URL plus the purgeCache tenant feature, neither of which are configured in the local compose, so an integration test would either skip or fail spuriously. A manual smoke test against a hosted project with the service_role key is recommended before promoting the canary to stable.

@github-actions github-actions Bot added the storage-js Related to the storage-js library. label Jun 4, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 4, 2026

Copy link
Copy Markdown

Open in StackBlitz

@supabase/auth-js

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

@supabase/functions-js

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

@supabase/postgrest-js

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

@supabase/realtime-js

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

@supabase/storage-js

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

@supabase/supabase-js

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

commit: 21a31f9

@mandarini mandarini force-pushed the feat/storage-purge-cache branch from 7964a89 to a719dd5 Compare June 4, 2026 12:12
@mandarini mandarini marked this pull request as ready for review June 4, 2026 12:13
@mandarini mandarini requested review from a team as code owners June 4, 2026 12:13
@mandarini mandarini self-assigned this Jun 4, 2026
Comment thread packages/core/storage-js/migrations/purge-cache.md Outdated
spydon
spydon previously approved these changes Jun 4, 2026
spydon
spydon previously approved these changes Jun 4, 2026
@mandarini mandarini added the do-not-merge Do not merge this PR. label Jun 4, 2026
@mandarini

Copy link
Copy Markdown
Contributor Author

do not merge until @itslenny works on this

@itslenny itslenny force-pushed the feat/storage-purge-cache branch 2 times, most recently from cfa1faf to 23d176f Compare June 22, 2026 17:26
itslenny
itslenny previously approved these changes Jun 22, 2026
Comment thread packages/core/storage-js/test/storageFileApi.test.ts Outdated
@itslenny itslenny force-pushed the feat/storage-purge-cache branch 2 times, most recently from 4b9ae03 to 59d1f93 Compare June 22, 2026 21:23
@itslenny itslenny requested a review from grdsdev June 22, 2026 21:24
grdsdev
grdsdev previously approved these changes Jun 22, 2026
@itslenny itslenny removed the do-not-merge Do not merge this PR. label Jun 25, 2026
itslenny
itslenny previously approved these changes Jun 25, 2026
@itslenny itslenny requested review from grdsdev and itslenny June 26, 2026 16:35
Comment thread packages/core/storage-js/src/packages/StorageBucketApi.ts Outdated
Comment thread packages/core/storage-js/test/storageBucketApi.test.ts Outdated
@ferhatelmas

ferhatelmas commented Jun 26, 2026

Copy link
Copy Markdown
Member
const controller = new AbortController()
await supabase.storage
  .from('avatars')
  .purgeCache({ object: 'folder/avatar1.png' }, { signal: controller.signal })

This example from PR description needs an update as well, it might be from a previous iteration

ferhatelmas
ferhatelmas previously approved these changes Jun 26, 2026
@itslenny itslenny requested a review from ferhatelmas June 26, 2026 19:10
grdsdev added a commit to supabase/sdk that referenced this pull request Jun 29, 2026
…ilities (#44)

* feat(storage): add purge_cache and purge_bucket_cache capabilities

Registers two new canonical storage capabilities sourced from
supabase/supabase-js#2429:

- storage.file_buckets.purge_cache — invalidate CDN cache for a single
  object via StorageFileApi.purgeCache(path)
- storage.file_buckets.purge_bucket_cache — invalidate CDN cache for an
  entire bucket via StorageBucketApi.purgeBucketCache()

Both require a service_role JWT and the purgeCache tenant feature.

* docs(storage): add spec files for purge_cache and purge_bucket_cache

Adds human-readable specs for the two new CDN cache purge capabilities:

- specs/storage/file_buckets/purge_cache.md
- specs/storage/file_buckets/purge_bucket_cache.md

Covers the HTTP endpoint, behavioral contract, service_role requirement,
purgeCache tenant feature prerequisite, error conditions, and cross-links.

* docs(storage): trim specs to SDK-only behavior

* docs(storage): remove spec files for purge_cache and purge_bucket_cache

* docs(storage): use 'capability' term in purge cache descriptions

* docs(storage): update purge cache descriptions to mention transformations option
@mandarini mandarini merged commit 7583ac5 into master Jun 29, 2026
25 of 26 checks passed
@mandarini mandarini deleted the feat/storage-purge-cache branch June 29, 2026 13:35
mandarini pushed a commit to supabase/ssr that referenced this pull request Jun 30, 2026
This PR updates `@supabase/supabase-js` to v2.110.0.

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

---

## Release Notes

## v2.110.0

## 2.110.0 (2026-06-30)

### 🚀 Features

- **repo:** drop Node.js 20 support
([#2482](supabase/supabase-js#2482))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
## v2.109.0

## 2.109.0 (2026-06-30)

### 🚀 Features

- **auth:** add custom_claims_allowlist to custom providers admin API
([#2473](supabase/supabase-js#2473))
- **realtime:** add postgres_changes filter builder, new operators and
select ([#2463](supabase/supabase-js#2463))
- **storage:** expose purgeCache for buckets and single objects
([#2429](supabase/supabase-js#2429))

### 🩹 Fixes

- **functions:** honor a caller's Content-Type override regardless of
casing ([#2455](supabase/supabase-js#2455))
- **realtime:** pin @supabase/phoenix and browser test CDN deps
([#2457](supabase/supabase-js#2457))
- **realtime:** add replication connection system message option
([#2470](supabase/supabase-js#2470))
- **storage:** keep sortBy defaults when list() is given a partial
sortBy ([#2454](supabase/supabase-js#2454))

### ❤️ Thank You

- Anubhav Anand @i-anubhav-anand
- Cemal Kılıç @cemalkilic
- Claude Opus 4.8 (1M context)
- Filipe Cabaço @filipecabaco
- Katerina Skroumpelou @mandarini
- Lenny
- Rodrigo Mansueli @mansueli
## v2.108.2

## 2.108.2 (2026-06-15)

### 🩹 Fixes

- **auth:** preserve valid session on refresh failure and cooldown
repeat failures
([#2436](supabase/supabase-js#2436))
- **realtime:** clarify httpSend() 404 error and server migration note
([#2444](supabase/supabase-js#2444))
- **release:** pin Deno and bound JSR publish to survive stranded-task
hangs ([#2439](supabase/supabase-js#2439))
- **release:** restore JSR publish flags and enable for beta
([#2440](supabase/supabase-js#2440))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
## v2.108.1

## 2.108.1 (2026-06-09)

### 🩹 Fixes

- **ci:** forward DOGFOOD_APP_CLIENT_ID to dogfood workflow
([#2434](supabase/supabase-js#2434))
- **postgrest:** then typing
([#2349](supabase/supabase-js#2349))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
- Vaibhav @7ttp

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
mandarini pushed a commit to supabase/supabase that referenced this pull request Jul 7, 2026
This PR updates @supabase/*-js libraries to version 2.110.1.

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

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

---

## Release Notes

## v2.110.1

## 2.110.1 (2026-07-07)

### 🩹 Fixes

- **auth:** defer init-time notifications until initializePromise
resolves ([#2498](supabase/supabase-js#2498))
- **realtime:** suppress disconnected status from onHeartbeat consumers
([#2496](supabase/supabase-js#2496))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
## v2.110.0

## 2.110.0 (2026-06-30)

### 🚀 Features

- **repo:** drop Node.js 20 support
([#2482](supabase/supabase-js#2482))

### ❤️ Thank You

- Katerina Skroumpelou @mandarini
## v2.109.0

## 2.109.0 (2026-06-30)

### 🚀 Features

- **auth:** add custom_claims_allowlist to custom providers admin API
([#2473](supabase/supabase-js#2473))
- **realtime:** add postgres_changes filter builder, new operators and
select ([#2463](supabase/supabase-js#2463))
- **storage:** expose purgeCache for buckets and single objects
([#2429](supabase/supabase-js#2429))

### 🩹 Fixes

- **functions:** honor a caller's Content-Type override regardless of
casing ([#2455](supabase/supabase-js#2455))
- **realtime:** pin @supabase/phoenix and browser test CDN deps
([#2457](supabase/supabase-js#2457))
- **realtime:** add replication connection system message option
([#2470](supabase/supabase-js#2470))
- **storage:** keep sortBy defaults when list() is given a partial
sortBy ([#2454](supabase/supabase-js#2454))

### ❤️ Thank You

- Anubhav Anand @i-anubhav-anand
- Cemal Kılıç @cemalkilic
- Claude Opus 4.8 (1M context)
- Filipe Cabaço @filipecabaco
- Katerina Skroumpelou @mandarini
- Lenny
- Rodrigo Mansueli @mansueli

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

storage-js Related to the storage-js library.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants