netlify-blobs
Store and retrieve unstructured files, objects, and cached data on Netlify using the @netlify/blobs module from Functions, Edge Functions, or Build Plugins.
Identity proven by GitHub's verified organisation badge (netlify)
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
- License
- MIT (permissive)
- Pin
- b5894c0pinned Oct 9, 2026
- Context cost
- about 2,900 tokenswhen the skill loads
- GitHub stars
- 39on netlify/context-and-tools
- Scripts
- None found
What it does
“Store and retrieve unstructured objects, files, and cache-like state on Netlify with the @netlify/blobs module. Use when persisting user file uploads (images/documents), caching computed output from functions or Background Functions, serving downloadable assets, storing JSON blobs keyed by ID, or seeding deploy-specific data. Reach for this for key/value or object storage from Functions, Edge Functions, or Build Plugins — not for per-user, transactional, or relational data (use Netlify DB for that). Triggers include "save an uploaded file", "cache API results", "store generated site map", "key/value store for a function", or "file uploads without a database".”
Generated summary from the skill's own text
Store and retrieve unstructured files, objects, and cached data on Netlify using the @netlify/blobs module from Functions, Edge Functions, or Build Plugins. Choose between site-wide stores (shared across all deploys) or deploy-specific stores (isolated per deployment) to persist user uploads, cache computed results, or maintain key-value state without a traditional database.
When to use it
- Persisting user file uploads like images or documents
- Caching API results or generated output from functions
- Storing JSON objects or files keyed by unique identifier
- Implementing key-value storage for temporary or semi-permanent state
When not to
- For per-user, transactional, or relational data (use Netlify DB instead)
- Building counters, balances, or read-modify-write operations
- When you need server-side expiration or automatic TTL on objects
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add netlify/context-and-tools --skill netlify-blobsInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install netlify/context-and-tools netlify-blobs@b5894c0e1c16d924dd5b28546b11c2de650fce7eInstalls the commit we checked (b5894c0).
Copy by hand
Download the folder at b5894c0 and put it in .claude/skills/netlify-blobs for Claude Code and in .agents/skills/netlify-blobs for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Claude Code plugin
/plugin install netlify-skills@claude-plugins-officialThe publisher's plugin in Claude's official marketplace. It may hold more than this skill, and we have not pinned it.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "netlify-blobs" by Netlify. Source, pinned to commit b5894c0e1c16d924dd5b28546b11c2de650fce7e: https://github.com/netlify/context-and-tools/tree/b5894c0e1c16d924dd5b28546b11c2de650fce7e/agent-plugin/skills/netlify-blobs 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/netlify-blobs/ for Claude Code and .agents/skills/netlify-blobs/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/netlify/netlify-blobs
SKILL.md at the pin
---
name: netlify-blobs
description: Store and retrieve unstructured objects, files, and cache-like state on Netlify with the @netlify/blobs module. Use when persisting user file uploads (images/documents), caching computed output from functions or Background Functions, serving downloadable assets, storing JSON blobs keyed by ID, or seeding deploy-specific data. Reach for this for key/value or object storage from Functions, Edge Functions, or Build Plugins — not for per-user, transactional, or relational data (use Netlify DB for that). Triggers include "save an uploaded file", "cache API results", "store generated site map", "key/value store for a function", or "file uploads without a database".
---
# Netlify Blobs
Modern syntax — import from `@netlify/blobs` and open a store, then call methods on the handle:
```ts
import { getStore, getDeployStore, listStores } from "@netlify/blobs";
import type { Context } from "@netlify/functions"; // or "@netlify/edge-functions"
```
In Functions, Edge Functions, and Build Plugins, `siteID`, `deployID`, `token` (and `region` for `getDeployStore`) are injected automatically. Install with `npm install @netlify/blobs`.
**Not a database.** For dynamic, per-user, transactional, or relational data, use Netlify DB. Blobs is for objects, files, and cache-like state, optimized for frequent reads and infrequent writes.
**Store scope is a footgun — read this first.** `getStore` opens a **site-wide store shared across ALL deploy contexts**: code on a deploy preview reads, overwrites, and deletes production data. Never run destructive tests or seed throwaway data from a preview against a site-wide store. Use `getDeployStore()` or a context-specific store name for isolation.
## Choosing a store type
- `getStore(name)` — site-wide, shared across all deploys. Data persists across deploys; previews see production data.
- `getDeployStore(name)` — deploy-specific, scoped to one deploy. Kept in sync on rollback, cleaned up on deploy deletion. Use for isolation and for anything a failed deploy must not corrupt.
- **Build plugins can READ from any of the site's stores, but WRITE only to deploy-specific stores** (`getDeployStore`). File-based uploads also write only to deploy-specific stores.
## Core writes and reads
```ts
const uploads = getStore("file-uploads");
// set: value is ArrayBuffer | Blob | string
await uploads.set(key, file, { metadata: { country: "Spain" } });
// setJSON: any JSON-serializable value
await uploads.setJSON(key, { hello: "world" });
// get: returns value or null. type: text (default) | json | arrayBuffer | blob | stream
const entry = await uploads.get(key); // string
const obj = await uploads.get(key, { type: "json" });
if (entry === null) { /* 404 */ }
```
`set`/`setJSON` overwrite an existing key. Both return `{ modified, etag }` (`etag` omitted when no new entry was generated).
### Persisting a user upload (Function)
```ts
import { getStore } from "@netlify/blobs";
import type { Context } from "@netlify/functions";
import { v4 as uuid } from "uuid";
export default async (req: Request, context: Context) => {
const form = await req.formData();
const file = form.get("file") as File;
const key = uuid();
const uploads = getStore("file-uploads");
await uploads.set(key, file, { metadata: { country: context.geo.country.name } });
return new Response("Submission saved");
};
```
Edge functions are identical except `import type { Context } from "@netlify/edge-functions";`.
### Reading (Function)
```ts
export default async (req: Request, context: Context) => {
const { key } = context.params;
const uploads = getStore("file-uploads");
const entry = await uploads.get(key);
if (entry === null) return new Response(`Not found: ${key}`, { status: 404 });
return new Response(entry);
};
```
## Metadata and conditional reads
```ts
// getWithMetadata: data + metadata + etag; supports conditional reads
const { data, etag, metadata } = await uploads.getWithMetadata(key);
// getMetadata: metadata + etag only, without downloading the blob
const meta = await uploads.getMetadata(key); // { etag, metadata } or null
```
Both return `null` if the key is absent. Both accept `{ consistency, etag, type }`.
**Conditional read:** pass a cached `etag`; if it still matches server-side, `data` is `null` (your copy is fresh). Compare the whole ETag value including surrounding quotes and any weakness prefix.
```ts
const { data, etag } = await uploads.getWithMetadata("my-key", { etag: cachedETag });
if (etag === cachedETag) {
// data is null — cached copy still fresh
}
```
## Concurrency: atomic conditional writes
**Last write wins — there is no concurrency control.** Do NOT build counters, balances, or read-modify-write logic on a blob key, even with `onlyIfMatch` retries — that is transactional data; use Netlify DB.
`set`/`setJSON` accept `{ onlyIfNew, onlyIfMatch }`:
```ts
// Create only if key does not exist
const { modified } = await emails.set("jane@netlify.com", "Jane Doe", { onlyIfNew: true });
if (!modified) return new Response("Email already exists", { status: 400 });
// Update only if the ETag still matches
const { modified } = await emails.set("jane@netlify.com", "New Jane", { onlyIfMatch: etag });
if (!modified) return new Response("Cached data is stale", { status: 400 });
```
## Listing
```ts
const { blobs } = await uploads.list(); // blobs: [{ etag, key }]
```
`list({ directories, paginate, prefix })`. Group keys hierarchically with `/`:
```ts
const { blobs, directories } = await animals.list({ directories: true });
// directories: ["cats", "dogs"]; blobs: top-level keys only
// Drill in — trailing slash REQUIRED (without it "catsuit" also matches)
const res = await animals.list({ directories: true, prefix: "cats/" });
```
Pagination: `list` returns all pages by default (pages of up to 1,000 entries). Set `paginate: true` for an `AsyncIterator`:
```ts
for await (const page of store.list({ paginate: true })) {
console.log(page.blobs);
}
```
`listStores({ paginate })` returns `{ stores: string[] }` — **does not include deploy-specific stores** (pages of up to 1,000).
## Deleting
```ts
await uploads.delete(key); // resolves undefined
const { deletedBlobs } = await uploads.deleteAll(); // deletes every object = deletes the store
```
## Expiration (no server-side TTL)
Blobs never expire on their own. Store an expiration timestamp in metadata, check it on read, and `delete` when past:
```ts
await uploads.set(key, body, { metadata: { expiration: new Date("2025-01-01").getTime() } });
const entry = await uploads.getWithMetadata(key);
const { expiration } = entry.metadata;
if (expiration && expiration < Date.now()) await uploads.delete(key);
```
## Consistency
Default is **eventual** consistency: writes are globally available immediately, but updates/deletions propagate to all edge locations within 60 seconds. Opt into **strong** consistency per store or per read:
```ts
const store = getStore({ name: "animals", consistency: "strong" }); // whole store
await store.get("dog", { consistency: "strong" }); // single read
```
Netlify CLI always uses strong consistency.
## Regions
`region` takes an **AWS region code** (not the functions airport code). Supported (any other value throws `InvalidBlobsRegionError` before the request): `ap-southeast-1`, `ap-southeast-2`, `eu-central-1`, `us-east-1`, `us-east-2`.
- **Deploy-specific stores** default to your functions region (auto-injected).
- **Site-wide stores** default to `us-east-2` and do NOT follow your functions region.
**Footgun — site-wide region is per-call:** if you need a site-wide store in a specific region, pass `region` on **every** `getStore` call for that store (reads, writes, deletes). A call that omits it uses `us-east-2` and silently sees no data — no error or warning.
**Footgun — changing a region does not move data:** the store appears empty in the new region while data remains in the old. To migrate, copy each entry to a store opened in the new region, then delete from the old.
```ts
const uploads = getDeployStore({ name: "file-uploads", region: "ap-southeast-2" });
const profiles = getStore({ name: "user-profiles", region: "eu-central-1" });
```
## File-based uploads (no build plugin)
Place files under `.netlify/blobs/deploy` in the base directory; Netlify uploads them (preserving directory structure) to **deploy-specific stores**. Attach metadata with a sibling JSON file named `$<filename>.json` (must be valid JSON or the deploy fails).
```
.netlify/blobs/deploy/
├─ dogs/good-boy.jpg
├─ dogs/$good-boy.jpg.json # metadata for good-boy.jpg
├─ cat.jpg
└─ mouse.jpg
```
**Caution:** Netlify empties `.netlify/blobs/deploy` before each build. Files committed to your repo are NOT uploaded — create blob files during the build (build command or build plugin).
## Access control (default to private)
Blobs have **no built-in access control** — the serving function is the gate. Blobs are only reachable through your own site's code, encrypted at rest and in transit. When in doubt, default to private: gate reads behind an authenticated function rather than exposing blobs publicly. Do not serve arbitrary user-supplied keys for sensitive data; scope keys with something callers cannot tamper with. Blobs is not part of Netlify's HIPAA-compliant offering.
## Constraints
- Store names: no `/` or `:`, max 64 bytes.
- Keys: non-empty, cannot start with `/`, max 600 bytes, any Unicode (some chars >1 byte).
- Object size max 5 GB; metadata max 2 KB.
- Functions written in **Go cannot access Netlify Blobs**.
- Fetch API required (Node.js 18+); otherwise pass a custom `fetch`: `getStore({ fetch, name: "file-uploads" })`.
- Local dev (Netlify Dev) uses a sandboxed local store: no file-based uploads, cannot read production data.
- File-based uploads require continuous deployment or CLI deploys.
## When an operation fails
Surface the error and read the function logs. Do not invent REST endpoints or side-channel APIs to retry.
## CLI and UI
`netlify blobs:list/get/set/delete` exist for inspection — see the [CLI command reference](https://cli.netlify.com/commands/blobs/). Browse and download in the UI under **Data & Storage > Blobs**.
## Module version migration
If you wrote to site-wide stores with `@netlify/blobs` 6.5.0 or earlier and upgrade, those stores become inaccessible due to a namespacing change. Migrate with the latest CLI, then use module 7.0.0+:
```sh
netlify recipes blobs-migrate YOUR_STORE_NAME
```
## Reference
Full API and background: [Netlify Blobs docs](https://docs.netlify.com/build/data-and-storage/netlify-blobs/) and the [data & storage overview](https://docs.netlify.com/build/data-and-storage/overview/).
<!-- system: agent-context/blobs/system.md — human-owned, merged by ctx-gen; edit system.md, not this section -->
# Netlify house rules (blobs)
These are org conventions, not docs facts — merged into the rendered skill by
ctx-gen and never generated. Owned by the skills maintainer.
1. Blobs is not a database. For dynamic, per-user, or transactional data,
use Netlify DB — Blobs is for objects, files, and cache-like state.
2. When a store operation fails, surface the error and read the function
logs — do not invent REST endpoints or side-channel APIs to retry.
3. `netlify blobs:list/get/set/delete` exist for inspection; the CLI
reference is their source of truth — link, don't restate.
4. Blobs have no built-in access control — the serving function is the gate.
When in doubt, default to private: gate reads behind an authenticated
function rather than exposing blobs publicly.
5. Site-scoped stores are shared across ALL deploy contexts — code on a
deploy preview reads, overwrites, and deletes production data. Never run
destructive tests or seed throwaway data from previews; use
`getDeployStore()` or a context-specific store name for isolation.
6. Don't build counters, balances, or read-modify-write logic on a blob key —
even with `onlyIfMatch` retries. That's transactional data; use Netlify DB.
7. Build plugins: state BOTH halves — they can read from any of the site's
stores, but write only to deploy-specific stores (`getDeployStore`).
From netlify/context-and-tools at b5894c0, shown as plain text under its license: MIT (permissive). The license text is in agent-plugin/LICENSE.
View SKILL.md on GitHub at b5894c0Files
- SKILL.md12 KB · 4b2e885e83a1
1 file, 12 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at b5894c0Also in
The same SKILL.md content appears in these places too. We list it once and credit the oldest public source.
More from Netlify
test-skill
This skill computes an AXIS Result by weighting three metrics: task completion at 60 percent, efficiency at 30 percent, and resilience at 10 percent.
Claude Code, Codex, Cursor +4 · about 120 tokens · pinned 6 days ago
using-axis
This skill teaches an agent how to run AXIS, a synthetic testing framework for AI agents, and interpret its output.
Claude Code, Codex, Cursor +4 · about 4.9K tokens · pinned 6 days ago
netlify-access-control
This skill routes users to the correct Netlify site-protection layer for their access-control need and clarifies the three distinct authentication concepts…
Claude Code, Codex, Cursor +4 · about 2.9K tokens · pinned 6 days ago
netlify-agent-runner
Run AI agents remotely on Netlify infrastructure to automate development tasks against your site's repository.
Claude Code, Codex, Cursor +4 · about 2.6K tokens · pinned 6 days ago
netlify-ai-gateway
Calls OpenAI, Anthropic Claude, Google Gemini, TypeSafe, or OpenRouter-hosted models from Netlify Functions and Edge Functions using each provider's official…
Claude Code, Codex, Cursor +4 · about 3.3K tokens · pinned 6 days ago
netlify-caching
This skill enables caching of dynamic responses from Netlify Functions and Edge Functions on Netlify's CDN.
Claude Code, Codex, Cursor +4 · about 3.6K tokens · pinned 6 days ago
Related in Backend and APIs
netlify-edge-functions
Deploy TypeScript or JavaScript handlers to Netlify's edge network using Deno runtime for low-latency request manipulation, geolocation logic, authentication…
by NetlifyOfficial publisher
Claude Code, Codex, Cursor +4 · about 3.9K tokens · pinned 6 days ago
netlify-functions
Write and deploy serverless functions on Netlify in TypeScript, JavaScript, or Go using the modern Request/Context/Response handler API.
by NetlifyOfficial publisher
Claude Code, Codex, Cursor +4 · about 4.6K tokens · pinned 6 days ago
netlify-identity
This skill adds user authentication to Netlify sites using the @netlify/identity npm package.
by NetlifyOfficial publisher
Claude Code, Codex, Cursor +4 · about 3.4K tokens · pinned 6 days ago
nvflare-orient
Routes NVFLARE requests to appropriate downstream skills based on user intent and preliminary source inspection findings.
by NVIDIAOfficial publisher
Claude Code, Codex, Cursor +4 · about 600 tokens · pinned 6 days ago
cloudflare-email-service
This skill enables agents to send transactional emails and route incoming emails using Cloudflare's Email Service platform.
by CloudflareOfficial publisher
Claude Code, Codex, Cursor +4 · about 1.6K tokens · pinned 6 days ago
durable-objects
Build and debug Cloudflare Durable Objects applications for stateful coordination and persistent storage on the edge.
by CloudflareOfficial publisher
Claude Code, Codex, Cursor +4 · about 1.5K tokens · pinned 6 days ago
Open roles at Netlify
No open roles at Netlify on the Agent Native jobs board right now.
Browse the jobs board