@leconome/payload-storage-ipfs
v0.2.0
Published
Payload CMS v3 storage adapter that pins uploads to IPFS via an IPFS Cluster ingest API and serves them from an IPFS gateway.
Maintainers
Readme
@leconome/payload-storage-ipfs
A Payload CMS v3 storage adapter that routes upload-collection files to IPFS. Each upload is pinned through an IPFS Cluster–style ingest API, served from an IPFS gateway via its stored CID, and unpinned when the document is deleted.
It targets the Econome IPFS storage center ingest API, but works with any HTTP endpoint that implements the small contract below.
Install
pnpm add @leconome/payload-storage-ipfs @payloadcms/plugin-cloud-storagepayload and @payloadcms/plugin-cloud-storage are peer dependencies.
Usage
import { buildConfig } from "payload";
import { ipfsStorage } from "@leconome/payload-storage-ipfs";
export default buildConfig({
collections: [
{
slug: "media",
upload: true,
fields: [],
},
],
plugins: [
ipfsStorage({
apiUrl: process.env.IPFS_API_URL!, // https://api.example.com
apiKey: process.env.IPFS_API_KEY!, // an ingest API key
gatewayUrl: process.env.IPFS_GATEWAY_URL!, // https://ipfs.io
collections: {
media: true,
},
}),
],
});That's it — uploads to the media collection are now pinned to IPFS. The
adapter adds a read-only cid field to each upload doc and serves files
from ‹gatewayUrl›/ipfs/‹cid›.
Options
| Option | Type | Description |
| --- | --- | --- |
| apiUrl | string | Base URL of the ingest API (no trailing slash). |
| apiKey | string | Sent as the x-api-key header on every request. |
| gatewayUrl | string | IPFS gateway used to build URLs and serve content. |
| collections | Record<string, true \| CollectionOptions> | Upload collections to route to IPFS. |
| enabled | boolean | Set false to disable (e.g. in local dev). |
How it works
- Upload →
POST {apiUrl}/ingest(multipart fieldfile, headerx-api-key) → expects{ "cid": "…" }. The CID is stored on the doc. - URL →
generateURLreturns{gatewayUrl}/ipfs/{cid}. - Serve →
staticHandlerlooks up the doc by filename and streams the bytes from the gateway (cached, immutable). - Delete →
DELETE {apiUrl}/ingest/{cid}(headerx-api-key) to unpin.
API contract (for other backends)
Any server implementing these two routes works:
POST /ingest— multipartfile,x-api-keyheader →200 { "cid": string }DELETE /ingest/:cid—x-api-keyheader →200
Caveats
- Shared CIDs: identical content yields the same CID. Deleting one document unpins content that another document might still reference. Dedupe upstream if that matters.
- Public content: gateway URLs are public. Don't use this for private files unless your gateway enforces access control.
License
MIT
