How to upload file to IPFS with only front-end

I am writing a front-end up, without no server. Just a front-end react application.

Now, users should be able to upload files to IPFS. Since it’s a front-end only without server, I really don’t have much idea how to let them upload files to IPFS.

I saw this code…

const IPFS = require('ipfs-core')
const ipfs = await IPFS.create()
const { cid } = await ipfs.add(file)
console.info(cid)

The above works.

Question 1. I want to confirm something. does IPFS.create create a node in the browser and that’s how it starts to work ? Is this stable and performant ? since node(i don’t mean node.js) is running in browser, maybe it’s not performant. What do you think ?

Question 2. if IPFS.create creates a node in the browser, I guess, when I refresh the page and that code again gets executed, old node dies and new one gets created. is this performant ?

I’d appreciate your insights on this, folks…

1 Like
  1. Yes. It’s stable depending on what you want to do. js-ipfs is in active development but I’m personally convinced you could use it in a stable and performant application. Just gotta make sure you test what you want to do.

  2. The node is re-created on each refresh however it’ll initialise whatever repo your specify with the repo option. If you don’t specify one, it loads the default repo, so the data and PeerID key(s) will still be saved if you reload the same repo. Example of a custom repo: const ipfs = await IPFS.create({repo: "example"});

@Discordian

Thanks a lot for the follow up and answers.

I might ask though: what’s repo exactly ? if I initialize 2 nodes with different repos, what would be the actual difference between those nodes ? and if I initialize 2 nodes with the same repos then what would be the difference ?

Thanks in advance.

Two nodes with different repos would be 2 nodes operating independently. Their own blockstore, pins, MFS, PeerID, etc. 2 nodes using the same repo is invalid, a lockfile will trigger an error. This would cause many conflicts as the nodes would attempt writes in the same locations, among other issues.

Happy to help :slight_smile:

1 Like

Hey @Discordian

Thanks for all your help. I realized that i have a couple of questions related to this package.

  1. When I add file to ipfs, which node does it get stored on ? If it’s a node in browser, then if browser dies as in closing the browser, node would get destroyed and no one would be able to get it . I understand that If I ask for the file one time from other node, for sure, that other node will store it too and this way, closing and killing the browser wouldn’t cause any issue. File would still be found. Any idea ?

  2. it seems like that the safest way is to pin it after uploading. So this is what i do

const { cid } = await ipfs.add(file);
const res = await ipfs.pin.add(cid);

would this be enough ? but I really don’t understand how to check if it’s pinned and which node stores it so that it never gets erased or anything like that ever.

I’d appreciate the guidance.

1 Like
  1. It gets stored on the local node in the browser. If the node goes offline (page closed) it will no longer serve the data. However once the node is live again (user opens page), it’ll load the same repo and be able to re-serve. So to answer your final question here, “How can I be sure another node has the data?”, you have to devise your own mechanism for that. I’m working on a demo for that (but it’s not finished enough to share at this moment), where the client advertises some metadata including CID over pubsub, and other nodes listen on the same topic, adding the data they see advertised (not pinned). The reason I don’t pin, is that way I can allow the GC to tidy up the data over time.

  2. Unfortunately pinning still only pins it to the local browser node.

1 Like

Thanks so much… @Discordian

I really don’t know what to do then… We have a website where it’s so important that data never goes missing that users upload through our front-end form…

Question 1. When do you think your demo will be ready ? as an approximation ?

Question 2. let’s say on my server, I also build my own node. How is it possible that I give the uploaded file from my local node in browser to my server’s node directly ? Is this possible and is it a good way and how can this be done ?

Question 3. What alternatives do I have ? one i can think of is that I build node.js server and that’s where I upload my file and when the file reaches my node.js server, I put it in the ipfs node that is on the same server as my node.js server. but I guess, this is wasted work, because Question:2 above should also be possible. right ?

It sounds like what you’re looking for is a pinning service. You want someone to store and serve the files when your frontend is closed. Setting up your own node.js server is like setting up your own pinning service. Using a pinning service should be greatly simplified by the new pinning service api introduced in go-ipfs 0.8 (I’m assuming js-ipfs was updated to support it) IPFS Pinning Service API

@zacharywhitley

js-ipfs contains pinning functionality.

const { cid } = await ipfs.add(file);
const res = await ipfs.pin.add(cid);

This works, but as @Discordian said, this would still pin to the local node in browser which doesn’t look like a pinning to me at all :smiley:

No problem!

  1. The demo itself? Probably Monday sounds like a good goal for the draft. (But no promises :stuck_out_tongue:)

  2. The easiest way to do that is using PubSub IMHO. You can easily publish/subscribe to topics, and send CIDs over. So the process I do in browser (simplified) is I add the file I want to send, which gives me a CID. I then advertise that CID over PubSub. On my go-ipfs node, there’s a Python script listening on that PubSub topic, and when it sees the CID it adds it to the node, so that way the data is available to the wider network much longer. I also use this node as a bootstrap node.

  3. Yeah I actually did this using a go-ipfs node and a simple Python script, I’ll include the script along with the demo (and a guide). zachary’s suggestion is really good too, using a pinning service like https://pinata.cloud for example, you can use features like the remote pinning API (check out some commands here). This can delegate the pinning work to a paid party.

1 Like

@Discordian

so basically, in my code, it would look like this…

const { cid } = await ipfs.add(file);
await ipfs.pin.remote.sevice.add('pinata', {
  endpoint: new URL('https://api.pinata.cloud'),
  key: 'your-pinata-key'
})
const res = await ipfs.pin.add(cid) // IMPORTANT: will this now add to pinata and to my local node automatically ? 
1 Like

Close! Ensure you’re reading through the other pin.remote API related commands :wink: . You’ll want to create pins now using ipfs.pin.remote.add

1 Like

@Discordian

haha, just saw this when you commented.

Thanks a lot.

One problem i can think of…

The process looks like this.

  1. browser uploads file to local node and gets cid.
  2. cid is then sent to pinata.
  3. pinata now asks for this cid and finds that my local node has it. pinata gets the file and stores it and also on all its nodes for safety.

What if as soon as cid is successfully brought to pinata, immediatelly my local node shut down(user turned browser or anything like this), this means that when pinata asks for the cid, it won’t find it anywhere(my node is closed). This could happen quite a lot. What’s the workaround in this case ?

1 Like

Actually the purpose of having Pinata pin it, is the data itself is also sent to their nodes, where they’ll re-host it for you. So when the local node goes offline, the CID will still be accessible :smiley:.

1 Like

@Discordian true, haha ,but I explained a different scenario.

Let’s follow together.

  1. we upload file to local node in browser with ipfs.add and this returns cid.
  2. we then do await ipfs.pin.add(cid). After this is resolved, It means that cid was received by pinata nodes. So what they will do is ask for the cid to the ipfs nodes. and pinata node will find it in our local node, but the problem is that what if as soon as pinata receives cid from my local node and starts to retrieve file by cid, at that very moment, local node goes offline, meaning that it no longer serves the file and pinata will not be able to find it at all for the first time.

makes sense ?

1 Like

@Discordian

Hey , hope you had a wonderful weekend. Just as a reminder, what do you think about my last scenario ?

1 Like

Okay I understand. So you’d want to ensure you’re not just doing await ipfs.pin.add but also doing await ipfs.remote.pin.add afterwards, so Pinata pins it. I believe ipfs.remote.pin.add will also block until Pinata completes the pin, so you could display a message when it completes, so the user doesn’t close the page too early.

Thank you! Sorry about that, I totally missed the reply! :blush:

1 Like

@Discordian

That’s awesome if it’s how it’s going to work, but:

Q1: I guess, if user uploads a really big file, for sure, it’s gonna take a good amount of time. which seems acceptable.

Q2: If you remember that we agreed on using pinata, and I don’t use the back-end service at all. I realized one problem. Where should i store pinata private key ? :smiley: I mean, storing in front-end can be exploitable by other people. you might say that i can store it on CI/CD , but again, this just makes it harder for malicious users to get their hands on it, but it’s still possible, because the key is still built in the front-end. any ideas ?

Thanks a lot and no worries.

Q1. Yup :smiley:

Q2. Oh jeeze I hadn’t thought that through I suppose :blush:. You could setup your own bootstrap node, and have other nodes communicate with it over PubSub, and have that node do the ipfs.remote.pin.add. I’m hoping to have a demo more-or-less demonstrating a way to do this up hopefully today (at least a draft) (edit: draft likely won’t be ready until tomorrow).

tl;dr for how I’m doing it, I do the add, like you’re doing right now, then I broadcast some JSON over a PubSub topic, from that a python script running on a bootstrap node (but you can use nodejs) gets the JSON and then adds the embedded CID to the node. I use JSON for some metadata, and that’s it really.

1 Like

@Discordian

Yes, I think using pinata is still a good choice and as you said, this can be done from the back-end node that is mine…

Can’t wait to see the DEMO <3

What do you mean by bootstrap node ? is there anything my browser local node can do to connect with bootstrap node better ? but I guess, this will be shown in the demo itself.

1 Like