This library provides a unified interface for uploading files to and deleting files from various IPFS storage providers.
Supported Providers:
- [x] Filebase [filebase.com]
- [x] Storacha [storacha.network] (formerly web3.storage)
- [x] Pinata [pinata.cloud]
- [x] Lighthouse [lighthouse.storage]
- [ ] Fleek [fleek.xyz] (Temporarily Disabled)
- Node.js: Version 18 or higher.
- Credentials: You will need API credentials for each service you intend to use. Store these securely, for example, using environment variables (see examples).
-
Storacha Setup: Storacha requires a one-time setup using their CLI (
w3
) to log in your agent (w3 login <email>
) and create/select a space (w3 space create
,w3 space use
). This library assumes the agent is already configured in the environment where the code runs.
npm install ipfs-storage
# or
yarn add ipfs-storage
-
Pinata:
uploadFile.pinata
now returns an object{ url: string, providerId: string }
. TheproviderId
is required fordeleteFile.pinata
. -
Fleek: Authentication changed (requires PAT/ProjectID), but the provider is temporarily disabled due to module resolution issues with
@fleek-platform/sdk
in ESM projects. -
Storacha (web3.storage): Authentication requires a pre-configured agent (via
w3 login
) instead of an API token. - Dependencies: Underlying SDKs for all providers have been updated.
First, set up your environment variables (e.g., in a .env
file and load with dotenv
):
# Filebase
FILEBASE_KEY=YOUR_FILEBASE_ACCESS_KEY
FILEBASE_SECRET=YOUR_FILEBASE_SECRET_KEY
FILEBASE_BUCKET=your-filebase-bucket-name
# Pinata
PINATA_JWT=YOUR_PINATA_JWT
# Storacha (No key needed here, login via w3 cli)
Then, use the library in your code:
const fs = require("fs");
const { join } = require("path");
const { uploadFile, deleteFile } = require("ipfs-storage");
const { create } = require('@web3-storage/w3up-client'); // Needed for pre-configured client example
require("dotenv").config(); // Load .env variables
// Helper function to extract CID v1 from common IPFS gateway URLs
const extractCidFromUrl = (url) => {
try {
const urlObject = new URL(url);
const hostnameParts = urlObject.hostname.split(".");
// Assuming format like https://{cid}.ipfs.{gateway}/
if (hostnameParts.length > 2 && hostnameParts[1] === "ipfs") {
return hostnameParts[0];
}
} catch (e) { /* ignore */ }
return null;
};
// Example usage (run within an async function or use .then())
async function runExamples() {
let fileBuffer;
try {
fileBuffer = fs.readFileSync(join(__dirname, "banner.png"));
} catch (err) {
console.error("Error reading file:", err);
return;
}
const fileData = { hash: "banner-test", ext: ".png", buffer: fileBuffer };
// --- Filebase Example ---
if (process.env.FILEBASE_KEY) {
try {
const filebaseConnect = {
key: process.env.FILEBASE_KEY,
secret: process.env.FILEBASE_SECRET,
bucket: process.env.FILEBASE_BUCKET,
};
const url = await uploadFile.filebase(filebaseConnect, fileData);
console.log("✅ FILEBASE Upload:", url);
await deleteFile.filebase(filebaseConnect, { hash: fileData.hash, ext: fileData.ext });
console.log("✅ FILEBASE Delete: OK");
} catch (err) {
console.error("🆘 FILEBASE:", err.message || err);
}
}
// --- Pinata Example ---
if (process.env.PINATA_JWT) {
try {
const pinataConnect = { jwt: process.env.PINATA_JWT };
// Pinata upload now returns { url, providerId }
const result = await uploadFile.pinata(pinataConnect, fileData);
console.log("✅ PINATA Upload:", result.url);
// Pinata delete now requires the providerId
await deleteFile.pinata(pinataConnect, { providerId: result.providerId });
console.log("✅ PINATA Delete: OK");
} catch (err) {
console.error("🆘 PINATA:", err.message || err);
}
}
// --- Fleek Example ---
/* // Temporarily disabled due to SDK compatibility issues
if (process.env.FLEEK_PAT && process.env.FLEEK_PROJECT_ID) {
// ... (Fleek example code commented out) ...
}
*/
// --- Storacha Example (formerly Web3.storage) ---
console.log("\n--- Storacha ---");
// Storacha requires a one-time EXTERNAL setup using `w3 login <email>`
// and `w3 space use <did>` before this library can use the ambient state.
// The library CANNOT perform the interactive email login.
console.log("Attempting Storacha using ambient agent state...");
try {
// If agent is logged in and space is set externally, this will work:
const url = await uploadFile.storacha({}, fileData);
console.log("✅ STORACHA Upload (Ambient):", url);
const cid = extractCidFromUrl(url);
if (cid) {
await deleteFile.storacha({}, { cid });
console.log("✅ STORACHA Delete (Ambient): OK");
} else {
console.error("🆘 STORACHA Delete (Ambient): Could not extract CID from URL", url);
}
} catch (err) {
console.warn("🆘 STORACHA (Ambient State):", err.message || err, "(FAILED: Ensure agent is logged in and space is selected externally via w3 commands)");
}
// Option for applications: Pass a pre-configured client & space DID
// (Application must handle client creation, login flow, and space selection itself)
console.log("\nExample: Using Storacha with a pre-configured client (if available)... ");
let preConfiguredClient;
let spaceDidToUse; // Replace with your actual Space DID
try {
// Example: Initialize client (in a real app, handle login flow properly)
preConfiguredClient = await create();
const accounts = await preConfiguredClient.accounts();
if (!accounts || Object.keys(accounts).length === 0) {
console.warn(" -> Storacha Pre-config: Agent not logged in. Skipping pre-configured client test.");
// In a real app: initiate login -> await client.login('email'); ... wait for link click ...
} else {
const spaces = await preConfiguredClient.spaces();
if (!spaces || spaces.length === 0) {
console.warn(" -> Storacha Pre-config: Agent has no spaces. Skipping pre-configured client test.");
} else {
spaceDidToUse = spaces[0].did(); // Using the first space for the example
console.log(` -> Storacha Pre-config: Using space DID: ${spaceDidToUse}`);
const storachaConnect = { client: preConfiguredClient, spaceDid: spaceDidToUse };
const url = await uploadFile.storacha(storachaConnect, fileData);
console.log("✅ STORACHA Upload (Pre-config):", url);
const cid = extractCidFromUrl(url);
if (cid) {
await deleteFile.storacha(storachaConnect, { cid });
console.log("✅ STORACHA Delete (Pre-config): OK");
} else {
console.error("🆘 STORACHA Delete (Pre-config): Could not extract CID from URL", url);
}
}
}
} catch (err) {
console.error("🆘 STORACHA (Pre-configured Client Example):", err.message || err);
}
// --- Lighthouse Example ---
if (process.env.LIGHTHOUSE_TOKEN) {
try {
const lighthouseConnect = { token: process.env.LIGHTHOUSE_TOKEN };
const url = await uploadFile.lighthouse(lighthouseConnect, fileData);
console.log("✅ LIGHTHOUSE Upload:", url);
// Note: deleteFile.lighthouse is currently not implemented (pending SDK docs)
const cid = extractCidFromUrl(url);
if (cid) {
try {
await deleteFile.lighthouse(lighthouseConnect, { cid });
console.log("✅ LIGHTHOUSE Delete: OK (Unimplemented function did not throw?)");
} catch (deleteErr) {
if (deleteErr.message && deleteErr.message.includes("not implemented")) {
console.log("✅ LIGHTHOUSE Delete: OK (Not implemented as expected)");
} else {
console.error("🆘 LIGHTHOUSE Delete Error:", deleteErr.message || deleteErr);
}
}
} else {
console.error("🆘 LIGHTHOUSE Delete: Could not extract CID from URL", url);
}
} catch (err) {
console.error("🆘 LIGHTHOUSE:", err.message || err);
}
}
}
runExamples();
---
`(c)` Alex Baker