netlify-access-control
Picks the right Netlify site-protection layer and disambiguates the three unrelated "auth" concepts users conflate — app-user…
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
Claude Code
$ npx -y skills add netlify/context-and-tools --skill netlify-blobs --agent claude-code
Codex
$ npx skills add netlify/context-and-tools --skill netlify-blobs -a codex -y
Cursor
$ npx skills add netlify/context-and-tools --skill netlify-blobs -a cursor -y
Gemini CLI
$ npx skills add netlify/context-and-tools --skill netlify-blobs -a gemini-cli -y
GitHub Copilot
$ npx skills add netlify/context-and-tools --skill netlify-blobs -a github-copilot -y
OpenCode
$ npx skills add netlify/context-and-tools --skill netlify-blobs -a opencode -y
Windsurf
$ npx skills add netlify/context-and-tools --skill netlify-blobs -a windsurf -y
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".
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.
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.
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";`.
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);
};**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
}**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 });const { blobs } = await uploads.list(); // blobs: [{ etag, key }]`list({ directories, paginate, prefix })`. Group keys h
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.
Picks the right Netlify site-protection layer and disambiguates the three unrelated "auth" concepts users conflate — app-user…
Run AI agent tasks remotely on Netlify using Claude, Codex, or Gemini. Use when the user wants to run an AI agent on their site,…
Use Netlify AI Gateway to call OpenAI, Anthropic Claude, Google Gemini, TypeSafe (Jev), or OpenRouter-hosted models…
Cache dynamic and static responses on Netlify's CDN from Functions, Edge Functions, and proxies. Use when you add caching or…
Configure Netlify builds and routing via netlify.toml, _redirects, and _headers. Use when setting a build command or publish…
Zero-config Postgres for Netlify apps via @netlify/database — querying data from Functions/Edge Functions, writing schema…
Use this skill whenever the user mentions Horizon by name in a Laravel context. Covers the full Horizon lifecycle: installing…
Use when launching cloud VMs, Kubernetes pods, or Slurm jobs for GPU/TPU/CPU workloads, training or fine-tuning models on cloud…
Use this skill when the user mentions "pinme", or needs to upload files, store to IPFS, create/publish/deploy websites or…