Skip to content
Deployment
Skill

/netlify-blobs

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

From plugin
netlify-skills
4915 skills1 MCP

Claude Code

$ npx -y skills add netlify/context-and-tools --skill netlify-blobs --agent claude-code

SKILL.md

netlify-blobs.SKILL.md
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`, open a store, then call methods on the handle.

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. It is 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 and shared across all deploys. Data persists across deploys, and previews see production data.
  • `getDeployStore(name)`: deploy-specific and scoped to one deploy. It stays in sync on rollback and is cleaned up on deploy deletion. Use it 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

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` is omitted when no new entry was generated.

Persisting a user upload (Function)

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)

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

**Null-check before destructuring.** `getWithMetadata` and `getMetadata` resolve to `null` when the key is absent. Destructuring the return value directly (`const { data } = await store.getWithMetadata(key)`) throws on a missing key. Assign the result to a variable, check for `null`, then read its fields.

// getWithMetadata: data + metadata + etag
const entry = await uploads.getWithMetadata(key);
if (entry === null) return new Response(`Not found: ${key}`, { status: 404 });
const { data, metadata } = entry;
return new Response(data, { headers: { "X-Country": metadata.country } });

// getMetadata: metadata + etag only, without downloading the blob
const meta = await uploads.getMetadata(key); // { etag, metadata } or null
if (meta === null) { /* missing */ }

Both accept `{ consistency, etag, type }`.

**Conditional read:** pass a cached `etag`. If it still matches server-side, `data` is `null`, which means your copy is fresh. Compare the whole ETag value, including surrounding quotes and any weakness prefix.

const entry = await uploads.getWithMetadata("my-key", { etag: cachedETag });
if (entry !== null && entry.etag === cachedETag) {
  // entry.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 }`:

// 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

const { blobs } = await uploads.list(); // blobs: [{ etag, key }]

`list({ directories, paginate, prefix })`. Group keys h

Read more
@netlify
Ships withnetlify-skills

Public Netlify skills for AI coding agents. Each skill is a focused, factual reference for a Netlify platform primitive — designed to help agents build correctly on Netlify without needing to search docs.

4915 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
49
Star history of netlify/context-and-tools on GitHub, a chart by star-history.com

Other skills on netlify-skills.

netlify-caching

netlify-caching

Cache dynamic and static responses on Netlify's CDN from Functions, Edge Functions, and proxies. Use when you add caching or…

netlify-config

netlify-config

Configure Netlify builds and routing via netlify.toml, _redirects, and _headers. Use when setting a build command or publish…

netlify-database

netlify-database

Zero-config Postgres for Netlify apps via @netlify/database — querying data from Functions/Edge Functions, writing schema…

More skills from other plugins.

configuring-horizon

configuring-horizon

Use this skill whenever the user mentions Horizon by name in a Laravel context. Covers the full Horizon lifecycle: installing…

coolify10d66k
@coollabsio@coollabsioInstall Skill
vercel-cli

vercel-cli

Guides deployment, configuration, and troubleshooting with the Vercel CLI. Use when operating Vercel projects or resources…

vercel12d16k
@vercel@vercelInstall Skill
skypilot

skypilot

Use when launching cloud VMs, Kubernetes pods, or Slurm jobs for GPU/TPU/CPU workloads, training or fine-tuning models on cloud…

@skypilot-org@skypilot-orgInstall Skill
pinme

pinme

Use this skill when the user mentions "pinme", or needs to upload files, store to IPFS, create/publish/deploy websites or…

pinme2mo3.8k
@glitternetwork@glitternetworkInstall Skill

Questions about netlify-blobs.

Which coding agents can use netlify-blobs?
netlify-blobs installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.