netlify/context-and-tools1 file

Netlify Blobs

Store and retrieve unstructured data like file uploads, images, documents, JSON, and cache-like state with Netlify Blobs. It is a zero-config key/value store accessible from Functions, Edge Functions, and Build Plugins. Reach for this when you handle a file or image upload, persist Background Function output like sitemaps or processed media, cache API responses, store per-deploy assets, or need a simple key/value store from a serverless function. Covers creating site and deploy stores, reading and writing values and JSON, listing keys with metadata, atomic conditional writes, consistency modes, region selection, and file-based uploads. Not for per-user, transactional, or relational data — use Netlify DB for that.

Specification
Skill ID
netlify/context-and-tools/netlify-blobs
Publisher
netlify
Repository
context-and-tools
Installs
0
Files
1
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

netlify/context-and-tools/netlify-blobsInstalls these files
  • SKILL.md

What this skill tells the agent

Netlify Blobs

Modern syntax — import from @netlify/blobs, open a store, operate on it:

import { getStore } from "@netlify/blobs";
const store = getStore("file-uploads");        // site-wide
await store.set(key, value, { metadata: { … } });
const entry = await store.get(key);            // null if missing

Deploy-specific isolation:

import { getDeployStore } from "@netlify/blobs";
const store = getDeployStore("file-uploads");

Requires Fetch API (Node.js 18+). Functions written in Go cannot access Blobs. Not for per-user/transactional/relational data — use Netlify DB.

Footguns (read first)

  • Site-scoped stores (`getStore`) 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.
  • Site-wide stores do NOT follow your functions region. getStore defaults to us-east-2 regardless of where your functions run — no error or warning is raised. To use another region you must pass region on every getStore call for that store (reads, writes, deletes); a call that omits it hits us-east-2 and won't see data held elsewhere. Changing a store's region does not migrate data.
  • Last write wins. No concurrency control. Do not build counters, balances, or read-modify-write logic on a blob key — even with onlyIfMatch retries. That's transactional data; use Netlify DB.
  • No built-in access control. The serving function is the gate. Default to private: gate reads behind an authenticated function rather than exposing blobs publicly. Treat user input as unsafe — don't serve arbitrary caller-supplied keys; scope keys with something callers can't tamper with.
  • Eventual consistency by default — updates/deletions take up to 60s to propagate. Pass consistency: "strong" if a read must see the latest write immediately (slower reads).
  • When an operation fails, surface the error and read the function logs. Do not invent REST endpoints or side-channel APIs to retry.

Store selection

  • getStore(name) — site-wide; persists across deploys, readable from all contexts.
  • getDeployStore(name) — scoped to one deploy; use for isolation and for any write from a Build Plugin or file-based upload.
  • Build Plugins: can read from any of the site's stores, but can write only to deploy-specific stores (getDeployStore).

Both accept a positional form getStore(name, { region, siteID, token }) / getDeployStore(name, { deployID, region, siteID, token }) or an object form getStore({ name, consistency, region, siteID, token, fetch }). siteID, deployID, and token are set automatically inside Functions/Edge Functions/Build Plugins; supply them explicitly only to override (e.g. siteID of another site you own). region is auto-set only for getDeployStore (defaults to your functions region); for getStore it is not auto-set and defaults to us-east-2. Site ID = API site_id = NETLIFY_SITE_ID = the UI's Project ID.

Common tasks

Persist an 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 uploads = getStore("file-uploads");
  await uploads.set(uuid(), file, {
    metadata: { country: context.geo.country.name }
  });
  return new Response("Submission saved");
};

Persist JSON — use setJSON:

const uploads = getStore("json-uploads");
await uploads.setJSON(key, data, { metadata: { … } });

Read a blob (null if missing):

const entry = await uploads.get(key);
if (entry === null) return new Response("Not found", { status: 404 });
return new Response(entry);

Read with metadata:

const { data, metadata } = await uploads.getWithMetadata(key);

Delete / delete a whole store:

await uploads.delete(key);
const { deletedBlobs } = await uploads.deleteAll(); // 0 if store didn't exist

API surface

Store-opening: getStore, getDeployStore, listStores (imported from @netlify/blobs). Store instance methods: get, getWithMetadata, getMetadata, set, setJSON, list, delete, deleteAll.

`set(key, value, { metadata, onlyIfMatch, onlyIfNew })` — value is ArrayBuffer | Blob | string. Overwrites by default. Resolves { modified, etag }. `setJSON(key, value, { metadata, onlyIfMatch, onlyIfNew })` — same, value any JSON-serializable. `get(key, { consistency, type })` — type one of text (default) / json / arrayBuffer / blob / stream. Resolves the value, or null if missing. `getWithMetadata(key, { consistency, etag, type })` — resolves { data, etag, metadata }, or null if missing. If etag matches the passed value, data is null (cache still fresh). `getMetadata(key, { consistency, etag, type })` — resolves { metadata, etag }, or null if missing. Check existence without downloading the blob. `list({ directories, paginate, prefix })` — resolves { blobs: [{ etag, key }], directories: string[] }. `listStores({ paginate })` — resolves { stores: string[] }. Does not include deploy-specific stores. `delete(key)` — resolves undefined. `deleteAll()` — resolves { deletedBlobs }; deleting a store is deleting all its blobs.

Atomic conditional writes

  • onlyIfNew: true — write only if the key does not exist.
  • onlyIfMatch: etag — write only if current ETag matches (optimistic concurrency).
  • Inspect the returned modified boolean to detect success/failure.
const { modified } = await emails.set("jane@netlify.com", "Jane Doe", { onlyIfNew: true });
if (!modified) return new Response("Email already exists", { status: 400 });

(These are for single-key create-if-absent / compare-and-set, not for building transactional counters.)

Listing hierarchically

Group keys with /. list({ directories: true }) returns top-level directories plus root blobs. Drill in with prefix — the prefix must include a trailing slash ("cats/"), or keys like catsuit also match.

const { blobs, directories } = await animals.list({ directories: true });
const cats = await animals.list({ directories: true, prefix: "cats/" });

Pagination

Server pages up to 1,000 entries (list) / 1,000 stores (listStores). Handled automatically by default; pass paginate: true for an AsyncIterator:

for await (const entry of store.list({ paginate: true })) {
  console.log(entry.blobs);
}

Conditional requests / local caching

Pass a cached etag to getWithMetadata/getMetadata; if it matches, data is null (your copy is fresh). Compare the whole value including surrounding quotes and any weakness prefix.

Configuration

Consistency

Default is eventual (single-region, edge-cached; propagation within 60s). Opt into strong per store or per read:

const store = getStore({ name: "animals", consistency: "strong" }); // store level
const dog = await store.get("dog", { consistency: "strong" });      // operation level

The Netlify CLI always uses strong consistency.

Regions

Valid regions (a smaller set than the function regions): us-east-1, us-east-2, eu-central-1, ap-southeast-1, ap-southeast-2.

  • Deploy-specific stores (getDeployStore) default to your functions region; region is auto-set in Functions/Edge Functions/Build Plugins. Override explicitly: ``ts const uploads = getDeployStore({ name: "file-uploads", region: "ap-southeast-2" }); ``
  • Site-wide stores (getStore) default to us-east-2 and do not follow your functions region. region is never auto-set here. ```ts

More from netlify

Use this skill in a project

Open any RiverX project, search the Skills panel for netlify/context-and-tools/netlify-blobs, and install it in one click.

Use this skill