Skip to content

ipfs-shipyard/ipfs-provider

Repository files navigation

ipfs-provider

Build Status Dependency Status

Returns IPFS API object by trying multiple providers in a custom fallback order.

This is a general-purpose replacement for ipfs-redux-bundle.

Install

via NPM

$ npm install ipfs-provider

via prebuilt browser bundles

<!-- remember to include js-ipfs (core) and/or js-ipfs-http-client, if they are used -->
<script src="https://cdn.jsdelivr.net/npm/ipfs-core/dist/index.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/ipfs-http-client/dist/index.min.js"></script>

<!-- prebuilt and minified bundle -->
<script src="https://cdn.jsdelivr.net/npm/ipfs-provider/dist/index.min.js"></script>

<script>
  const { getIpfs, providers } = window.IpfsProvider
  const { ipfs, provider, apiAddress } = await getIpfs({
    loadHttpClientModule: () => window.IpfsHttpClient,
    loadJsIpfsModule: () => window.IpfsCore,
    providers: [ /* see Usage below */ ]
    })
</script>

Note: when using prebuilt bundles in production use explicit versions and SRI hashes. Details here.

Usage

const { getIpfs, providers } = require('ipfs-provider')
const { httpClient, jsIpfs } = providers

const { ipfs, provider, apiAddress } = await getIpfs({
  // when httpClient provider is used multiple times
  // define its constructor once, at the top level
  loadHttpClientModule: () => require('ipfs-http-client'),

  // note this is an array, providers are tried in order:
  providers: [

    // try various HTTP endpoints (best-effort),
    httpClient({
      // (1) try multiaddr of a local node
      apiAddress: '/ip4/127.0.0.1/tcp/5001'
    }),
    httpClient(), // (2) try "/api/v0/" on the same Origin as the page
    httpClient({
      // (3) try arbitrary API from URL string
      apiAddress: 'https://some.example.com:8080'
    }),
    httpClient({
      // (4) try API defined by a custom http client config
      apiAddress: {
        host: 'apis.example.com',
        port: '443',
        protocol: 'https',
        apiPath: 'custom/path/to/api/v0',
        headers: {
          authorization: 'Basic dXNlcjpwYXNz'
        }
      }
    }),
    // (5) final fallback to spawning embedded js-ipfs running in-page
    jsIpfs({
      // js-ipfs package is used only once, as a last resort
      loadJsIpfsModule: () => require('ipfs-core'),
      options: { } // pass config: https://github.com/ipfs/js-ipfs/blob/master/packages/ipfs/docs/MODULE.md#ipfscreateoptions
    })
  ]
})

for await (const file of ipfs.add("text")) {
  if (file && file.cid) {
    console.log(`successfully stored at ${file.cid}`)
  } else {
    console.error('unable to ipfs.add', file)
  }
}
  • ipfs – returned instance of IPFS API (see SPEC)
  • provider – a string with a name of the first successful provider.
    • built-in names match constants from providers: httpClient, jsIpfs, windowIpfs and webExt.
  • apiAddress – returned only when httpClient provider is used, provides information which HTTP endpoint succeded

Examples

See examples/ for sample code and demonstration of advanced fallback strategies.

Providers

You can customize the order of the providers by passing a different array order to the providers array.

For example, if you want to try httpClient and then jsIpfs, you should run it like this:

const { getIpfs, providers } = require('ipfs-provider')
const { httpClient, jsIpfs } = providers

const { ipfs, provider } = await getIpfs({
  providers: [
    httpClient(),
    jsIpfs()
  ]
})

Customizing connection test

const { ipfs, provider } = await getIpfs({
  providers: [ /* array of providers to try in order */ ],
  connectionTest: () => { /* boolean function to test the connection to IPFS, default one tries to ipfs.get the CID of an empty directory */ },
})

httpClient

Tries to connect to HTTP API via js-ipfs-http-client:

const { ipfs, provider } = await getIpfs({
  providers: [
    httpClient({
      loadHttpClientModule: () => require('ipfs-http-client'),
      apiAddress: 'https://api.example.com:8080/'
    })
  ]
})

This provider will attempt to establish connection with (in order):

  1. apiAddress (if provided)
  2. /api/ at the current Origin
  3. the default local API (/ip4/127.0.0.1/tcp/5001)

It supports lazy-loading and small bundle sizes. The client library is initialized using constructor (in order):

  1. one returned by loadHttpClientModule async function (if provided)
  2. one exposed at window.IpfsHttpClient (if present)

Value passed in apiAddress can be:

  • a multiaddr (string like /ip4/127.0.0.1/tcp/5001 or an object)
  • a String with an URL (https://api.example.com:8080/)
  • a configuration object supported by the constructor ({ host: '1.1.1.1', port: '80', apiPath: '/ipfs/api/v0' })

To try multiple endpoints, simply use this provider multiple times. See examples/browser-browserify/src/index.js for real world example.

jsIpfs

Spawns embedded js-ipfs (a full IPFS node in JavaScript) in the context of the current page using customizable constructor:

const { ipfs, provider } = await getIpfs({
  providers: [
    jsIpfs({
      loadJsIpfsModule: () => require('ipfs-core'),
      options: { /* advanced config */ }
    })
  ]
})
  • loadJsIpfsModule should be a function that returns a promise that resolves to a js-ipfs constructor
  • options should be an object which specifies advanced configurations to the node.
  • TIP: when used in a browser context, use ipfs-core for a smaller browser bundle

windowIpfs

window.ipfs was an experiment created by ipfs-companion browser extension. It supported passing an optional list of permissions to display a single ACL prompt the first time it is used:

const { ipfs, provider } = await getIpfs({
  providers: [
    windowIpfs({
      // example
      permissions: { commands: ['add','cat','id', 'version'] }
    })
  ]
})

webExt

webExt looks for an instance in the background page of a WebExtension (useful only in browser extensions, not regular pages, disabled by default)

const { ipfs, provider } = await getIpfs({
  providers: [
    webExt()
  ]
})

Test

$ npm test

Lint

Perform standard linting on the code:

$ npm run lint

Contribute

Feel free to dive in! Open an issue or submit PRs.

To contribute to IPFS in general, see the contributing guide.

License

MIT © Protocol Labs