Home Reference Source Test

kinto-http.js

Greenkeeper badge

Build Status

A JavaScript HTTP Client for the Kinto API.

Read the API documentation.

Table of Contents


Requirements

Installation

In the browser, you can load prebuilt scripts hosted on unpkg:

<script src="https://unpkg.com/kinto-http/dist/kinto-http.min.js"></script>

In nodejs:

$ npm install kinto-http --save

Then (ES6):

import KintoClient from "kinto-http";

Or (ES5):

var KintoClient = require("kinto-http");

Note that this HTTP client can be transparently used server side or in a regular browser page. In the browser, creating an instance is achieved that way:

const client = new KintoClient("http://");

Changelog

See upgrading docs and the full detailed changelog on Github.

Usage

A client instance is created using the KintoClient constructor, passing it the remote Kinto server root URL, including the version:

const client = new KintoClient("https://kinto.dev.mozaws.net/v1");

Options

Authentication

Authenticating against a Kinto server can be achieved by adding an Authorization header to the request.

By default Kinto server supports Basic Auth authentication, but others mechanisms can be activated such as OAuth (eg. Firefox Account)

Using Basic Auth

Simply provide an Authorization header option to the Kinto constructor:

const secretString = `${username}:${password}`;
const kinto = new KintoClient("https://my.server.tld/v1", {
  headers: {
    Authorization: "Basic " + btoa(secretString)
  }
});

Notes

  • As explained in the server docs, any string is accepted. You're not obliged to use the username:password format.

Using an OAuth Bearer Token

As for Basic Auth, once you have retrieved a valid OAuth Bearer Token, simply pass it in an Authorization header:

const kinto = new KintoClient("https://my.server.tld/v1", {
  headers: {
    Authorization: `Bearer ` + oauthBearerToken)
  }
});

Server information

A Kinto server exposes some of its internal settings, information about authenticated user, the HTTP API version and the API capabilities (e.g. plugins).

client.fetchServerInfo([options])
  .then(({data}) => ...);

Sample result:

{
    "project_name": "kinto",
    "project_version": "3.0.2",
    "url": "http://0.0.0.0:8889/v1/",
    "project_docs": "https://kinto.readthedocs.io/",
    "http_api_version": "1.6",
    "settings": {
        "batch_max_requests": 25,
        "readonly": false
    },
    "user": {
        "bucket": "2f9b1aaa-552d-48e8-1b78-371dd08688b3",
        "id": "basicauth:f505765817a6b4ea46278be0620ddedd83b10f71f7695683719fe001cf0871d7"
    },
    "capabilities": {
        "default_bucket": {
            "description": "The default bucket is an alias for a personal bucket where collections are created implicitly.",
            "url": "http://kinto.readthedocs.io/en/latest/api/1.x/buckets.html#personal-bucket-default"
        }
    }
}

Options

Helpers

Buckets

Listing buckets

client.listBuckets([options])
  .then(({data}) => ...);

Sample result:

{
  data: [
    {
      id: "comments",
      last_modified: 1456182233221,
    },
    {
      id: "blog",
      last_modified: 1456181213214,
    },
  ]
}

Options

This method accepts the generic parameters for sorting, filtering and paginating results.

Creating a new bucket

client.createBucket("blog"[, options])
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456182233221,
    "id": "blog"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

It's alternatively possible to create a bucket without specifying an id, so the Kinto server will create one for you:

client.createBucket()
  .then(result => ...);

Note: if you plan on providing options along id autogeneration, you have to specify null as the first argument:

client.createBucket(null, {data: {foo: 42}, retry: 3})
  .then(result => ...);

Options

Selecting a bucket

client.bucket("blog");

Getting bucket data

client.bucket("blog").getData()
  .then(result => ...);

Sample result:

{
  "last_modified": 1456182336242,
  "id": "blog",
  "foo": "bar"
}

Options

Setting bucket data

client.bucket("blog").setData({foo: "bar"})
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456182336242,
    "id": "blog",
    "foo": "bar"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

Options

Getting bucket permissions

client.bucket("blog").getPermissions()
  .then(result => ...);

Sample result:

{
  "write": [
    "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
  ]
}

Options

Setting bucket permissions

const permissions = {
  read:  ["github:bob"],
  write: ["github:bob", "github:john"]
};

client.bucket("blog").setPermissions(permissions[, options])
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456182888466,
    "id": "blog"
  },
  "permissions": {
    "read": ["github:bob"],
    "write": [
      "github:bob",
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8",
      "github:john"
    ]
  }
}

Options

Notes

Deleting a bucket

client.deleteBucket("testbucket"[, options])
  .then(result => ...);

Sample result:

{
  "data": {
    "deleted": true,
    "last_modified": 1456182931974,
    "id": "blog"
  }
}

Options

Creating a collection

Named collection

client.bucket("blog").createCollection("posts")
  .then(result => ...);

Sample result:


{
  "data": {
    "last_modified": 1456183004372,
    "id": "posts"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

With an ID generated automatically

client.bucket("blog").createCollection()
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456183040592,
    "id": "OUh5VEDa"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

Note that OUh5VEDa is the collection ID automatically generated by the server.

Options

Note: For generated names, options can be specified only if the first parameters are provided: createCollection(undefined, {safe: true})

Listing bucket collections

client.bucket("blog").listCollections()
  .then(({data}) => ...);

Sample result:

{
  data: [
    {
      "last_modified": 1456183153840,
      "id": "posts"
    },
    {
      "last_modified": 1456183159386,
      "id": "comments"
    }
  ]
}

Options

This method accepts the generic parameters for sorting, filtering and paginating results.

Deleting a collection

client.bucket("blog").deleteCollection("test")
  .then(result => ...);

Sample result:

{
  "data": {
    "deleted": true,
    "last_modified": 1456183116571,
    "id": "posts"
  }
}

Options

Creating a user group

Kinto has a concept of groups of users. A group has a list of members and belongs to a bucket.

Permissions can refer to the group instead of an individuals - this makes it easy to define «roles», especially if the same set of permissions is applied to several objects.

When used in permissions definitions, the full group URI has to be used:

    {
      data: {
        title: "My article"
      },
      permissions: {
        write: ["/buckets/blog/groups/authors", "github:lili"],
        read: ["system.Everyone"]
      }
    }

Named group

client.bucket("blog").createGroup("admins")
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456183004372,
    "id": "admins",
    "members": []
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

With a list of members and attributes

client.bucket("blog").createGroup("admins", ["system.Authenticated"], {data: {pi: 3.14}})
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456183004372,
    "id": "admins",
    "members": ["system.Authenticated"],
    "pi": 3.14
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

With an ID generated automatically

client.bucket("blog").createGroup()
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456183040592,
    "members": [],
    "id": "7YHFF565"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

Note that 7YHFF565 is the group ID automatically generated by the server.

Options

Note: For generated names, options can be specified only if the first parameters are provided: createGroup(undefined, [], {safe: true})

Listing bucket groups

client.bucket("blog").listGroups()
  .then(({data}) => ...);

Sample result:

{
  "data": [
    {
      "last_modified": 1456183153840,
      "id": "admins",
      "members": ["system.Authenticated"],
      "pi": 3.14
    },
    {
      "last_modified": 1456183159386,
      "id": "moderators",
      "members": ["github:lili"]
    }
  ]
}

Options

This method accepts the generic parameters for sorting, filtering and paginating results.

Getting a bucket group

client.bucket("blog").getGroup("admins")
  .then(({data}) => ...);

Sample result:

{
  "data": {
      "last_modified": 1456183153840,
      "id": "admins",
      "members": ["system.Authenticated"],
      "pi": 3.14
  }
}

Options

Updating an existing group

const updated = {
  id: "cb0f7b2b-e78f-41a8-afad-92a56f8c88db",
  members: ["system.Everyone", "github:lili"],
  pi: 3.141592
};

client.bucket("blog").updateGroup(updated, {permissions: {write: ["fxa:35478"]}})
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456183778891,
    "id": "cb0f7b2b-e78f-41a8-afad-92a56f8c88db",
    "members": ["system.Everyone", "github:lili"],
    "pi": 3.141592
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8",
      "fxa:35478"
    ]
  }
}

Options

Deleting a group

client.bucket("blog").deleteGroup("admins")
  .then(result => ...);

Sample result:

{
  "data": {
    "deleted": true,
    "last_modified": 1456183116571,
    "id": "admins"
  }
}

Options

Listing bucket history

client.bucket("blog").listHistory()
  .then(({data}) => ...);

Sample result:

{
  "data": [
    {
      "action": "update",
      "collection_id": "articles",
      "date": "2016-07-20T11:18:36.530281",
      "id": "cb98ecd7-a66f-4f9d-82c5-73d06930f4f2",
      "last_modified": 1469006316530,
      "record_id": "b3b76c56-b6df-4195-8189-d79da4a128e1",
      "resource_name": "record",
      "target": {
          "data": {
              "id": "b3b76c56-b6df-4195-8189-d79da4a128e1",
              "last_modified": 1469006316529,
              "title": "Modified title"
          },
          "permissions": {
              "write": [
                  "basicauth:43181ac0ae7581a23288c25a98786ef9db86433c62a04fd6071d11653ee69089"
              ]
          }
      },
      "timestamp": 1469006098757,
      "uri": "/buckets/blog/collections/articles/records/b3b76c56-b6df-4195-8189-d79da4a128e1",
      "user_id": "basicauth:43181ac0ae7581a23288c25a98786ef9db86433c62a04fd6071d11653ee69089",
    }
  ]
}

Options

This method accepts the generic parameters for sorting, filtering and paginating results.

Collections

Selecting a collection

const posts = client.bucket("blog").collection("posts");

Getting collection data

client.bucket("blog").collection("posts").getData()
  .then(result => ...);

Sample result:

{
  "last_modified": 1456183561206,
  "id": "posts",
  "preferedAuthor": "@chucknorris"
}

Options

Setting collection data

client.bucket("blog").collection("posts")
  .setData({preferedAuthor: "@chucknorris"})
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456183561206,
    "id": "posts",
    "preferedAuthor": "@chucknorris"
  },
  "permissions": {
    "write": [
      "github:bob",
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8",
      "github:john"
    ]
  }
}

Options

Getting collection permissions

client.bucket("blog").collection("posts").getPermissions()
  .then(result => ...);

Sample result:

{
  "write": [
    "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8",
  ]
}

Options

Setting collection permissions

client.bucket("blog").collection("posts")
  .setPermissions({
    read: ["github:bob"],
    write: ["github:john", "github:bob"]
  })
  .then(result => ...);

Sample result:

{
  "data": {
    "last_modified": 1456183508926,
    "id": "posts"
  },
  "permissions": {
    "read": ["github:bob"],
    "write": [
      "github:bob",
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8",
      "github:john"
    ]
  }
}

Options

Notes

Creating a new record

client.bucket("blog").collection("posts")
  .createRecord({title: "My first post", content: "Hello World!"})
  .then(result => ...);

Sample result:

{
  "data": {
    "content": "Hello World!",
    "last_modified": 1456183657846,
    "id": "cb0f7b2b-e78f-41a8-afad-92a56f8c88db",
    "title": "My first post"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

Options

Retrieving an existing record

client.bucket("blog").collection("posts")
  .getRecord("cb0f7b2b-e78f-41a8-afad-92a56f8c88db")
  .then(result => ...);

Sample result:

{
  "data": {
    "content": "Hello World!",
    "last_modified": 1456183657846,
    "id": "cb0f7b2b-e78f-41a8-afad-92a56f8c88db",
    "title": "My first post"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

Options

Updating an existing record

const updated = {
  id: "cb0f7b2b-e78f-41a8-afad-92a56f8c88db",
  title: "My first post, edited",
  content: "Hello World, again!"
};

client.bucket("blog").collection("posts")
  .updateRecord(updated)
  .then(result => ...);

Sample result:

{
  "data": {
    "content": "Hello World, again!",
    "last_modified": 1456183778891,
    "id": "cb0f7b2b-e78f-41a8-afad-92a56f8c88db",
    "title": "My first post, edited"
  },
  "permissions": {
    "write": [
      "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
    ]
  }
}

Options

Deleting record

client.bucket("blog").collection("posts")
  .deleteRecord("cb0f7b2b-e78f-41a8-afad-92a56f8c88db")
  .then(result => ...);

Sample result:

{
  "data": {
    "deleted": true,
    "last_modified": 1456183877287,
    "id": "cb0f7b2b-e78f-41a8-afad-92a56f8c88db"
  }
}

Options

Listing records

client.bucket("blog").collection("posts")
  .listRecords()
  .then(result => ...);

Sample result:

{
  last_modified: "1456183930780",
  next: <Function>,
  totalRecords: 2,
  data: [
    {
      "content": "True.",
      "last_modified": 1456183930780,
      "id": "a89dd4b2-d597-4192-bc2b-834116244d29",
      "title": "I love cheese"
    },
    {
      "content": "Yo",
      "last_modified": 1456183914275,
      "id": "63c1805a-565a-46cc-bfb3-007dfad54065",
      "title": "Another post"
    }
  ]
}

The result object exposes the following properties:

Options

This method accepts the generic parameters for sorting, filtering and paginating results.

Batching operations

This allows performing multiple operations in a single HTTP request.

client.bucket("blog").collection("posts")
  .batch(batch => {
    batch.deleteRecord("cb0f7b2b-e78f-41a8-afad-92a56f8c88db");
    batch.createRecord({title: "new post", content: "yo"});
    batch.createRecord({title: "another", content: "yo again"});
  })
  .then(result => ...);

Sample result:

[
  {
    "status": 200,
    "path": "/v1/buckets/blog/collections/posts/records/a89dd4b2-d597-4192-bc2b-834116244d29",
    "body": {
      "data": {
        "deleted": true,
        "last_modified": 1456184078090,
        "id": "a89dd4b2-d597-4192-bc2b-834116244d29"
      }
    },
    "headers": {
      "Content-Length": "99",
      "Content-Type": "application/json; charset=UTF-8",
      "Access-Control-Expose-Headers": "Retry-After, Content-Length, Alert, Backoff"
    }
  },
  {
    "status": 201,
    "path": "/v1/buckets/blog/collections/posts/records",
    "body": {
      "data": {
        "content": "yo",
        "last_modified": 1456184078096,
        "id": "afd650b3-1625-42f6-8994-860e52d39201",
        "title": "new post"
      },
      "permissions": {
        "write": [
          "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
        ]
      }
    },
    "headers": {
      "Content-Length": "221",
      "Content-Type": "application/json; charset=UTF-8",
      "Access-Control-Expose-Headers": "Retry-After, Content-Length, Alert, Backoff"
    }
  },
  {
    "status": 201,
    "path": "/v1/buckets/blog/collections/posts/records",
    "body": {
      "data": {
        "content": "yo again",
        "last_modified": 1456184078102,
        "id": "22c1319e-7b09-46db-bec4-c240bdf4e3e9",
        "title": "another"
      },
      "permissions": {
        "write": [
          "basicauth:0f7c1b72cdc89b9d42a2d48d5f0b291a1e8afd408cc38a2197cdf508269cecc8"
        ]
      }
    },
    "headers": {
      "Content-Length": "226",
      "Content-Type": "application/json; charset=UTF-8",
      "Access-Control-Expose-Headers": "Retry-After, Content-Length, Alert, Backoff"
    }
  }
]

Options

{
  "errors":    [], // Encountered errors (HTTP 400, >=500)
  "published": [], // Successfully published resources (HTTP 200, 201)
  "conflicts": [], // Conflicting resources (HTTP 412)
  "skipped":   []  // Missing target resources on the server (HTTP 404)
}

Listing all resource permissions

If the permissions_endpoint capability is installed on the server, you can retrieve the list of all permissions set for the authenticated user using the listPermissions() method:

client.listPermissions([options])
  .then(result => ...);

Sample result:

{
  "data": [
    {
      "bucket_id": "mybucket",
      "id": "mybucket",
      "permissions": [
        "write",
        "read",
        "group:create",
        "collection:create"
      ],
      "resource_name": "bucket",
      "uri": "/buckets/mybucket"
    },
    ...
  ]
}

Options

Result object properties

Attachments

If the attachment capability is available from the Kinto server, you can attach files to records. Files must be passed as data urls, which can be generated using the FileReader API in the browser.

Adding an attachment to a record

client.bucket("blog").collection("posts")
  .addAttachment(dataURL, {title: "First post"});

Options

Updating an attachment

client.bucket("blog").collection("posts")
  .addAttachment(dataURL, {id: "22c1319e-7b09-46db-bec4-c240bdf4e3e9"});

Deleting an attachment

client.bucket("blog").collection("posts")
  .removeAttachment("22c1319e-7b09-46db-bec4-c240bdf4e3e9");

Generic bucket and collection options

Both bucket() and collection() methods accept an options object as a second arguments where you can define the following options:

Sample usage:

client.bucket("blog", {
  headers: {"X-Hello": "Hello!"},
  safe: true,
  retry: 2,
});

Here the X-Hello header and the safe option will be used for building every outgoing request sent to the server, for every collection attached to this bucket.

This works at the collection level as well:

client.bucket("blog")
  .collection("posts", {
    headers: {"X-Hello": "Hello!"},
    safe: true,
    retry: 2,
  });

Every request sent for this collection will have the options applied.

Last, you can of course pass these options at the atomic operation level:

client.bucket("blog")
  .collection("posts")
  .updateRecord(updatedRecord, {
    headers: {"X-Hello": "Hello!"},
    safe: true,
    retry: 2,
  });

The cool thing being you can always override the default defined options at the atomic operation level:

client.bucket("blog", {safe: true})
  .collection("posts")
  .updateRecord(updatedRecord, {safe: false});

The safe option explained

The safe option can be used:

Safe creations

When creating a new ressource, using the safe option will ensure the resource will be created only if it doesn't already exist on the server.

Safe updates

If a last_modified property value is set in the resource object being updated, the safe option will ensure it won't be overriden if it's been modified on the server since that last_modified timestamp, raising an HTTP 412 response describing the conflict when that happens:

const updatedRecord = {
  id: "fbd2a565-8c10-497a-95b8-ce4ea6f474e1",
  title: "new post, modified",
  content: "yoyo",
  last_modified: 1456184189160
};

client.bucket("blog")
  .collection("posts")
  .updateRecord(updatedRecord, {safe: true});

If this record has been modified on the server already, meaning its last_modified is greater than the one we provide , we'll get a 412 error response.

If no last_modified value is provided at all, a safe update will simply guarantee that an existing resource with the provided ID won't be overriden.

Safe deletions

The same applies for deletions, where you can pass both a safe and last_modified options:

client.bucket("blog")
  .collection("posts")
  .deleteRecord("fbd2a565-8c10-497a-95b8-ce4ea6f474e1", {
    safe: true,
    last_modified: 1456184189160
  });

Generic options for list operations

Every list operations like listBuckets(), listCollections, listHistory, listGroups() or listRecords() accept parameters to sort, filter and paginate the results:

Sorting

By default, results are listed by last_modified descending order. You can set the sort option to order by another field:

client.bucket("blog").collection("posts")
  .listRecords({sort: "title"})
  .then(({data, next}) => {

Polling for changes

To retrieve the results modified since a given timestamp, use the since option:

client.bucket("blog").collection("posts")
  .listRecords({since: "1456183930780"})
  .then(({data, next}) => {

Paginating results

By default, all results of the first page are retrieved, and the default configuration of the server defines no limit. To specify a max number of results to retrieve, you can use the limit option:

client.bucket("blog").collection("posts")
  .listRecords({limit: 20})
  .then(({data, hasNextPage, next}) => {

To check if a next page of results is available, you can check for the hasNextPage boolean property. To actually fetch the next page of results, call the next() function obtained:

client.bucket("blog").collection("posts")
  .listRecords({limit: 20})
  .then(({data, hasNextPage, next}) => {
    if (hasNextPage) {
      // resolve with data for page 2
      return next().then(({data}) => data);
    } else {
      // resolve with data for page 1
      return data;
    }
  });

Last, if you just want to retrieve and aggregate a given number of result pages, instead of dealing with calling next() recursively you can simply specify the pages option:

client.bucket("blog").collection("posts")
  .listRecords({limit: 20, pages: 3})
  .then(({data, hasNextPage, next}) => ...); // A maximum of 60 results will be retrieved here
Notes

If you plan on fetching all the available pages, you can set the pages option to Infinity. Be aware that for large datasets this strategy can possibly issue an excessive number of HTTP requests.

Events

The KintoClient exposes an events property you can subscribe public events from. That events property implements nodejs' EventEmitter interface.

The backoff event

Triggered when a Backoff HTTP header has been received from the last received response from the server, meaning clients should hold on performing further requests during a given amount of time.

The backoff event notifies what's the backoff release timestamp you should wait until before performing another operation:

const client = new KintoClient();

client.events.on("backoff", function(releaseTime) {
  const releaseDate = new Date(releaseTime).toLocaleString();
  alert(`Backed off; wait until ${releaseDate} to retry`);
});

The deprecated event

Triggered when an Alert HTTP header is received from the server, meaning that a feature has been deprecated; the event argument received by the event listener contains the following deprecation information:

const client = new KintoClient();

client.events.on("deprecated", function(event) {
  console.log(event.message);
});

The retry-after event

Some errors on the server side are transient (service unavailable or integrity errors). A Retry-After HTTP header in the response indicates the duration in seconds that clients should wait before retrying the request.

The retry-after event notifies what is the timestamp you should wait until before performing another operation:

const client = new KintoClient();

client.events.on("retry-after", function(releaseTime) {
  const releaseDate = new Date(releaseTime).toLocaleString();
  alert(`Wait until ${releaseDate} to retry`);
});

Note:

We also automatically retry all requests that have a Retry-After response.

Browser Compatibility

This library uses some features that are not supported on Internet Explorer or Safari.

Please add polyfills for these to get full functionality.

Upgrading

From 1.x to 2.x

Contributing

Coding style

All the JavaScript code in this project conforms to the prettier coding style. A command is provided to ensure your code is always formatted accordingly:

$ npm run cs-format

The cs-check command ensures all files conform to that style:

$ npm run cs-check

Consider installing the pre-commit hooks that automatically format your code and check that it's lint-free. To do so:

Integration tests

It's possible to run the integration test suite against an external Kinto server instance. To do so you need to define the TEST_KINTO_SERVER environment variable and set it to the server base URL:

$ TEST_KINTO_SERVER=https://my.kinto-server.tld/v1 npm test

Releasing

If you need to release a new version of kinto-http.js, you can follow the Release documentation.