IPFSX
Designing a more ergonomic IPFS API
Lab Week 2018
How can IPFS API be more appealing to programmers? - We can make it easier to program for IPFS
- Programming for IPFS can be more enjoyable
- Programming for IPFS can be more exciting
Solving these can encourage developers to use IPFS - What would I like to code?
- If I could, what would IPFS API look like?
- I don't think we should just change things on a whim
- I do think there is an increasingly large set of reasons to change things
This is about improving developer experience (It's currently just a mapping layer from the API I want to expose
to the API that IPFS or IPFS API exposes)
The biggest difference is making use of async iterators Firstly, Iterable
is an interface for producing iterators
There are lots of things that are iterable in JS already:
arrays, objects, maps, sets.
You can use them in for (x of y)
loops An Iterable
object exposes a function that creates an iterator
...which can be used to iterate over the items in an iterable.
An iterator implements the iterator protocol
i.e. it has a next
method
which returns an object
{ done, value }
An async iterator instead returns a Promise
when you call next
You can use async iterables in
for await (x of y)
loops
So we have a way to stream data using native language constructs!
🙌 We don't need to use Node.js streams or pull streams any more No more multi API add
, addPullStream
, addReadableStream
Smaller bundle, fewer dependencies! Fewer tests to run (testing all combinations) Easier to create an interface-ipfs-core
compatible interface (fewer things to implement)
Less boilerplate converting between streams Easier to error handle - only try catch
Better stack traces - not clipped at async boundaries Enough!
What does it look like?
Create node
const ipfsx = require('ipfsx')
const IPFS = require('ipfs') // N.B. also works with ipfs-api!
const node = await ipfsx(new IPFS)
// node is READY to use
Add content
for await (const item of node.add('hello world!'))
console.log(item.cid.toString())
"Shorthand"
const { cid } = await node.add('hello world!').first()
console.log(cid.toString())
Streaming add
const item = await node.add(fs.createReadStream(/*...*/)).first()
Add multiple
const adder = node.add([
{ path: 'root/file1', content: fs.createReadStream(/*...*/) },
{ path: 'root/file2', content: fs.createReadStream(/*...*/) }
])
for await (const res of adder)
console.log(res.cid, res.path)
For fun, create your own iterator!
const generator = function * () {
for (let i = 0; i < 10; i++)
yield crypto.randomBytes()
}
const { cid } = await node.add(generator()).first()
console.log(cid)
For fun, create your own async iterator!
const generator = async function * () {
for (let i = 0; i < 10; i++)
yield new Promise(resolve => resolve(crypto.randomBytes()))
}
const { cid } = await node.add(generator()).first()
console.log(cid)
Cat it out!
let data = Buffer.alloc(0)
const cid = 'QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks'
for await (const chunk of node.cat(cid, options))
data = Buffer.concat([data, chunk])
console.log(data.toString()) // Hello, world!