· Nacho Coll · Tutorials · 9 min čitanja
IPFS Upload API — Potpuni razvojni tutorial
Naučite prenositi datoteke na IPFS putem REST API-ja. Potpuni primjeri koda u JavaScriptu, Node.js-u i curl-u. Prenosite JSON, slike, koristite potpisane tokene za prijenose s klijenta.

IPFS Upload API — Potpuni razvojni tutorial
Prenošenje datoteka na IPFS trebalo bi biti jednostavno kao slanje POST zahtjeva. U ovom tutorijalu ćete učiniti upravo to — prenosit ćete JSON dokumente, slike i PDF-ove na IPFS uz ništa više od fetch() i API ključa. Na kraju ćete imati potpunu Node.js skriptu koja prenosi sadržaj, izlistava datoteke, dohvaća metapodatke i generira potpisane tokene za sigurne prijenose s klijenta. Novi ste na IPFS-u? Počnite s Što je IPFS Pinning? kako biste razumjeli osnove prije ronjenja u kod.

Što ćete izgraditi
- Prenijeti JSON objekt na IPFS i dobiti natrag identifikator sadržaja (CID).
- Prenijeti binarnu sliku s diska.
- Priložiti opise i prilagođene metapodatke prijenosima.
- Upitati svoju povijest prijenosa po rasponu datuma.
- Dohvatiti detalje za određenu datoteku.
- Stvoriti vremenski ograničene potpisane tokene tako da preglednik može prenositi izravno bez izlaganja vašeg API ključa.
- Rukovati pogreškama i implementirati logiku ponovnog pokušaja.
Sve se izvodi protiv jedne osnovne URL: https://api.ipfs.ninja
1. Postavljanje — Registracija i dobivanje API ključa
- Registrirajte se za besplatan račun (kreditna kartica nije potrebna).
- Idite na API Keys u bočnoj traci dashboarda.
- Kliknite Create key, dajte mu ime i odmah kopirajte ključ — neće se ponovno prikazati.

Vaš ključ izgleda ovako: bws_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6. Svaki primjer ispod šalje ga u zaglavlju X-Api-Key.
2. Prenošenje JSON-a na IPFS
Najjednostavniji prijenos: proslijedite običan JavaScript objekt kao content i API ga serijalizira, prikvači na IPFS i vraća CID.
// upload-json.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const payload = {
content: {
name: "Galactic Badge #42",
description: "Proof of attendance — Galactic Meetup 2026",
attributes: [
{ trait_type: "Event", value: "Galactic Meetup" },
{ trait_type: "Year", value: 2026 }
]
}
};
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify(payload)
});
const data = await res.json();
console.log("CID :", data.cid);
console.log("Size:", data.sizeMB, "MB");
console.log("IPFS:", data.uris.ipfs);
console.log("URL :", data.uris.url);Pokrenite s node upload-json.mjs. Uspješan odgovor izgleda ovako:
{
"cid": "bafkreigx7gq...",
"sizeMB": 0.0004,
"uris": {
"ipfs": "ipfs://bafkreigx7gq...",
"url": "https://ipfs.ninja/ipfs/bafkreigx7gq..."
}
}Polje url upućuje na javni HTTP gateway, tako da je sadržaj odmah dostupan u bilo kojem pregledniku.
3. Prenošenje slike
Binarne datoteke (slike, PDF-ovi, audio) šalju se kao base64-kodirani nizovi u polju content.
// upload-image.mjs
import { readFileSync } from "node:fs";
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const imageBuffer = readFileSync("./photo.png");
const base64 = imageBuffer.toString("base64");
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({ content: base64 })
});
const data = await res.json();
console.log("Image CID:", data.cid);
console.log("Gateway :", data.uris.url);API automatski otkriva MIME tip — PNG, JPEG, WebP, GIF i PDF svi su podržani. Nisu potrebna dodatna zaglavlja ili poništavanja content-type.
Uz curl ista operacija izgleda ovako:
BASE64=$(base64 -w 0 photo.png)
curl -X POST https://api.ipfs.ninja/upload/new \
-H "Content-Type: application/json" \
-H "X-Api-Key: bws_your_key_here" \
-d "{\"content\": \"$BASE64\"}"4. Prenošenje s metapodacima
Svaki prijenos prihvaća dva neobavezna polja: description (oznaka slobodnog teksta) i metadata (proizvoljni parovi ključ-vrijednost). Oba se spremaju uz CID i vraćaju kada kasnije izlistate ili dohvatite datoteku.
// upload-with-metadata.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({
content: { title: "Meeting Notes", body: "Q1 roadmap recap..." },
description: "Q1 2026 planning meeting notes",
metadata: {
project: "acme-app",
author: "dana",
version: "1"
}
})
});
const data = await res.json();
console.log("CID:", data.cid);Metapodaci olakšavaju filtriranje i organizaciju datoteka na vašoj strani bez raščlanjivanja samog IPFS sadržaja.
5. Prikvačivanje postojećeg CID-a
Ako već imate sadržaj na IPFS-u i želite osigurati da ostane dostupan, prikvačite ga po CID-u:
// pin-cid.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const res = await fetch(`${API}/pin`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({
cid: "bafkreigx7gq...",
description: "Pinned from external source"
})
});
const data = await res.json();
console.log("Pinned:", data.cid);6. Izlistavanje vaših datoteka
Dohvatite svaku datoteku koju ste prenijeli unutar vremenskog prozora. Query parametri from i to su Unix timestampi u milisekundama.
// list-files.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const now = Date.now();
const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000;
const url = `${API}/upload/list?from=${oneWeekAgo}&to=${now}`;
const res = await fetch(url, {
headers: { "X-Api-Key": API_KEY }
});
const files = await res.json();
for (const file of files) {
console.log(`${file.cid} ${file.description ?? "(no description)"}`);
}Uz curl:
FROM=$(($(date +%s) * 1000 - 604800000))
TO=$(($(date +%s) * 1000))
curl -s "https://api.ipfs.ninja/upload/list?from=$FROM&to=$TO" \
-H "X-Api-Key: bws_your_key_here" | jq .7. Detalji datoteke
Dohvatite cjelovit zapis za jedan CID, uključujući metapodatke, veličinu i timestampe:
// get-file.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const CID = "bafkreigx7gq...";
const res = await fetch(`${API}/file/${CID}`, {
headers: { "X-Api-Key": API_KEY }
});
const details = await res.json();
console.log(JSON.stringify(details, null, 2));8. Prijenosi s klijenta uz potpisane tokene
Ugrađivanje API ključa u preglednički bundle sigurnosni je rizik. Umjesto toga, generirajte kratkotrajni potpisani token na svom poslužitelju i proslijedite ga klijentu.
Server (Express)
// server.mjs
import express from "express";
const app = express();
const API = "https://api.ipfs.ninja";
const API_KEY = process.env.IPFS_API_KEY;
app.post("/api/upload-token", async (req, res) => {
const response = await fetch(`${API}/upload/signed-url`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({
name: "browser-upload",
expiresIn: 600 // token valid for 10 minutes
})
});
const { token, tokenId, expiresAt } = await response.json();
res.json({ token, tokenId, expiresAt });
});
app.listen(3000, () => console.log("Server running on :3000"));Klijent u pregledniku
<!-- upload.html -->
<input type="file" id="filePicker" />
<button id="uploadBtn">Upload to IPFS</button>
<pre id="result"></pre>
<script>
const API = "https://api.ipfs.ninja";
document.getElementById("uploadBtn").addEventListener("click", async () => {
// 1. Get a signed token from your own backend
const tokenRes = await fetch("/api/upload-token", { method: "POST" });
const { token } = await tokenRes.json();
// 2. Read the selected file as base64
const file = document.getElementById("filePicker").files[0];
if (!file) return alert("Pick a file first");
const base64 = await new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result.split(",")[1]);
reader.readAsDataURL(file);
});
// 3. Upload directly to IPFS using the signed token
const uploadRes = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Signed ${token}`
},
body: JSON.stringify({
content: base64,
description: file.name
})
});
const data = await uploadRes.json();
document.getElementById("result").textContent = JSON.stringify(data, null, 2);
});
</script>Preglednik nikada ne vidi vaš API ključ. Potpisani token automatski istječe. Tokeni su višestruke uporabe — mogu se koristiti više puta dok ne isteknu ili budu opozvani. useCount se prati, ali se ne nameće kao ograničenje.
9. Rukovanje pogreškama
API koristi konvencionalne HTTP statusne kodove. Evo onih koje biste trebali eksplicitno rukovati:
| Status | Značenje | Što učiniti |
|---|---|---|
| 400 | Bad request — polja koja nedostaju ili su nevažeća | Provjerite je li content prisutan i valjan |
| 401 | Unauthorized — loš ili nepostojeći API ključ | Provjerite zaglavlje X-Api-Key |
| 413 | Payload too large | Smanjite veličinu datoteke ili razdijelite sadržaj |
| 429 | Rate limited | Pričekajte i pokušajte ponovno s eksponencijalnom odgodom |
| 500 | Server error | Pokušajte ponovno nakon kratke odgode |
Otporna funkcija prijenosa s eksponencijalnim backoffom:
// resilient-upload.mjs
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
async function uploadWithRetry(content, retries = 3) {
for (let attempt = 1; attempt <= retries; attempt++) {
const res = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
},
body: JSON.stringify({ content })
});
if (res.ok) return await res.json();
const body = await res.text();
if (res.status === 401) {
throw new Error(`Authentication failed: ${body}`);
}
if (res.status === 413) {
throw new Error(`Payload too large — reduce file size. ${body}`);
}
if (res.status === 429 || res.status >= 500) {
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
console.warn(`Attempt ${attempt} failed (${res.status}). Retrying in ${delay}ms...`);
await new Promise((r) => setTimeout(r, delay));
continue;
}
throw new Error(`Upload failed (${res.status}): ${body}`);
}
throw new Error("Upload failed after all retries");
}
// Usage
const data = await uploadWithRetry({ hello: "world" });
console.log("CID:", data.cid);10. Cjelovit radni primjer
Jedna skripta koja vježba svaki endpoint pokriven u ovom tutorijalu. Spremite je kao ipfs-demo.mjs i pokrenite s node ipfs-demo.mjs.
// ipfs-demo.mjs
import { readFileSync } from "node:fs";
const API = "https://api.ipfs.ninja";
const API_KEY = "bws_your_key_here";
const headers = {
"Content-Type": "application/json",
"X-Api-Key": API_KEY
};
async function request(method, path, body) {
const opts = { method, headers };
if (body) opts.body = JSON.stringify(body);
const res = await fetch(`${API}${path}`, opts);
if (!res.ok) throw new Error(`${method} ${path} → ${res.status}: ${await res.text()}`);
return res.json();
}
// --- 1. Upload JSON ---
console.log("--- Upload JSON ---");
const jsonResult = await request("POST", "/upload/new", {
content: { name: "Demo Token", edition: 1 },
description: "Tutorial demo — JSON upload",
metadata: { tutorial: "true" }
});
console.log("CID:", jsonResult.cid);
console.log("URL:", jsonResult.uris.url);
// --- 2. Upload an image (if photo.png exists) ---
try {
const img = readFileSync("./photo.png");
console.log("\n--- Upload Image ---");
const imgResult = await request("POST", "/upload/new", {
content: img.toString("base64"),
description: "Tutorial demo — image upload"
});
console.log("CID:", imgResult.cid);
console.log("URL:", imgResult.uris.url);
} catch {
console.log("\n--- Skipping image upload (no photo.png found) ---");
}
// --- 3. Pin the JSON CID ---
console.log("\n--- Pin CID ---");
const pinResult = await request("POST", "/pin", {
cid: jsonResult.cid,
description: "Pinned from tutorial"
});
console.log("Pinned:", pinResult.cid);
// --- 4. Get file details ---
console.log("\n--- File Details ---");
const details = await request("GET", `/file/${jsonResult.cid}`);
console.log(JSON.stringify(details, null, 2));
// --- 5. List recent files ---
console.log("\n--- Recent Files ---");
const now = Date.now();
const oneHourAgo = now - 60 * 60 * 1000;
const files = await request("GET", `/upload/list?from=${oneHourAgo}&to=${now}`);
console.log(`Found ${files.length} file(s) in the last hour`);
for (const f of files) {
console.log(` - ${f.cid} ${f.description ?? ""}`);
}
// --- 6. Create a signed upload token ---
console.log("\n--- Signed Token ---");
const tokenResult = await request("POST", "/upload/signed-url", {
name: "demo-token",
expiresIn: 300
});
console.log("Token ID :", tokenResult.tokenId);
console.log("Expires :", new Date(tokenResult.expiresAt).toISOString());
// --- 7. Upload using the signed token ---
console.log("\n--- Upload with Signed Token ---");
const signedRes = await fetch(`${API}/upload/new`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Signed ${tokenResult.token}`
},
body: JSON.stringify({
content: { note: "Uploaded with a signed token" }
})
});
const signedData = await signedRes.json();
console.log("CID:", signedData.cid);
console.log("\nDone.");Zamijenite bws_your_key_here stvarnim ključem i pokrenite skriptu. Svaki korak ispisuje svoj rezultat tako da možete pratiti.
11. Sljedeći koraci
Sada znate kako prenositi, prikvačivati, izlistavati i dohvaćati datoteke putem IPFS Upload API-ja i kako čuvati sigurnima preglednike prijenose s potpisanim tokenima. Za širi pregled koji uključuje UI dashboarda i Python primjere, pogledajte Kako prenijeti datoteke na IPFS. Evo gdje krenuti odavde:
- API Reference — Cjelovita dokumentacija endpointa na ipfs.ninja/docs.
- Prilagođeni gatewayi — Poslužujte IPFS sadržaj s vlastite domene. Pogledajte vodič za postavljanje gatewaya.
- Analitika — Pratite obujam prijenosa, propusnost i brojeve pinova u dashboardu.
- HTTP klijent — Nijedan SDK nije potreban. Možete koristiti standardni
fetch()ili bilo kojeg HTTP klijenta za interakciju s API-jem.
Ako naiđete na probleme, otvorite tiket na support.ipfs.ninja ili se pridružite zajednici na Discordu.
