{"id":32545978,"url":"https://github.com/integreat-io/integreat","last_synced_at":"2025-10-28T18:56:45.036Z","repository":{"id":27576485,"uuid":"77085685","full_name":"integreat-io/integreat","owner":"integreat-io","description":"Integration layer written in TypeScript","archived":false,"fork":false,"pushed_at":"2025-09-22T16:01:59.000Z","size":5177,"stargazers_count":14,"open_issues_count":36,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-30T01:41:29.267Z","etag":null,"topics":["api","data","integration","integreat"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/integreat-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-12-21T21:00:00.000Z","updated_at":"2025-08-28T12:25:39.000Z","dependencies_parsed_at":"2023-01-16T22:16:36.566Z","dependency_job_id":"d66b0ffd-2b09-4260-b16f-22c7730fd034","html_url":"https://github.com/integreat-io/integreat","commit_stats":{"total_commits":685,"total_committers":3,"mean_commits":"228.33333333333334","dds":0.004379562043795637,"last_synced_commit":"718c11973a347c673df9cd9d897ebc96fd5ba314"},"previous_names":["kjellmorten/integreat"],"tags_count":128,"template":false,"template_full_name":null,"purl":"pkg:github/integreat-io/integreat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integreat-io%2Fintegreat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integreat-io%2Fintegreat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integreat-io%2Fintegreat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integreat-io%2Fintegreat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/integreat-io","download_url":"https://codeload.github.com/integreat-io/integreat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/integreat-io%2Fintegreat/sbom","scorecard":{"id":490261,"data":{"date":"2025-08-11","repo":{"name":"github.com/integreat-io/integreat","commit":"48c7e1150c54b19dfadf1fbf4e61ce0d3beaf11b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":3,"reason":"4 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/publish.yaml:1","Warn: no topLevel permission defined: .github/workflows/verify.yaml:1","Warn: no topLevel permission defined: .github/workflows/version.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":2,"reason":"dependency not pinned by hash detected -- score normalized to 2","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yaml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/publish.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yaml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/publish.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/verify.yaml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/verify.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/verify.yaml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/verify.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/verify.yaml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/verify.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/verify.yaml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/verify.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/version.yaml:40: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/version.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/version.yaml:47: update your workflow using https://app.stepsecurity.io/secureworkflow/integreat-io/integreat/version.yaml/main?enable=pin","Info:   0 out of   8 GitHub-owned GitHubAction dependencies pinned","Info:   3 out of   3 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/publish.yaml:6"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":9,"reason":"SAST tool is not run on all commits -- score normalized to 9","details":["Warn: 17 commits out of 18 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-xffm-g5w8-qvg7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T18:56:47.336Z","repository_id":27576485,"created_at":"2025-08-19T18:56:47.336Z","updated_at":"2025-08-19T18:56:47.336Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281495102,"owners_count":26511350,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-28T02:00:06.022Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","data","integration","integreat"],"created_at":"2025-10-28T18:56:43.140Z","updated_at":"2025-10-28T18:56:45.025Z","avatar_url":"https://github.com/integreat-io.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![Integreat](media/logo.png)\n\nAn integration layer written in TypeScript.\n\n[![npm Version](https://img.shields.io/npm/v/integreat.svg)](https://www.npmjs.com/package/integreat)\n[![Maintainability](https://api.codeclimate.com/v1/badges/a5bd9841a47ff9f74577/maintainability)](https://codeclimate.com/github/integreat-io/integreat/maintainability)\n\nThe basic idea of Integreat is to make it easy to define how to send data to\nand receive data from a set of [**services**](#services), and expose them\nthrough a well defined interface, abstracting away the specifics of each\nservice.\n\nThere are a few concepts that makes this possible:\n\n- [**Transporters**](#transporters) and [**adapters**](#adapters) speak the\n  language of different types of services and standards of data exchange, and\n  does the basic translation to and from the structures used by Integreat. You\n  deal with familiar JavasScript objects, arrays, and primitive data types,\n  regardless of what the service expects.\n- [**Mutation pipelines**](#mutations) let you define how the data coming from\n  or going to a service should be transformed. This includes changing the\n  overal structure, renaming properties, transforming and filtering values with\n  transformer functions, etc. You may also provide your own transformer\n  functions.\n- [**Schemas**](#schemas) serve as a common normalization of data between\n  services. You define your own schemas and mutate data to and from them,\n  enabling inter-service sharing of data. If you have data in one schema, you\n  may send it to any service where you have set up the right mutations for this\n  schema, again abstracting away all service details.\n\nAll configuration is done through basic JSON-friendly structures, and you\ndefine your services with different endpoints, mutation pipelines,\nauthentication schemes, etc.\n\nYour configuration is spun up as an Integreat instance. To send and retrieve\ndata, you dispatch [**actions**](#actions) to your instance and get\n[**response**](#action-response) objects back. You may define [jobs](#jobs) to\nrun simple actions or longer \"flows\" consisting of several actions with\nconditions and logic. You may also configure [queues](#queues) to have actions\nrun in sequence or on a later time.\n\n```\n                   ____________________________________________________\n                  |                                                   |\n                  |                Integreat instance                 |\nAction ----|      |                                                   |\n           |-\u003e Dispatch \u003c-\u003e Schema \u003c-\u003e Mutation \u003c-\u003e Adapter \u003c-\u003e Transporter \u003c-\u003e Service\nResponse \u003c-|      |                                                   |\n                  |___________________________________________________|\n```\n\nTo deal with security and permissions, Integreat has a concept of an ident.\nOther authentication schemes may be mapped to Integreat's ident scheme, to\nprovide data security from a service to another service or to the dispatched\naction. A ground principle is that nothing that enters Integreat from an\nauthenticated service, will leave Integreat unauthenticated. What this means,\nthough, depends on how you define your services.\n\n# Table of contents\n\n1. [Usage](#usage)\n   1. [Install](#install)\n   2. [Basic example](#basic-example)\n2. [Integreat concepts](#integreat-concepts)\n   1. [Services](#services)\n   2. [Transporters](#transporters)\n   3. [Adapters](#adapters)\n   4. [Authenticators](#authenticators)\n   5. [Mutations](#mutations)\n   6. [Schemas](#schemas)\n   7. [Actions](#actions)\n   8. [Jobs](#jobs)\n   9. [Queues](#queues)\n   10. [Middleware](#middleware)\n3. [Debugging](#debugging)\n\n# Usage\n\n## Install\n\nRequires node v18.\n\nNote: This package is native [ESM](https://nodejs.org/api/esm.html). See this\nguide on how to [convert to or use ESM packages](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c).\n\nInstall from npm:\n\n```\nnpm install integreat\n```\n\nYou will probably also need some [transporters](#transporters) and\n[adapters](#adapters), and the basic transformers in\n[`integreat-transformers`](https://github.com/integreat-io/integreat-transformers).\n\n## Basic example\n\nThe following is the \"hello world\" example of Integreat. As most hello world\nexamples, this is a bit too trivial a use case to demonstrate the real\nusefulness of Integreat, but it shows you the simplest setup possible.\n\nHere, we fetch cat facts from the API endpoint\n'https://cat-fact.herokuapp.com/facts', which returns data in JSON and requires\nno authentication. The returned list of facts are mutated and cast to the\n`fact` schema. We only fetch data _from_ the service, and no data is sent _to_\nit.\n\n```javascript\nimport Integreat from 'integreat'\nimport httpTransporter from 'integreat-transporter-http'\nimport jsonAdapter from 'integreat-adapter-json'\n\nconst schemas = [\n  {\n    id: 'fact', // The id of the schema\n    shape: {\n      // The fields of the type\n      id: 'string', // An id field will always be included, but we define it here for readability\n      text: 'string', // The text of the cat fact\n      createdAt: 'date', // The created date (`createdAt` and `updatedAt` will always be dates)\n    },\n    access: { allow: 'all' }, // No access restrictions\n  },\n]\n\nconst services = [\n  {\n    id: 'catfact', // The id of the service\n    transporter: 'http', // Use the http transporter\n    adapters: ['json'], // Run the request and the response through the json adapter\n    options: {\n      transporter: {\n        // Options for the transporter\n        uri: 'https://cat-fact.herokuapp.com/facts', // Only the uri is needed here\n      },\n    },\n    endpoints: [\n      {\n        match: { action: 'GET', type: 'fact' }, // Match to a GET action for type 'fact'\n        mutation: {\n          $direction: 'from', // We're mutating data _from_ the service\n          // Here we're mutating `response.data` and \"setting it back\" where we found it ...\n          'response.data': [\n            'response.data[]',\n            {\n              $iterate: true, // Mutate each item in an array\n              id: '_id', // The id is called `_id` the data from the service\n              text: 'text', // text is called `text`\n              createdAt: 'createdAt', // Creation date is called `createdAt`\n            },\n          ],\n        },\n      },\n    ],\n  },\n]\n\n// Create the Integreat instance from our definitions and provide the\n// transporters and adapters we require.\nconst great = Integreat.create(\n  { schemas, services },\n  { transporters: { http: httpTransporter }, adapters: { json: jsonAdapter } },\n)\n\n// Prepare an action to fetch all cat facts from the service `catfact`\nconst action = { type: 'GET', payload: { type: 'fact', service: 'catfact' } }\n\n// Dispatch the action and get the response\nconst response = await great.dispatch(action)\n```\n\nThe `response` object will look like this:\n\n```javascript\n{\n   status: 'ok',\n   data: [\n    {\n      id: '58e008780aac31001185ed05',\n      $type: 'fact',\n      text: 'Owning a cat can reduce the risk of stroke and heart attack by a third.',\n      createdAt: new Date('2018-03-29T20:20:03.844Z')\n    },\n    // ...\n  ]\n}\n```\n\n# Integreat concepts\n\nAs mentioned in the introduction, the building blocks of Integreat are\nservices, transporters and adapters, mutation pipelines, and schemas.\n\n## Services\n\nA service is the API, database, FTP server, queue, etc. that you want to get\ndata from and/or set data to. We pass on a set of service definitions to\nIntegreat, specifying what transporter, adapters, authentication schemas it\nrequires, in adition to defining the different endpoints available on the\nservice, how they should be called, and how data should be mutated in each\ncase.\n\nWe'll get back to the details of all of this in turn, but first we want to\nhighlight how central the concept of a service is to Integreat. Basically, in\nIntegreat \"everything is a service\". A simple REST/JSON API is a service, a\ndatabase is a service, and everything external you want to communicate with are\nservices. Want to set up a queue to handle actions one by one? That's a\nservice. Want to cache data in a memory store? That's a service. Want to\nschedule actions to run on intervals? That's a service to. By simply defining\nservices and their specifics, you may set up a variety of different types of\nconfigurations with the same few building blocks. This is very powerful as\nsoon as you get into the right mindset.\n\nServices are configured by service definitions, and tells Integreat how to\nfetch data from a service, how to mutate this data to schemas, and how to send\ndata back to the service.\n\nThe service definition object includes the transporter id, adapter ids, any\nauthentication method, the endpoints for fetching from and sending to the\nservice, mutations that data to all endpoints will pass through, and options\nfor transporters, adapters, etc.\n\n```javascript\n{\n  id: \u003cservice id\u003e,\n  transporter: \u003ctransporter id\u003e,\n  adapters: [\u003cadapter id\u003e, \u003cadapter id\u003e, ...],\n  auth: \u003cauth config\u003e,\n  meta: \u003ctype id\u003e,\n  options: {...},\n  mutation: \u003cmutation pipeline\u003e,\n  endpoints: [\n    \u003cendpoint definition\u003e,\n    ...\n  ]\n}\n```\n\nService definitions are passed to Integreat on creation through the\n`Integreat.create()` function. There is no way to change service defintions\nafter creation.\n\nSee [mutations](#mutations) for a description of how to define the mutation\npipeline for a service.\n\nThe `auth` property should normally be set to the id of an\n[auth definition](#service-authentication), if the service requires\nauthentication. In cases where the service is authenticated by other means,\ne.g. by including username and password in the uri, set the `auth` property to\n`true` to signal that this is an authenticated service. For services accepting\nincoming actions, `auth` should be set to an object with\n`{ outgoing: \u003cauth id | true\u003e, incoming: \u003cauth id | true\u003e}`. To accept several\nincoming actions, provide an array of `\u003cauth id | true\u003e`, and they will be run\nfrom first to last until one of them returns an ident or an error other than\n`noaccess`.\n\n\u003e [!NOTE]\n\u003e When connecting to a service for listening, the `outgoing` auth is\n\u003e used. `incoming` is only used for validating the actions being dispatched\n\u003e \"back\" from the service.\n\nIn `options`, you may provide options for transporters and adapters. It is\nmerged with the `options` object on the endpoint. See\n[the `options` object](#options-object) for more on this.\n\n### Endpoints\n\nA service will have at least one endpoint, but often there will be several.\nEndpoints are the definitions of the different ways Integreat may interact with\na service. You decide how you want to set up the endpoints and what is the\nright \"endpoint design\" for a service, but there might be one endpoint for each\noperation that can be done with a type of data.\n\nFor example, let's say you have a simple REST API with blog articles and\nauthors. There will most likely be an endpoint to fetch all (or some) articles,\none endpoint for fetching one article by id, one endpoint for creating an\narticle, one for updating an article, and so on. And you'll have similar\nendpoints for authors, one endpoint for fetching all, one for fetching one by\nid, one endpoint for creating an author, etc. As this is REST, each endpoint\nwill address a different combination of urls and http verbs (through the\ntransporter).\n\nAs another example, you may be accessing a database of articles and authors\ndirectly. The configuration details will be very different than for a REST API,\nbut you'll probably have the same endpoints, fetching all articles, fetching\none, creating, updating, and the same all over for users. Instead of urls and\nhttp verbs, as for REST, these endpoints will address different databases and\ndifferent database operations (through the transporter).\n\n\u003e [!NOTE]\n\u003e This is not to say that Integreat requires you to set up endpoints\n\u003e exactly as described in these examples, it might be that you would like to\n\u003e set up an endpoint that handles many of these cases. The intention here is\n\u003e just to give you an understanding of what an endpoint is in Integreat.\n\nWhen you dispatch an action, Integreat will figure out what service and what\nendpoint to send the action to. The target service is often specified in the\naction payload with the `targetService` (or shorthand `service`) property, but\nif not, the default service of the schema specified with the payload `type`\nproperty, will be used.\n\nThe `targetService` property will be set on the the action payload when it is\nsent to the transporter, before it goes through the middleware. There are two\nexceptions to this, however. `targetService` will not be set for a queued\naction (and it will not be removed if it is already set), and it will not be\nset for actions where the `meta.options.doSetTargetService` flag is set to\n`false`.\n\nThe matching to an endpoint is done by finding the endpoint whose `match`\nobject matches the action with most accuracy. The rules of the endpoint\nmatching is describe in more details [below](#match-properties).\n\nHere's the format of an endpoint definition:\n\n```javascript\n{\n  id: \u003cendpoint id\u003e,\n  match: {\n    type: \u003cschema id\u003e,\n    scope: \u003c'collection'|'member'|'members'|'all'\u003e,\n    action: \u003caction type\u003e,\n    params: {...},\n    incoming: \u003cboolean\u003e,\n    conditions: [...]\n  },\n  validate: [\n    {\n      condition: \u003cmutation pipeline\u003e,\n      failResponse: \u003cresponse object\u003e\n    }\n  ],\n  mutate: \u003cmutation pipeline\u003e,\n  adapters: [\u003cadapter id\u003e, \u003cadapter id\u003e, ...],\n  auth: \u003cauth config\u003e,\n  allowRawRequest: \u003cboolean\u003e,\n  allowRawResponse: \u003cboolean\u003e,\n  castWithoutDefaults: \u003cboolean\u003e,\n  options: {...},\n}\n```\n\nAll of these properties are optional. An empty endpoint defintion object will\nmatch anything, pass on the action to the transporter untouched, and relay any\nresponse coming back. This might be what you need, but often you'll want to\nspecify a few things:\n\n- `id`: The endpoint may have an id, which you may use to specify that you want\n  an action to go to this particular id. However, most of the time you'll set\n  up the `match` object so that Integreat will decide what endpoint to use for\n  the action you dispatch.\n- `match`: The match object is used to decide the right endpoint for an action.\n  More one this in the [Match properties](#match-properties) section.\n- `validate`: This is an array of condition that have to be met in order for\n  Integreat to proceed with the endpoint. The `condition` is a mutation\n  pipeline that should return a truthy value for the validation to pass. Any\n  falsy value will cause the validation to fail. If `validate` is missing or\n  an empty array, no validation will be done. This may sound similar to `match\n  `, but `validate` is only processed after a match is found, and if the\n  validation fails, no other endpoint is considered. On a failing validation,\n  the `failResponse` is returned as the response from this action, or a\n  `badrequest` response if no `failResponse` is provided. There's also a\n  shorthand, where you set `failResponse` to a string, which will be the\n  `error` message of the `badrequest` response. The response is passed through\n  the mutation pipeline.\n- `mutate`: A mutation pipeline for the endpoint. The pipeline is run for both\n  actions going to a service and the response coming back, so keep this in mind\n  when you set up this pipeline. See [Mutation pipelines](#mutations)\n  for more on how to define the mutation. `mutation` is an alias for `mutate`.\n- `adapters`: An array of adapter ids that will be appended to the array of\n  adapters set on the service.\n- `auth`: Auth config that will override the `auth` config on the service. See\n  [description of `auth` under Services](#services) for more on this. The\n  endpoint `auth` will only apply in cases where we have an endpoint, like when\n  we're sending a request to a service or receiving an incoming request, but\n  when we're e.g. connecting to a service to start listening, the `auth` on the\n  service will be used. This also goes for incoming requests where the\n  transporter does not provide an action with the auth attempt.\n- `allowRawRequest`: When set to `true`, payload `data` sent to this endpoint\n  will not by cast automatically nor will an error be returned if the data is\n  not typed.\n- `allowRawResponse`: When set to `true`, response `data` coming from this\n  endpoint will not be cast automatically nor will an error be returned if the\n  data is not typed. The default is `false`, expcept for incoming endpoints\n  (endpoints where `match` object has `incoming: true`) where the default value\n  is `true`.\n- `castWithoutDefaults`: Set to `true` when you don't want to set default\n  values on casted data. This also means no `id` will be generated and no\n  `createdAt` or `updatedAt` will be set – when any of these are missing in\n  the data. Default is `false`.\n- `options`: This object is merged with the `options` object on the service\n  definition, and provide options for transporters and adapters. See\n  [the `options` object](#options-object) for more on this.\n\n#### Match properties\n\nAn endpoint may specify none or more of the following match properties:\n\n- `type`: When set, the endpoint will only be used for actions with the\n  specified schema type (the schema's id). `type` may also be an array of\n  types, matching any one of the schemas in the list.\n- `scope`: May be `member`, `members`, `collection`, or `all`, to specify that\n  the endpoint should be used to request one item (member) by id, several items\n  by ids (members), or an entire collection of items. Setting this to `member`\n  or `members` will only match actions with a payload `id` property, and the\n  `id` should be an array of ids for `members`. Not setting this property, or\n  setting it to `all`, signals an endpoint that will work for all scopes.\n- `action`: May be set to the type string of an action. The endpoint will match\n  only actions of this type. When this is not specified, any action type will\n  match. `action` may also be a list of action types, matching any of these.\n- `params`: This object should list all params that this endpoint supports. A\n  param in this context is any property on the action payload except `type`,\n  `id`, or `data`. Use the param name as key on this object and set the value\n  to `true` if it is required, and `false` if it is optional. When matching\n  endpoints, an action will only match if it has all the required params, and\n  in case several match, the endpoint with more specified params will be\n  preferred.\n- `incoming`: If this is `true`, it will only match incoming actions, if\n  `false` only outgoing, and if not set, it will match both.\n- `conditions`: An array of mutation pipelines that will be run on the action\n  to see if it's a fit for this endpoint. If all pipelines return a truthy\n  value, the endpoint is chosen (given that the other match properties also\n  match). We rely on JavaScript definition of 'truthy' here, so any value that\n  is not `false`, `null`, `undefined`, `0`, `NaN`, or `''` will be considered\n  truthy.\n\n\u003e [!NOTE]\n\u003e There used to be a `filters` property on the endpoint match object. It is\n\u003e still supported, but it's deprecated and will be removed in v1.1. Please use\n\u003e `conditions` instead.\n\n\u003e [!NOTE]\n\u003e Editor's note: Describe what incoming actions are, and give more details on\n\u003e filters.\n\nThere might be cases where several endpoints match an action, and in these\ncases the endpoint with the highest level of specificity will be used. E.g.,\nfor a `GET` action asking for resources of type `entry`, an endpoint with both\n`action: 'GET'` and `type: 'entry'` is picked over an endpoint matching all\n`GET` actions regardless of type. For `params` and `filters` this is decided by\nthe highest number of properties on these objects.\n\nThe order of the endpoints in the `endpoints` list matters only when two\nendpoints are equally specified with the same match properties specified. Then\nthe first one is used.\n\nWhen no match properties are set, the endpoint will match any actions, as long\nas no other endpoints match.\n\nFinally, if an action specifies the endpoint id with the `endpoint`\n[payload property](#payload-properties), this overrides all else, and the\nendpoint with the id is used regardless of how the match object would apply.\n\nExample service definition with endpoint match object:\n\n```javascript\n{\n  id: 'entries',\n  transporter: 'http',\n  endpoints: [\n    {\n      match: {\n        type: 'entry',\n        action: 'GET',\n        scope: 'collection',\n        params: {\n          author: true,\n          archive: false\n        }\n      },\n      options: {\n        transporter: {\n          uri: 'https://example.api.com/1.0/{author}/{type}_log?archive={archive}'\n        }\n      }\n    }\n  ],\n  // ...\n}\n```\n\n### Options object\n\nA service defintion may have `options` object in two places: Direction on the\nservice definition and on any of the endpoints. When an action is sent to an\nendpoint, the combination of the two `options` are used. Also, there may be\ndifferent options for the transporter and for the adapters.\n\nExample of an `options` object set on the service definition:\n\n```javascript\n{\n  id: 'entries',\n  options: {\n    uri: 'https://ourapi.com/v1',\n    transporter: {\n      method: 'POST',\n      incoming: { port: 3000 }\n    },\n    adapters: {\n      xml: { namespaces: { ... } },\n      // ...\n    }\n  }\n}\n```\n\nAny properties set directly on the `options` object or on a `transporter`\nproperty, are treated as options for the transporter. If there are properties\non both the `options` and a `transporter` object, they will be merged, with the\n`transporter` object having priority if conflicts. This is a shallow merge, so\nobjects used in the options will not be merged.\n\nIn the example above, the options passed to the transporter will include `uri`,\n`method`, and `incoming`.\n\nThe `incoming` object on the transporter options is a bit special, as it holds\nseparate options for transporters that support incoming requests trough the\n`listen()` method. If there are `incoming` objects on both the `options` and\n`transporter` objects, they will be merged, again with priority to the one on\nthe `transporter` object.\n\nNote that we recommend setting transporter options on the `transporter` object\nfor clarity, but both will work.\n\nAdapter options may be given in an `adapters` object, where each adapter may\nhave its own options, set with the id of the adapter as a key. In the example\nabove, the `xml` adapter will be given the `namespaces` object. A requirement\nfor this, is that the adapter actually have an id. Adapters provided directly\non service definition may not have an id, but all adapters that are referenced\nby an id, will also be given options set on that id, which is the common behavior.\n\nFinally, when all this sorting have been done on options from both the service\ndefinition and an endpoint, the two options structures are merged before being\nused. Here, the endpoint options take priority, so that you may set a general\noption on the service, and override it on the endpoint.\n\nExample of endpoint options overriding service options:\n\n```javascript\n{\n  id: 'entries',\n  options: {\n    transporter: {\n      uri: 'https://ourapi.com/v1',\n      method: 'GET',\n    }\n  },\n  endpoints: [\n    {\n      match: { ... }\n    },\n    {\n      match: { ... },\n      options: {\n        transporter: {\n          method: 'POST'\n        }\n      }\n    }\n  ]\n}\n```\n\nHere, the first enpoint will be given `method: 'GET'`, while the next will get\n`method: 'POST'`.\n\nBefore actions are passed through mutations and finally passed to the\ntransporter, the merged transporter options is set on an `options` property in\nthe `meta` object of the action. This way, you may also mutate these options\nbefore they reach the transporter.\n\n### Service authentication\n\nThis definition format is used to authenticate with a service:\n\n```javascript\n{\n  id: \u003cid\u003e,\n  authenticator: \u003cauthenticator id\u003e,\n  options: {\n    // ...\n  },\n  overrideAuthAsMethod: \u003cauth-as method\u003e,\n}\n```\n\n- `id`: The id used to reference this authentication, especially from the\n  [service definition](#services).\n- `authenticator`: The id of an [authenticator](#authenticators) used to\n  authenticate requests. Integreat comes with a few basic ones built in, and\n  there are others available.\n- `options`: An object of values meaningful to the authenticator. See the\n  documentation of each authenticator to learn how it should be configured.\n- `overrideAuthAsMethod`: Transporters specify a default method for getting an\n  auth object that makes sense for authenticating with the service. For\n  instance, the HTTP transporter has `asHttpHeaders` as the default, to get the\n  relevant auth headers to send with the request. With `overrideAuthAsMethod`,\n  you may override this in the service auth definition when relevant. Default\n  value is `undefined`, meaning \"no override\". Note that we say \"method\" here,\n  but the value is a string with the name of the auth-as method to use.\n\nThe authenticator is responsible for doing all the heavy-lifting, based on the\noptions provided in the service authentication definition.\n\n### Configuring service metadata\n\nIntegreat supports getting and setting metadata for a service. The most common\nuse of this is to keep track of when data of a certain type was last synced.\n\nSome services may have support for storing their own metadata, but usually you\nset up a dedicated service for storing other services' metadata. A few\ndifferent pieces goes into setting up a meta store:\n\n- A meta schema with the fields available as metadata\n- A service for storing metadata, with an endpoint suporting the metadata\n  schema\n- Possible a metadata mutation for the metadata service\n\nWhen all of this is set up, you activate the metadata on the service the\nmetadata will be stored for, by setting the `meta` property to the id of the\nschema defining the metadata fields. The `service` set on the schema will tell\nIntegreat what service to get and set the metadata from/to.\n\nThe schema will look something like this:\n\n```javascript\n{\n  id: 'meta', // You may give it any id you'd like and reference it on the `meta` prop on the service\n  service: \u003cid of service handling the metadata\u003e,\n  shape: {\n    \u003cmetadataKey\u003e: \u003ctype string\u003e,\n    // ...\n  }\n}\n```\n\nTo get or set metadata, use [`GET_META`](#get_meta) and [`SET_META`](#set_meta)\nwith the service you are getting metadata from as the `service`. Integreat will\nfigure out the rest.\n\n## Transporters\n\nA transporter handles all the details of sending and receiving data to and from\na service. When dispatching an action to a service, the action will be handled\nin a relevant manner for the type of service the transporter supports, e.g.\nsending an http requrest for the HTTP transporter, or doing a query to a\ndatabase for the MongoDb transporter. Some transporters may also support\nlistening to a service, e.g. the HTTP transporter listing for incoming requests\nor the MQTT transporter subscribing to events on a topic.\n\nIntegreat has transporters for some common cases, and more may come:\n\n- [Bull](https://github.com/integreat-io/integreat-transporter-bull)\n- [FTP](https://github.com/integreat-io/integreat-transporter-ftp)\n- [HTTP](https://github.com/integreat-io/integreat-transporter-http)\n- [MongoDb](https://github.com/integreat-io/integreat-transporter-mongodb)\n- [MQTT](https://github.com/integreat-io/integreat-transporter-mqtt)\n- [Redis](https://github.com/integreat-io/integreat-transporter-redis)\n\nYou may write your own transporters if your case is not covered by any of\nthese. Documentation on developing transporters are coming.\n\nIntegreat will handle the transporters based on you configurations, but there\nare some specifics to each transporter, like HTTP needing an `uri` option or\nMongoDb needing a `collection` option. See the documentation of each\ntransporter for more.\n\n## Adapters\n\nAdapters are working together with transporters to prepare the incoming and\noutgoing data in accordance with the type of services they support.\n\nAs an example, the HTTP transporter will return data from a response as a\nstring, since there is no common way to treat the response body. So for a JSON\nAPI, you will configure the JSON adapter to make sure the data from the\nmutations are sent as a JSON string, and that the JSON comming back from the\nservice is parsed before mutation starts. For a service using XML, you would\ninstead set up the XML adapter, and perhaps also the SOAP adapter, to again\nstringify and parse the data going back and forth.\n\nThe MongoDb transporter, on the other hand, does not require any adapters, as\ndocuments from the database will always come as arrays and object, and may be\nfed directly into the mutation pipelines.\n\nIntegreat currently have the following adapters:\n\n- [CSV](https://github.com/integreat-io/integreat-adapter-csv)\n- [JSON](https://github.com/integreat-io/integreat-adapter-json)\n- [SOAP](https://github.com/integreat-io/integreat-adapter-soap)\n- [Url encoded form data](https://github.com/integreat-io/integreat-adapter-form)\n- [URI templates](https://github.com/integreat-io/integreat-adapter-uri)\n- [XML](https://github.com/integreat-io/integreat-adapter-xml)\n\nYou may write your own adapters as well, and documentation on this is coming.\n\n## Authenticators\n\nAt its simplest, an authenticator will provide necessary credientials to an\noutgoing action, or an ident to an incoming action. Some authenticators do this\nbased only on the options provided, while others will do a more complex dance\nwith the service or a third-party service, like with OAuth2.\n\nWhen [setting up a service](#services), you may provide it with an auth id that\nrefers to a [service authentication definition](#service-authentication), that\nagain refers to an authenticator by id. The service auth definition also holds\noptions for the authenticator, so when assigning an auth id to a service,\nyou're assigning it an authenticator with those specific options. Another\nservice may use the same authenticator, but with different options, and you\nwould set this up with a different service authentication definition.\n\nAuthentication for outgoing actions are done when sending the action. When\nauthenticated, an auth object is retrieved with the auth-as method specified on\nthe transporter (e.g. `asHttpHeaders` for the http transporter), or on the\n`overrideAuthAsMethod` in [auth options](#service-authentication) if set. The\nauth object is passed to the transporter on the action `meta.auth` prop. It is\napplied just before sending it, though, so it will be available to service\nmiddleware, but not to the mutation pipeline. This is done to expose\ncredentials in as few places as possible. If you however _want_ to have the\nauth object in mutations, set `authInData` to `true` on the service or\nendpoint options, and authentication will be done in the `preflightAction`\nstep instead, making it available on `meta.auth` throughout the entire mutation pipeline.\n\nFor incoming actions, authentication is done when a listening action calls the\n`authenticate()` callback. The `validate()` method on the authenticator is used\nhere, which will provide the transporter with an authorized ident.\n\nAvailable authenticators:\n\n- `action`: Will dispatch an action and use the response data to create an\n  authentication. The `options` should have the action type as `action`, and\n  the entire payload as `payload`. The response data should have an `auth`\n  object, that will be used directly, and an optional `expire` that is a\n  timestamp on which the auth will expire, in milleseconds since Epoc\n  (1970-01-01). If no `expire` is returned, the `expireIn` option will be used\n  if present. It is given as milliseconds before the auth should expire, or a\n  `ms` string like `'1h'`.\n- `http`: Supports http native authentications, like `Basic` and `Bearer`. It's\n  included with the\n  [HTTP transporter](https://github.com/integreat-io/integreat-transporter-http).\n- `ident`: Will always grant access and `validate()` will return an ident with\n  the id provided in `identId` on the `options` object, or `'anonymous'` if no\n  `identId` is provided. This is built into Integreat.\n- `options`: Will pass on the options as authentication, so whatever you\n  provide here is the authentication. What options to provide, then, is\n  depending on what the relevant transporter requires. For outgoing actions,\n  the options are provided as is. Incoming action are validated agains the\n  values given in the options (the keys may be dot notation paths in this\n  case, and `identId` is excluded). An ident with the `identId` from the\n  options as `id`, is returned if the action matches. This is built into\n  Integreat.\n- `token`: A simple way of authenticating with a given token. For HTTP\n  requests, the token will be provided as a `Authorization` header, and a\n  configurable prefix like `Basic` or `Bearer`. This is built into Integreat.\n- [`jwt`](https://github.com/integreat-io/integreat-authenticator-jwt): Will\n  generate and encode a JavaScript Web Token (JWT) based on the options.\n- [`oauth2`](https://github.com/integreat-io/integreat-authenticator-oauth2):\n  Will run the balett of calling different OAuth2 endpoints and receive a token\n  based on the provided options.\n\n## Mutations\n\nBoth on the service and on endpoints, you define mutation pipelines. The\nservice mutation is run before the endpoint mutation for data coming from a\nservice, and in the oposite order when going to a service.\n\nA nice - but sometimes complicated - thing about mutations, is that they are\nrun in both directions. They are by default defined for mutating data coming\n_from_ a service, and will be run in reverse for data going _to_ a service. In\nsome cases this reversing of the pipeline will work as expected without\nmodifications -- you define the mutation pipeline for data coming _from_ the\nservice, and the reversed pipeline works _to_ as well. But many times you need\nto make adjustments and sometimes you'll have to have separate steps based on\nthe direction. We'll get into more details in the following.\n\nA mutation pipeline consists of one or more steps that the data will go\nthrough, before coming out on the other in the desired shape. It helps\npicturing this as an actual pipeline. After each step, data will be in a\ndifferent shape, and this is the input to the next step.\n\nYou define a pipeline in Integreat with an array, although for a pipeline with\nonly one step, you may skip the array for simplicity.\n\nEach step may be one of the following:\n\n- [**A dot notation path**](#dot-notation-paths), e.g. `path.to.data`. The data\n  at that path will be extracted, and will be provided as the data to the next\n  step in the pipeline. When going in reverse, the data will be set on that\n  path instead.\n- **A mutation object** is an object that basically describes the object you\n  want as a result, where the keys are dot notation paths and the values are\n  mutation pipelines. Each pipeline on the mutation object will be run on the\n  data, and then set on the path, resulting in an object that will be passed on\n  to the next step. Setting `$iterate: true` on the object will cause it to\n  iterate over items in an array, otherwise it will be applied to the array.\n  Setting `$modify: true` will cause any properties on an object in the\n  pipeline not set in the mutation, to be included, much like the spread in\n  JavaScript. Setting `$modify` to a path works the same, but you will spread\n  from the object at the path (`$modify: true` is equal to `$modify: '.'`).\n- **A transform object** letting you run a transformer function on the data,\n  e.g. `{ $transform: 'number' }` to transform the value into a number, or\n  `undefined` if not possible.\n- **A filter object** that will run a transformer function on the data and\n  filter away any items not resulting in a truthy value. As an example,\n  `{ $filter: 'boolean' }` will filter away anything that is not convertable to\n  `true` in JS rules. When applied to an array, you'll get an array where items\n  are filtered away. For an object or a plain value, filtering away will means\n  `undefined` is passed on to the next step in the pipeline.\n- **An if object** that runs a `then` pipeline if the provided pipeline returns\n  truthy, and the `else` pipeline if it returns falsy.\n- **A cast object**, e.g. `{ $cast: 'author' }` that casts the data into a\n  schema, removing all properties that is not part of the shape of the schema,\n  and transforming all values to the expected types or `undefined` if not\n  possible. (Not available until v1.0)\n\n### Dot notation paths\n\nAt its most basic, a dot notation path is just a property key, like `content`.\nYou may dive into a data structure by adding a key from the next level,\nseparated by a dot, like `content.articles`. With an object like this:\n\n```javascript\n{\n  content: {\n    articles: [{ id: '1' }, { id: '2' }],\n    authors: [{ id: 'john' }]\n  }\n}\n```\n\n... the path `content.articles` will give you the array\n`[{ id: '1' }, { id: '2' }]`.\n\nYou may add brackets to the path to traverse into arrays, e.g.\n`content.articles[0]` will give you the object `{ id: '1' }`, and\n`content.articles[0].id` will give you `'1'`.\n\nEmpty brackets, like `content.articles[]` will ensure that you get an array\nback. If the data at the path is an array, this will return the same as\n`content.articles`, but if the path returns an object or a plain value, it will\nbe returned in an array.\n\nWhen mapping data _to_ a service, the paths are used to reconstruct the data\nformat the service expects. Only properties included in the paths will be\ncreated.\n\nArrays are reconstructed with any object or value at the first index, unless a\nsingle, non-negative index is specified in the path.\n\nYou may use a carret `^` to go one level up -- to the parent -- in the data\n(after going down), so after `content.articles`, the path `^.authors` will\nreturn `[{ id: 'john' }]`. Arrays count as one level, so after\n`content.articles[0]` you will need to go up twice like so: `^.^.authors`.\n\nA double carret `^^` takes you to the top -- the root -- so after\n`content.articles[0].id`, `^^.content.authors` returns `[{ id: 'john' }]`.\n\nCarret notations -- parents and roots -- does not currently work in reverse,\nbut they might in a future version.\n\n### Non-values\n\nThe behavior of some transformers are based upon certain values being\nnon-values. E.g. `{ $alt: [\u003cpipeline 1\u003e, \u003cpipeline 2\u003e] }` will use the value\nfrom the first pipeline if it returns a value, otherwise the value from the\nsecond pipeline, meaning it will check for non-values. By default `null`,\n`undefined`, and `''` (empty string) are non-values. By setting the `nonvalues`\nparam to an array of values in the defintions object you pass to\n`Integreat.create()`, you may specify your own non-values.\n\nIf you don't want empty string to a non-value, for instance, you do this:\n\n```javascript\nconst great = Integreat.create({\n  nonvalues: [null, undefined],\n  // ... other definitions\n})\n```\n\n## Schemas\n\nA central idea to Integreat, is that any integration has two sides; the getting\nof data from one service and the sending of data to another. Instead of setting\nup an integration directly from A to B, you have a schema in middle, and\nconfigure how data from A will be mutated to a schema, and then have data in\nthat schema will be mutated and sent to B.\n\nThis is a useful abstraction, and if you ever need to change one side, you can\ndo so without involving the other side. If you need to switch out service B\nwith service C, you can do so without involving the configuration of service\nA, or you can send data to both B and C, using the same setup for service A.\n\nTo be clear, you can setup flows without schemas in Integreat, but then you may\nloose this flexibility and maintainability.\n\nA schema describe the data you expected to get out of Integreat, or send\nthrough it. You basically define the fields and their types, and may then cast\ndata to that shape. Note that data on an action for a specified type, will be\nautomatically cast to that type.\n\n```javascript\n{\n  id: \u003cschema id\u003e,\n  plural: \u003cthe id in plural\u003e,\n  service: \u003cthe default service for this schema\u003e,\n  shape: {\n    \u003cfieldId\u003e: \u003cfield type\u003e,\n    \u003cfieldId\u003e: {\n      $type: \u003cfield type\u003e,\n      default: \u003cdefault value\u003e\n      const: \u003cvalue that will override any other value\u003e\n    },\n  },\n  access: \u003caccess def\u003e\n}\n```\n\n- `id`: The id of the schema, used to reference it in actions (the payload\n  `type`), when casting to the schema with `{ $type: '\u003cschema id\u003e' }`, and to\n  signal what schema a data object is cast to (the `$type` prop on typed data\n  items). The convention is to use singular mode for the `id`, e.g. if your\n  defining a schema for articles, you would give it the id `'article'`.\n- `plural`: When the plural of `id` is not simply a matter of adding an `'s'`,\n  you may specify the plural mode here. E.g. `id: 'entry'` would have\n  `plural: 'entries'`. This is not used by Integreat right now, but it may be\n  used in the future for error messages, generating APIs from schemas, etc.\n- `service`: You may specify a default service for the schema when it makes\n  sense. This allows you to dispatch an action for a type without specifying\n  the target service, e.g. `{ type: 'GET', payload: { type: 'article' } }`,\n  and have Integreat use the default service. This is a way of hiding\n  configuration details from the code dispatching the actions, and you may\n  also change the default service without changing the dispatching code if\n  need be. You may always override this by specifying a `service` on the\n  action payload.\n- `shape`: This is where you define all the fields, see\n  [the section below](#the-shape-of-a-schema).\n- `generateId`: Set this to `true` to generate a unique id for the `id` field\n  when the data being cast does not provide an `id`. Default is `false`, which\n  will just set `id: null`. The id will be 36 chars and consist of A-Z, a-z,\n  0-9, underscore `'_'`, and hyphen `'-'`.\n- `access`: Integreat lets you define authorization schemes per schema. All use\n  of data cast to a schema will then be controlled by the rules you set here.\n  See [Access rules](#access-rules) below for details on these rules. Note that\n  `access` is optional, but when you get data from a service where any form of\n  authentication is used to access the data, you will not be able to do\n  anything with the data unless you cast it to a schema with `access` set up\n  (or specifically says that you allow raw data from that endpoint).\n\n### The shape of a schema\n\nThe shape is defined by an object where each key is the id of a field, which\nmay contain only alphanumeric characters, and may not start with a digit. A\nschema cannot have the same id as a primitive type (see list below).\n\nThe values on this object define the types of the fields and a few other\noptional features:\n\n```javascript\n{\n  $type: \u003cfield type\u003e,\n  default: \u003cdefault value\u003e\n  const: \u003cvalue that will override any other value\u003e\n}\n```\n\nThe `$type` prop sets the type of the field. The available primitive types, are\n`string`, `integer`, `float` (or `number`), `boolean`, and `date`. A field may\nalso have another schema as its type, in which case the id of the schema is set\nin `$type`. An example can be an\n`article` schema with an `author` field of type `user`, referring to a schema\nwith id `user`. When casting the `article`, data on the `author` prop will be\ncast with the `user` schema.\n\nThe `default` value will be used when the field is `undefined`, `null`, or not\npreset in data object being cast to this schema. If `default` is set to a\nfunction, the function will be run with no argument, and the returned value is\nused as the default value. When no `default` is given, `undefined` is used.\n\nThe `const` value override any value you provide to the field. It may be useful\nif you want a field to always have a fixed value. Just as for `default`, you\nmay set it to a function, in which case the function will be run without\narguments and the returned value will be used.\n\nIf both `const` and `default` are set, `const` will be used.\n\nWhen only setting the field type, you don't need to provide the entire object,\nyou can just provide the type string.\n\nExample schema:\n\n```javascript\n{\n  id: 'article',\n  shape: {\n    id: 'string', // Not needed, as it is always provided, but it's good to include for clarity\n    title: { $type: 'string', default: 'Unnamed article' },\n    text: 'string',\n    readCount: 'integer',\n    archived: { $type: 'boolean', default: false },\n    rating: 'float',\n    createdAt: 'date',\n    updatedAt: 'date'\n  },\n  access: 'all'\n}\n```\n\nNote that if you provide the `id` field, it should be set to type `'string'` or\nIntegreat will throw. The same happens if you set `createdAt` or `updatedAt` to\nanything else than the type `'date'`. If you don't include these fields,\nIntegreat will include the `id` for you, but not `createdAt` or `updatedAt`.\n\n### Typed data\n\nWhen data is cast to a schema, the data will be in the following format:\n\n```\n{\n  id: \u003cstring\u003e,\n  $type: \u003cschema\u003e,\n  createdAt: \u003cdate\u003e,\n  updatedAt: \u003cdate\u003e,\n  \u003ckey\u003e: \u003cvalue\u003e,\n  \u003ckey\u003e: { id: \u003cstring\u003e, $ref: \u003cschema\u003e },\n  \u003ckey: [{ id: \u003cstring\u003e, $type: \u003cschema\u003e, ... }],\n  ...\n}\n```\n\n- `id`: The id is mandatory and created by Integreat when it is not included in\n  the schema. If you don't map anything to the id prop, it will be set to\n  `null`, unless the schema is set up with `generateId: true`, in which case a\n  universally unique id will be generated for you.\n- `$type`: Set to the id of the schema by Integreat. This is a signal that the\n  data has been cast.\n- `createdAt`: This is not mandatory, but has special meaning. When a schema\n  has a `createdAt` field, but the date is not set in the data, it will be set\n  to the same as `updatedAt` (if provided) or to the current date/time.\n- `updatedAt`: Just as `createdAt`, this is not mandatory. When a schema has\n  an `updatedAt` field, and the date is not set in the data, it will be set to\n  the same as `createdAt` (if provided) or the current date/time.\n- `\u003ckey\u003e`: Then follows the values of all the fields specified in the schema.\n  Any value not provided in the data will be set to their default value, unless\n  `castWithoutDefaults` is set to `true` in\n  [the endpoint definition](#endpoints). When casting a value results in\n  `undefined`, it will not be included on the returned object. Fields that has\n  the `id` of other schemas as their type, will be objects. If only the id is\n  provided in the data, the `{ id: \u003cstring\u003e, $ref: \u003cschema id\u003e }` format will\n  be used, with `$ref` being the id of the field type schema. When more data is\n  provided, Integreat will cast it to the target schema and provide the entire\n  data object, or array of objects, with the relevant `$type`.\n\n### Access rules\n\nSet the `access` property on a schema to enforce permission checking. This\napplies to any service that provides data in this schema.\n\nThe simplest access rule is `auth`, which means that anyone can do anything\nwith the data of this schema, as long as they are authenticated. Being\nauthenticated, in this context, means that the dispatched action has an\n`ident` in the `meta` object. See [the section on idents](#idents) for more on\nthis.\n\nExample of a schema with an access rule:\n\n```javascript\n{\n  id: 'article',\n  shape: {\n    // ...\n  },\n  access: 'auth'\n}\n```\n\nTo signal that the schema really has no need for authorization, use `all`.\nThis is not the same as not setting the `auth` prop, as `all` will override\nIntegreat's principle of not letting authorized data out of Integreat without\nan access rule. `all` allows anybody to access the data, even the\nunauthenticated.\n\nOn the other end of the spectrum, `none` will allow no one to access data cast\nto this schema, no matter who they are.\n\nFor more fine-grained rules, set `access` to an access definition object with\nrules telling Integreat which rights to require when performing different\nactions with a given schema. These rules apply to the [idents](#idents) set on\nthe action `meta` object.\n\nThe following access rule props are available:\n\n- `allow`: Set to `all`, `auth`, or `none`, to give access to everybody, only\n  the authenticated, or no one at all. This is what we describe in short form\n  above, where we provided this string instead of a access rule object.\n- `role`: Authorize only idents with this `role`. May also be an array.\n- `ident`: Authorize only idents with this precise `id`. May also be an array.\n- `roleFromField`: Same as `role`, except the role is gotten from a field in\n  the schema. When authorizing data cast to this schema, the value of the role\n  field needs to be identical to (one of) the role(s) of the ident.\n- `identFromField` - The same as `roleFromField`, but for an ident id.\n\nIn addition, you may override the general access rules of a schema with\nspecific rules for a type of action, by setting an `action` object with access\nrules for action types. Here's an example of an access definition for allowing\nall authorized idents to `GET` data in a certain shema, requiring the role\n`admin` for `SET`s, and disallowing all other actions with the general rule\n`allow: 'none'`:\n\n```javascript\n{\n  id: 'article',\n  shape: {\n    // ...\n  },\n  access: {\n    allow: 'none',\n    actions: {\n      GET: { allow: 'auth' },\n      SET: { role: 'admin' }\n    }\n  }\n}\n```\n\nNote that these action specific rules only applies to actions being sent to a\nservice. Some actions will never reach a service, but will instead trigger\nother actions, and access will be granted or rejected only for the actions\nthat are about to be sent to a service. E.g. when you dispatch a `SYNC`\naction, it starts off by dispatching one or more `GET` actions. The `SYNC`\naction is not subjected to any access rules, but the `GET` actions are, and so\nthe `SYNC` will fail if one of the `GET` is rejected.\n\nAnother example, intended for authorizing only the ident matching a user:\n\n```javascript\n{\n  id: 'user',\n  shape: {\n    // ...\n  },\n  access: { identFromField: 'id' }\n}\n```\n\nHere, only actions where the ident id is the same as the id of the user data,\nwill be allowed. This means that authenticated users (idents) may only\nonly access their own user data.\n\n## Actions\n\nActions are serializable objects that are dispatched to Integreat. It is a\nimportant that they are serializable, as this allows them to, for instance, be\nput in a database persisted queue and be picked up of another Intergreat\ninstance in another process. Note that `Date` objects are considered\nserializable, as they are converted to ISO date strings when needed.\n\nAn action looks like this:\n\n```javascript\n{\n  type: \u003caction type\u003e,\n  payload: \u003cpayload object\u003e,\n  meta: \u003cmeta object\u003e\n}\n```\n\n- `type`: This is the id of one [of the action handlers](#available-action-handlers)\n  that comes with Integreat, e.g. `GET`. When you dispatch an action, it is\n  handed off to this handler (after some inital preperation). You may write\n  your own action handlers as well.\n- `payload`: Holds parameters and [data](#typed-data) for this action. There\n  are some reserved [payload properties](#payload-properties), and the rest\n  will be made available to you in the mutation pipeline.\n- `meta`: Holds information about the action that does not belong in the\n  payload, like the ident of the user dispatching, action id, etc. There are\n  some reserved [meta properties](#meta-properties), but you may add your own\n  here too.\n\nWhen an action is dispatched, it returns a [response object](#action-response)\nwith status, data, error message, etc.\n\nNote that in a mutation pipeline, action handler, or middleware, the\nresponse object is provided as a fourth property on the action. You will most\nlikely meet this at least when setting up mutations.\n\nIntegreat will keep track of how many actions have been dispatched and are\ncurrently being process. The Instance object (`great` in\n[the example](#basic-example) at the beginning of this README) has a\n`dispatchedCount` property that gives you the number dispatched actions waiting\nto be completed. Every time all dispatched are completed, a `done` event will\nbe emitted.\n\n### Payload properties\n\nThe payload is, together with the action `type`, a description to Integreat and\nthe service of what to do. A design principle of Integreat has been to have as\nlittle specifics in these payload, so actions may be discpatched to service\nwithout knowing how the service works. This is not always possible, at least\nnot yet, but it's a good principle to follow, also when you configure services\nand plan what props need to be sent in the action payload.\n\nYou may set any properties on the payload, and they will be be available to you\nin the service endpoint match and in the service mutations. Some properties\nhave special meanings, though, and you should avoid using them for anything\nelse:\n\n- `type`: The type of the data the action sends and/or receives. This refers to\n  the `id` of a schema, and you will usually want to set this. Data provided\n  in the payload `data` and response `data` will be cast to this schema. If\n  you're dealing with several types in one action, you may set an array of\n  types, but will have to cast the data in the mutation yourself. Integreat\n  will validate that the data you send and receive is indeed of that type, and\n  will give you an auth error if not. (See\n  [`allowRawRequest` and `allowRawResponse` on endpoints](#endpoints) for an\n  exception.)\n- `id`: You provide an id when you want to address a specific data item,\n  usually when you want to fetch one data item with an action like\n  `{ type: 'GET', payload: { type: 'article', id: '12345' } }`. You may also\n  supply an array of ids to fetch several data items by id. When setting data,\n  the id will instead be specified in the `data` when appropriate.\n- `data`: The data to send to a service. This may be any data that makes sense\n  to the service, but will usually be a [typed data object](#typed-data) or an\n  array of typed data objects, where the adjustments for the service happens in\n  service mutations.\n- `service`: The `id` of the service to send this action to. If not specified,\n  Integreat will try and find the right service from the `type`.\n- `targetService`: An alias of `service`.\n- `sourceService`: When data comes from a different service and has not been\n  mutated and cast yet, the `sourceService` property will tell Integreat to run\n  the data through the source service configuration before passing the action\n  on to an action handler. An example may be data coming in through an API,\n  where the API is configured as a service in Integreat. Note that this\n  property is usually set by transporters in their `listen()` methods, but you\n  may also set it directly on the action when it makes sense.\n- `endpoint`: Set this to the `id` of a service endpoint when you want to\n  override the endpoint match rules of Integreat. This should only be used when\n  it is really necessary. Normally, you should instead design the match\n  properties to match the correct actions.\n\nFor services that support pagination, i.e. fetching data in several rounds, one\npage at a time, the following properties may be supported:\n\n- `pageSize`: The number of data items to fetch in one request to the service.\n  By specifying a page size, you signal that you would like to use pagination,\n  and without it all other pagination properties will be disregarded. You will\n  get the number of data items you specify (or less, if there are not that many\n  items), and may then go on to dispatch an action for the next page. See\n  [pagination](#pagination) for more\n- `pageOffset`: The number of data items to \"skip\" before returning the number\n  of items specified in `pageSize`. If you ask for 500 items, the first action\n  should have `pageOffset: 0` (or not specified), the next action\n  `pageOffset: 500`, then `pageOffset: 1000`, and so on.\n- `page`: The index of the page to fetch. Unlike most other indexes, this\n  starts with `1` being the first page. The effect is the same as\n  `pageOffset`, it's just a different way of specifying it. `page: 1` is the\n  same as `pageOffset: 0`, and `page: 2` is the same as `pageOffset: 500`,\n  given a `pageSize: 500`. Integreat will actually calculate both before\n  sending it to the transporter, as different types of services support\n  different types of pagination.\n- `pageAfter`: As an alternative to specifying the number of items to skip, you\n  may ask for the items after the item with the id you provide as `pageAfter`.\n  If the last item of the first page is `'12345'`, you may set\n  `pageAfter: '12345'` to get the next page.\n- `pageBefore`: This works the same as `pageAfter`, except it is intended for\n  when your going backward and fetching a number items _before_ the id you\n  provide.\n- `pageId`: Some services and/or transporters will return an id for the next\n  page, as an alternative to the other properties mentioned above. You then\n  apply this id as `pageId` when dispatching the action for the next page. Note\n  that this id may hold internal logic from the transporter, but you should\n  never rely on this logic and simply use it as an id.\n\n\u003e [!IMPORTANT]\n\u003e Pagination has to be supported by the service and your\n\u003e service configuration, and sometimes also the transporter. Integreat prepares\n\u003e and passes on these pagination properties, but if the service disregards\n\u003e them, there is little Integreat can do – except limiting the number of items\n\u003e returned. It's up to you to figure out how to configure pagination for a\n\u003e service, but youshould use these pagination properties to support it, to make\n\u003e this predictable. It also lets you use actions such as `GET_ALL`, that\n\u003e support pagination.\n\nFinally, there are some properties that has no special meaning to Integreat\nitself, but that may be set on incoming actions from transporters. These should\nideally be used in the same way or avoided:\n\n- `contentType`: A keyword for the type of content in the `data` property. E.g.\n  `application/json` or `text/plain`.\n- `headers`: An object of header information, given as key/value pairs. The\n  value may be a string or an array of strings. This may be HTTP headers or any\n  other type of header information that makes sense to a service.\n- `hostname`: The host name that incoming request was sent to. For HTTP, this\n  will be the domain name the request was sent to.\n- `method`: The method of the incoming request. The HTTP transporter will set\n  this to `GET`, `POST`, `PUT`, etc. from the incoming request.\n- `path`: The path from the incoming request. For the HTTP transporter, this\n  will be the part of the url after the domain name, like\n  `'/v1.0/articles/12345'`.\n- `port`: The port number of the incoming request.\n- `queryParams`: An object of query params from the incoming request, usually\n  key/value pairs where the value is a string or an array of strings. For HTTP,\n  this will be the part after the question mark.\n\n### Meta properties\n\nThe action meta object is for information about an action that does not\ndirectly define the action itself. The difference may be subtle in some cases,\nbut the general rule is a piece of information affects how the action is run,\nit should be in the payload. E.g. the type of items to fetch is in the\npayload, while the time the action was dispatched would go in the meta.\n\nThis rule does not always hold, e.g. for information on the user dispatching\nthe action in `ident` on the meta object. Different idents may result in\ndifferent data being returned from the service, but still the action to\nperform is the same, so it makes sense to have the ident in the meta object.\n\nYou may set your own meta properties, but in most cases you'll probably rather\nset payload properties.\n\nCurrent meta properties reserved by Integreat:\n\n- `ident`: The ident to authorize the action with. May hold an `id`, `roles`,\n  `tokens`, and a few other options. See\n  [the section on idents](#idents).\n- `id`: The id of the action itself. You may set this yourself or let Integreat\n  generate a universally unique id for you. Useful for logging and may be used\n  by queues.\n- `cid`: Correlation id. When dispatching an action without `meta.cid`,\n  Integreat will set it to the same as the `meta.id`. All actions that are then\n  dispatched as a consequence of that action (e.g. a `SYNC` or `GET_META`),\n  will have the same `cid`. The `cid` may then be used to group actions\n  belonging together, e.g. when displaying logs. The dispatching code set the\n  `cid` on an action, e.g. to correlate an action and the actions it\n  dispatches, with other operations outside Integreat.\n- `gid`: Group id. This has some of the same purpose as `cid`, as it may be\n  used to group actions that belong together, but `gid` is not always set, and\n  will be used for smaller groups of actions than `cid`. Right now, `RUN`,\n  `SYNC`, and `GET_ALL` will use the `id` of the original action as `gid` for\n  all actions they dispatched.\n- `dispatchedAt`: Timestamp for when the action was dispatched (set by\n  Integreat).\n- `queue`: Signals to Integreat that an action may be queued. Set to `true`\n  when you want the action to be queued, but executed as soon as possible. Set\n  to a UNIX timestamp (number) to schedule for a later time. If no queue is\n  set up, the action will be dispatched right away. More on this under\n  [the section on queues](#queues).\n- `queuedAt`: Timestamp for when the action was pushed to the queue (set by\n  Integreat).\n- `options`: Used for passing the processed service endpoint options object to\n  a transporter. The `options` object is available through mutations, so that\n  you may modify it futher before it goes to the transporter. Note that only\n  the transporter options are provided here, not the adapter options.\n- `authorized`: An internal flag signaling that the action has been authorized.\n  Will be removed from any dispatched actions.\n\n### Action response\n\nWhen you dispatch an action, you will get a response object back in this\nformat:\n\n```javascript\n{\n  status: \u003cstatus code\u003e,\n  data: \u003cdata from the service, usually mutated\u003e,\n  error: \u003cerror message\u003e,\n  warning: \u003cwarning message\u003e,\n  origin: \u003ccode telling where an error originated\u003e\n  access: \u003cholds the ident actually being used\u003e,\n  paging: \u003cpagination objects\u003e,\n  params: \u003ckey/value pairs\u003e,\n  headers: \u003ckey/value pairs\u003e,\n  responses: \u003carray of sub-responses when relevant\u003e,\n}\n```\n\n- `status`: The status of the action. Will be `ok` when everything went well,\n  see [list of status codes](#status-codes) below for more.\n- `data`: Any data returned from the service, after being modified by the\n  mutation pipelines from your service and endpoint configuration. It will be\n  cast to [typed data](#typed-data) through the schema specified by the payload\n  `type`, if it is set to a single type and the endpoint `allowRawResponse` is\n  not set to `true`.\n- `error`: All error statuses (i.e. not `ok` or `queued`) will return an error\n  message, some may include error messages from the service.\n- `warning`: When the action was successful, but there still was something you\n  should know, the warning message is where you'll get noticed. An example is\n  when you get an array of data items, but some of them was removed due to the\n  access of the ident on the action.\n- `origin`: When the response is an error (status is not `'ok'` or `'queue'`),\n  this property will hold a code for where the error originated. The goal is to\n  set it as close to the actual origin as possible. See\n  [list of origin codes](#origin-codes) below.\n- `access`: An object holding the `ident` that was actually being used. This\n  may be different than the `meta.ident` on the action, as the ident may also\n  be mutated or completed with roles etc. along the way.\n- `paging`: For services and transporters that support\n  [pagination](#pagination), this object will hold information about how to get\n  the next or previous page, in a `next` or `prev` object. These objects are\n  essentially the payloads you need to dispatch (with the same action `type`\n  and meta), to get the next or previous page. If there is no next or previous\n  page, the corresponding prop will not be set on the `paging` object. When\n  pagination is not relevant or used, the `paging` object may be missing\n  completely.\n- `params`: Integreat never sets this, but you may set it in your mutations to\n  provide parameters from a service that does not belong in the `data`.\n- `headers`: Integreat never sets this, but you may set it in your mutations to\n  provide header key/value pairs from a service. Typically used when this is a\n  response to an incoming request that support headers, like HTTP do.\n- `responses`: In some cases, an action will run several sub-actions, like\n  `SYNC` or `RUN`. The action handlers _may_ then provide an array of all the\n  sub-response objects here.\n\n\u003e [!NOTE]\n\u003e Editor's note: Is it correct that queues return the id in the data?\n\nWhen the status is `queued`, the id of the queued action may found in\n`response.data.id`. This is the id assigned by the queue, and not necessarily\nthe same as `action.meta.id`.\n\n### Status codes\n\nThe `status` property on the action response will be one of the following\nstatus codes:\n\n- `ok`: Everything is well, data is returned as expected\n- `queued`: The action has been queued. This is regarded as a success status\n- `noaction`: The action did nothing, e.g. when a `SYNC` action has no data to\n  sync\n- `notfound`: Tried to get or modify a resource that does not exist\n- `timeout`: The attempt to perform the action timed out\n- `toomany`: Too many actions/requests were sent. This is normally used for\n  rate limiting errors from a service.\n- `autherror`: An authentication request failed\n- `noaccess`: Authentication is required or the provided auth is not enough\n- `badrequest`: Request data is not as expected\n- `badresponse`: Response data is not as expected\n- `error`: Any other error\n\n### Origin codes\n\nThe `origin` property is not exclusively defined, but these are some of the\nmore common codes:\n\n- `service:\u003cservice id\u003e`: The error originated in service. There may also be\n  third level of detail here, if the service sets an origin code of its own.\n  E.g. `'service:entries:handshake`.\n- `middleware:service:\u003cservice id\u003e`: The error happened in the middleware chain\n  on the service side.\n- `internal:service:\u003cservice id\u003e`: Used for errors in the service class, that\n  has nothing to do with the actual service, e.g. if the service class is not\n  configured correctly.\n- `mutate:request`: The error was set in a request mutation pipeline.\n- `mutate:response`: The error was set in a response mutation pipeline.\n- `auth:action`: The error occured while attempting to authorize the action.\n- `auth:data`: The error occured while attempting to authorize data in an\n  action payload or a response.\n- `auth:service:\u003cservice id\u003e`: The error occured while attempting to authorize\n  the service with the given id.\n- `auth:service:\u003cservice id\u003e:\u003cauthenticator id\u003e`: The error occured while\n  attempting to authorize the service with the given id, through the given\n  authenticator.\n- `handler:\u003chandler id\u003e`: The error occurred with the handler with the given\n  id, e.g. `'handler:GET'`. This means the error did happen in the service or\n  the mutation pipelines, but in the internal workings of then handler.\n- `validate:service:\u003cservice id\u003e:endpoint:\u003cendpoint id\u003e`: Validation of an\n  action against an endpoint failed. Note that not all endpoints has an id, in\n  which case that part of the origin code is left out.\n- `middleware:dispatch`: The error happened within the middleware chain, on the\n  `dispatch()` end (not on the service end).\n- `dispatch`: This is the lowest level of origin, as the error happened within\n  the `dispatch()` method.\n\n### Idents\n\nAn ident in Integreat is basically an id unique to one participant in the\nsecurity scheme. It is represented by an object that may also have other\nproperties to describe the ident's access, like `roles`, or to make it possible\nto match to identities in other services.\n\nExample ident:\n\n```javascript\n{\n  id: 'ident1',\n  tokens: ['auth0|12345', 'github|23456'],\n  roles: ['admin']\n}\n```\n\n- `id`: A unique string identifying the ident. The actual value is irrelevant\n  to Integreat, as long as it is a string with A-Z, a-z, 0-9, \\_, and -, and\n  it's unique within one Integreat configuration. This means that mapped\n  values from services may be used as ident ids, as long as they are unique\n  among these services.\n- `tokens`: A list of values that may identify this ident in other services.\n  For example, an api that uses GitHub OAuth to identify its users, may provide\n  the `'github|23456'` token in the example above, which will be replaced with\n  this ident when it enters Integreat.\n- `roles`: A list of roles or permissions given to this ident. The roles are\n  custom defined per setup, and may be mapped to roles from other systems. When\n  setting the auth rules for a schema, you specify required rules so that to\n  get data cast in this schema, an ident with e.g. the role `admin` must be\n  provided.\n- `type`: An optional string to signal when this ident is `'ROOT'`, `'SCHED'`,\n  `'SYST'`, or `'ANON'`. This is used internally by Integreat, but in some\n  cases you may want to set this yourself. Make sure, however, that you don't\n  let third-parties set `'ROOT'`. Make sure to also set the id, typically to\n  `'root'` or `'anonymous'`. Not setting any `type` is the same as setting it\n  to `'CUST'`, which is the default.\n- `isCompleted`: A flag to signal that an ident has already been completed, so\n  that it won't be completed again. Used by the `completeIdent` middleware. You\n  should normally not need to set this yourself.\n\nThe ident may also hold other custom properties, defined in the\n`identConfig.mapping` in the definitions passed when created a new Integreat\ninstance. See description under [Idents](#idents).\n\nActions are authenticated by setting an ident on the `meta.ident` property.\nIt's up to the code dispatching an action to get hold of the properties of an\nident in a secure way. Once Integreat receives an ident through a dispatch, it\nwill assume this is accurate information and uphold its part of the security\nagreement and only return data and execute actions that the ident have\npermissions for.\n\nNote that it's possible to set up\n[the `completeIdent` middleware](#completeIdent-middleware) for combining\ninformation from the authenticator with user information held e.g. in a\ndatabase.\n\n### Available action handlers\n\n#### `GET`\n\nGet data from a service. You receive the data on the `data` property, after it\nhas been run through your service and endpoint mutations.\n\nExample GET action to a collection of data items:\n\n```javascript\n{\n  type: 'GET',\n  payload: { type: 'article' }\n}\n```\n\nBy providing an `id` property on `payload`, the item with the given id and type\nis fetched, if it exists:\n\n```javascript\n{\n  type: 'GET',\n  payload: { type: 'article', id: '12345' }\n}\n```\n\nSee [the section on payload properties](#payload-properties) for more\nproperties that may be used with the `GET` action.\n\n#### `GET_ALL`\n\nWill run as many `GET` actions as needed to the get all available pages of\ndata.\n\nThe action ...\n\n```javascript\n{\n  type: 'GET_ALL',\n  payload: { type: 'article', pageSize: 500 }\n}\n```\n\n... will dispatch the following action is sequence:\n\n```javascript\n{\n  type: 'GET',\n  payload: { type: 'article', pageSize: 500 }\n}\n```\n\n```javascript\n{\n  type: 'GET',\n  payload: { type: 'article', pageSize: 500, pageOffset: 500 }\n}\n```\n\n... and so on, until we get no more data.\n\nSee [the section on pagination](#pagination) for more on the paging properties.\n\nNote that `GET_ALL` requires a `pageSize` param, and will return a `badrequest`\nresponse if one is not set.\n\n#### `SET`\n\nSend data to a service. The data to send is provided in the payload `data`\nproperty. Recomended practice is to provide the data as\n[typed data](#typed-data), i.e. data objects cast to a schema, and let\nmutations on the service endpoint modify it to the format the service expects.\n\nAny data coming back from the service, will be provided on `response.data` and\nmay be mutated through service endpoint mutations, just as for [`GET`](#get)\nactions.\n\nExample `SET` action:\n\n```javascript\n{\n  type: 'SET',\n  payload: {\n    type: 'article',\n    data: [\n      { id: '12345', $type: 'article', title: 'First article' },\n      { id: '12346', $type: 'article', title: 'Second article' }\n    ]\n  }\n}\n```\n\n#### `UPDATE`\n\nUpdate data on a service. The idea is that while `SET` is used for setting data\nto a service – with no regard to what is actually set in the service already,\n`UPDATE` is used for updating data, possibly not overwriting all properties.\nIf `UPDATE` provides data with only a few properties, the expectation is that\nonly these properties will be updated in the service. The `UPDATE` action is\nalso expected to fail when the item being updated does not exist, unlike `SET`,\nthat will usually create it.\n\nNote that the actual behavior is up to how you set up the service and what the\nservice itself supports, but the `UPDATE` action will provide you with a way of\ndoing this.\n\nAn `UPDATE` action may be handled in one of two ways, where the first is just\nto run it against a service endpoint, much like a `SET` action (except it will\nmatch different endpoints). Data provided in the payload `data` is mutated and\nsent to the service according to the endpoint configuration, and any data\ncoming back, will be provided on `response.data` and mutated.\n\nWhat makes `UPDATE` different from `SET`, though, is the second way we may\nhandle `UPDATE` actions. Whenever there is no maching `UPDATE` endpoint,\nIntegreat will run the action as a `GET` and then a `SET`, to mimick and\nupdate. The `GET` action will have the same `payload` and `meta` as the\noriginal action. The same goes for the `SET` action, but the `payload.data`\nwill be the data returned from `GET` merged with the data on the original\n`UPDATE` action. This will be a deep merge, prioritizing properties from the\n`UPDATE` action, but any `createdAt` date in the data from `GET` will be kept.\nIf there's an `updatedAt` in the merged data, it will be overriden by the\ncurrent time.\n\nA requirement for this approach to work as expected, is that the data is casted\nto the same schema, but that should normally be the case when you use\n`payload.type` and don't set `allowRawRequest` or `allowRawResponse` on the\nendpoint.\n\nWhen a `GET` fail, the `UPDATE` will fail with the same status and error.\n\nExample `UPDATE` action:\n\n```javascript\n{\n  type: 'UPDATE',\n  payload: {\n    type: 'article',\n    data: [\n      { id: '12345', $type: 'article', title: 'First article' },\n      { id: '12346', $type: 'article', title: 'Second article' }\n    ]\n  }\n}\n```\n\n#### `DELETE` / `DEL`\n\nDelete one or more items from a service. Set the data for the items to delete,\nin the payload `data` property as an array of [typed data](#typed-data).\nIn most cases, you only need to provide the `id` and the `$type`, but the way\nyou set up the service may require more properties.\n\nAny data coming back from the service, will be provided on `response.data` and\nmay be mutated through service endpoint mutations, just as for [`GET`](#get)\nactions.\n\nExample `DELETE` action:\n\n```javascript\n{\n  type: 'DELETE',\n  payload: {\n    type: 'article',\n    data: [\n      { id: '12345', $type: 'article' },\n      { id: '12346', $type: 'article' }\n    ]\n  }\n}\n```\n\nYou may also `DELETE` one item like this:\n\n```javascript\n{\n  type: 'DELETE',\n  payload: {\n    id: 'ent1',\n    type: 'entry'\n  }\n}\n```\n\n`DEL` is a shorthand for `DELETE`.\n\n#### `GET_META`\n\nGet metadata for a service. See\n[the section on metadata](#configuring-service-metadata) for how to set this\nup.\n\nThe `data` of the response from this aciton contains the `service` (the service\nid) and `meta` object with the metadata set as properties.\n\nExample `GET_META` action:\n\n```javascript\n{\n  type: 'GET_META',\n  payload: {\n    service: 'entries',\n    keys: ['lastSyncedAt', 'status']\n  }\n}\n```\n\nThis will return data in the following form:\n\n```javascript\n{\n  status: 'ok',\n  data: {\n    service: 'entries',\n    meta: {\n      lastSyncedAt: '2017-08-19T17:40:31.861Z',\n      status: 'ready'\n    }\n  }\n}\n```\n\nIf the action has no `keys`, all metadata set on the service will be retrieved.\nThe `keys` property may be an array of keys to retrieve several in one request,\nor a single key.\n\n#### `SET_META`\n\nSet metadata on a service. The payload should contain the `service` to get\nmetadata for (the service id), and a `meta` object, with all metadata to set as\nproperties.\n\nAny data coming back from the service, will be provided on `response.data` and\nmay be mutated through service endpoint mutations, just as for [`GET`](#get)\nactions.\n\nExample `SET_META` action:\n\n```javascript\n{\n  type: 'SET_META',\n  payload: {\n    service: 'entries',\n    meta: {\n      lastSyncedAt: Date.now()\n    }\n  }\n}\n```\n\nNote that the service must be set up to handle metadata. See\n[Configuring metadata](#configuring-service-metadata) for more.\n\n#### `RUN`\n\nThe `RUN` action will run jobs provided to `Integreat.create()` in the jobs\ndefinitions. These jobs will then run other actions or series of action, also\ncalled \"flows\".\n\nOnly one payload property is required – the `jobId`, which refers to a job in\nthe jobs definitions. Any other properties on the payload will be passed on as\ninput to the job.\n\nAn action for running the `archiveOutdated` job:\n\n```javascript\n{\n  type: 'RUN',\n  payload: { jobId: 'archiveOutdated' }\n}\n```\n\nSee [the section on jobs](#jobs) for more on how to configure jobs.\n\n#### `SYNC`\n\nThe `SYNC` action will `GET` items from one service and `SET` them on another.\nThere are different options for how to retrieve items, ranging from a crude\nretrieval of all items on every sync, to a more fine grained approach where\nonly items that have been updated or created since last sync, will be synced.\n\nThe simplest action definition would look like this, where all items would be\nretrieved from the service and set on the target:\n\n```javascript\n{\n  type: 'SYNC',\n  payload: {\n    type: \u003citem type\u003e,\n    retrieve: 'all',\n    from: \u003cservice id | payload\u003e,\n    to: \u003cservice id | payload\u003e\n  }\n}\n```\n\nThe action will dispatch a `GET` action right away, and then immediately\ndispatch a `SET_META` action to update the `lastSyncedAt` date on the service.\nThe `SET` actions to update the target service is added to the queue if one is\nconfigured.\n\nTo retrieve only new items, change the `retrieve` property to `updated`. In\nthis case, the action will dispatch `GET_META` to get the `lastSyncedAt` from\nthe `from` service, and get only newer items, by passing it the `updatedAfter`\nparam. The action will also filter out older items, in case the service does\nnot support `updatedAfter`.\n\nBy setting `retrieve` to `created`, you accomplish the same, but with\n`createdAfter`.\n\nIf you need to include more params in the actions to get from the `from`\nservice or set to the `to` service, you may provide a params object for the\n`from` or `to` props, with the service id set as a `service` param. You may\nalso provide different action types than `GET` and `SET`, by setting the\n`action` prop on the `from` or `to` objects respectively.\n\nBy default, `SYNC` will send every item gotten from the `from` service to the\n`to` service. You can split an array of items into several sets, by setting the\n`maxPerSet` on the payload object, to a max number of items per set. If you\nneed to have one set per individual item, you may set `setMember` to `true` on\nthe payload object. (This is almost the same as setting `maxPerSet` to `1`,\nexcept it won't be wrapped in an array.)\n\n\u003e [!NOTE]\n\u003e There are more options than these, and the documentation will be updated to\n\u003e include them later.\n\n#### `EXPIRE`\n\nThe `EXPIRE` action have two alternative way of operating. If the\n`deleteWithParams` param is `false` or not set, we will first dispatch a `GET`\naction to fetch expired data items from a service, and the then dispatch a\n`DELETE` action with the retrieved data items. If `deleteWithParams` is `true`,\nwe will instead dispatch a `DELETE` action right away with the same params we\nwould have provided to the `GET` action.\n\nHere's an example of an `EXPIRE` action that will dispatch a `GET` and a\n`DELETE`:\n\n```javascript\n{\n  type: 'EXPIRE',\n  payload: {\n    service: 'store',\n    type: 'entry',\n    endpoint: 'getExpired',\n    msFromNow: -24 * 60 * 60 * 1000 // Delete entries older than 24 hours\n  }\n}\n```\n\nHere's an example of an `EXPIRE` action that will dispatch a `DELETE` directly:\n\n```javascript\n{\n  type: 'EXPIRE',\n  payload: {\n    service: 'store',\n    type: 'entry',\n    deleteWithParams: true\n  }\n}\n```\n\nThe `GET` action (or the `DELETE` action when `deleteWithParams` is `true`)\nwill have a `timestamp` property with the current time as microseconds since\nepoc (Januar 1, 1970 UTC), and `isodate` as the current time in the extended\nISO 8601 format(`YYYY-MM-DDThh:mm:ss.sssZ`).\n\nTo have `timestamp` and `isodate` be a time in the future instead, set\n`msFromNow` to a positive number of milliseconds. This will be added to the\ncurrent time. To have a time in the past, use a negative number for\n`msFromNow`.\n\n#### `SERVICE`\n\nA `SERVICE` action will be sent directly to the specified service without any\nintervention by Integreat. This allows for running specialized actions on the\nservice that goes beyond what Integreat supports. It's up to each transporter\nto support such actions, describe what they'll do, and define their payload\nproperties.\n\nAn example of an action that will tell a\n[Bull](https://github.com/integreat-io/integreat-transporter-bull) queue to\nclean out all completed jobs more than a week old:\n\n```javascript\n{\n  type: 'SERVICE',\n  payload: {\n    type: 'cleanCompleted',\n    targetService: 'bullService',\n    olderThanMs: 604800000\n  }\n}\n```\n\n### Write your own action handlers\n\nYou may write your own action handlers to handle dispatched actions just like\nthe built-in types.\n\nAction handler signature:\n\n```javascript\nasync function (action, { dispatch, getService, setProgress, options }) { ... }\n```\n\n- `action`: This is the dispatched action after it has been modified a bit\n  by the `dispatch()` method and possible after running an incoming mutation on\n  it. The modifications include cleaning up alias fields (e.g. `service` will\n  be set as `targetService`), removing sensitive or forbidden fields, and\n  setting a few default or internal fields (like the `dispatchedAt` meta).\n- `dispatch`: From the handler, you may dispatch your own sub actions to the\n  provided `dispatch()` method. Note that this is an \"internal dispatch\n  method\", so it will return an action with the `response` object on it,\n  instead of just the `response` object. It's good practice to set the `cid`\n  meta prop for the actions you dispatch, to the `cid` meta prop on the\n  `action` you're handling. You should also use the same `ident` unless you\n  have very good reasons to do otherwise, to make sure you don't create\n  security holes.\n- `getService`: This is a convenience method that will return the relevant\n  service object when you provide it with a type and optional a service id.\n  With a service id, you'll get the service with that id, with only the type,\n  you'll get the default service for that type. E.g.: `getService('article')`.\n- `setProgress`: For long running tasks, you may want to set the progress along\n  the way. Progress is specified as a number between `0` and `1`, e.g.\n  `setProgress(.5)` to signal that you're halfway through. When the your\n  handler is finished, the progress will automatically be set to `1`. This may\n  be used by queue implementations etc., to give progress feedback to users\n  and to know the action has not gone stale.\n- `options`: This is an object with a few settings: `queueService` is the id of\n  the service set up as the default queue, and `identConfig` is the config\n  object used for mapping ident schemas to ids, roles, and tokens (see\n  [the `completeIdent` middleware](#completeIdent-middleware)). In addition to\n  the middleware, you may now set `identConfig.completeIdent` to `true` to have\n  Integreat complete idents on dispatch and from the authentication callback\n  used for incoming actions.\n\nYour action handler must return a [response object](#action-response). If your\nhandler just relays to another action handler, it may pass on the response\nreturned from that handler, but in many cases it will be more correct to\ngenerate your own response.\n\nYou provide your custom actions to Integreat on setup, by providing an object\nwith the key set to the action type your handler will be responsible for, and\nthe handler function as the value:\n\n```javascript\nconst actions = {\n  `MY_ACTION`: async function myAction (action, { dispatch }) { ... }\n}\nconst great = Integreat.create(defs, { schemas, services, mappings, actions })\n```\n\nNote that if you set up your custom action handler with an action type that is\nalready used by one of Integreat's built-in action handlers, the custom handler\nwill have precedence. So be careful when you choose an action type, if your\nintention is not to replace an existing action handler.\n\n## Jobs\n\nYou define jobs to run one or more actions on a schedule or to add additional\nlogic that is not provided by one specific service endpoint. When you dispatch\nseveral actions, in sequence or in parallel, we call it a \"flow\".\n\nA simple job running on a schedule, may look like this:\n\n```javascript\nconst syncJob = {\n  id: 'syncEntries',\n  cron: '0 */1 * * *', // Every hour\n  action: {\n    type: 'SYNC',\n    payload: {\n      type: 'entry',\n      retrieve: 'updated',\n      from: 'entries',\n      to: 'store',\n    },\n  },\n}\n```\n\nThis will dispatch the given `SYNC` action every hour. (The `SYNC` action and\ncron syntax is out of scope in this section. Fron cron expressions,\n(Crontab)[https://crontab.cronhub.io] is a good and practical resource.)\n\nAn alternative to running a job on a schedule with `cron`, is to run it by\ndispatching a `RUN` action with the job id in the payload `jobId` param.\n\nA flow may look like this:\n\n```javascript\nconst flowJob = {\n  id: 'getEntryFromOtherService',\n  flow: [\n    {\n      id: 'getFromStore',\n      action: {\n        type: 'GET',\n        payload: {\n          type: 'entry',\n          targetService: 'store',\n        },\n      },\n      premutation: {\n        payload: {\n          $modify: 'payload',\n          id: '^^.action.payload.id',\n        },\n      },\n    },\n    {\n      id: 'getFromOtherService',\n      action: {\n        type: 'GET',\n        payload: {\n          type: 'entry',\n          targetService: 'otherService',\n        },\n      },\n      premutation: {\n        payload: {\n          $modify: 'payload',\n          id: '^^.getFromStore.response.data.otherId',\n        },\n      },\n    },\n  ],\n}\n```\n\nSeveral things are going on here: First, we have a flow with two actions. We\nimagine here that we are going to fetch an entry with an id that is found in\nthe `store` service, and use the `otherId` retrieved from that service to get\nthe entry from `otherService`. The two steps in `flow` look a lot like a job,\nand in one way they are the same, with some differences, that we will get back\nto. They are run sequentally in the order they appear in the `flow` array.\n\nSecondly, we have a `premutation` on each step. This is given the `action` and\nmay mutate it before it is dispatched. As for endpoint mutations, the top level\nhas `$modify: true` as default, but we need to modify the sub-objects we\ninclude, when that is what we want. In the first step, we set the payload `id`\nto the `id` provided in the action that called this job. This job is passed to\nmutations on jobs and steps under the name `'action'`, and we prepend it with\nthe root path (`^^`) as it is found on the top level of the data structure\nwe're mutating.\n\nThe second step is similar, but here we set the payload `id` to the `otherId`\nfound in the response data of the first step. The action and the response from\na step is available to all following steps by the id of the step, in this case\n`'getFromStore'`. We have to prepend with the root path (`^^`) here as well.\nWhen we say the action and response is available, we find it as an action\nobject with any response on a `response` property.\n\nWith the action\n`{ type: 'RUN', payload: { jobId: 'getEntryFromOtherService', id: '12345' } }`,\nthe first step will dispatch a `GET` for the id `12345`. If that action\nsucceeds with the data `{ id: '12345', otherId: '67890' }`, the second step\nwill dispatch a `GET` for the id `67890`. The response from the last action is\nreturned by default.\n\nIf any job step fails, the entire job will fail and the error will be returned,\nunless you set up any `preconditions`, `postconditions`, or `postmutations` to\nalter this default behavior. More on that in the following sections.\n\n### Job and step mutations\n\nJobs and job steps provide two mechanisms for mutations: `premutation` and\n`postmutation`. They work in a similar way, but `premutation` is used to\nmutate the action of a job or a job step _before_ it is dispatched, and\n`postmutations` is used to mutate the response from a job or a step.\n\nNote that `premutation` will not have an effect on a job with a `flow`, but\n`postmutation` may be used with the response from a flow.\n\nBoth mutation pipelines are passed an object that holds the action and response\nof every step that has been run so far, set on a property with the same key as\nthe step id. The response is given as a `response` property on the action. The\naction that was dispatched to run the job is also included on the `'action'`\nproperty. (Thus a step may not have the id `'action'`.) The object also holds\nthe action of the step on an internal property, and this is the starting point\nfor the mutations. You do not reference the step action directly, just mutate\nwas is given in the pipeline data and use the root path `^^` to get to the\nactions and responses of other steps.\n\nFor both pipelines, an action object is expected, and in the case of\n`postmutation` the action should have a `response` object which is what will be\nused as the response from the job or step.\n\n### Job and step conditions\n\nBy default, if a step in a flow fails, no more steps are run, and the entire\njob will fail with the response from the failed step. You may, however,\nprovide your own conditions for when a step should be run and when a step\nshould be regarded as having failed.\n\n`preconditions` may be set to an array of condition objects, that must all\npass in order for the step to be run. Each condition object must have a\n`condition` property with an array of mutation pipelines, and all these\npipelines must return truthy for the condition to be regarded as passed. Each\npipeline is given the same object that is given to `premutation` with action\nand responses from previous steps, but without the action for this step. See\n[the section on mutating jobs](#job-and-step-mutations) for more on this.\n\nThe condition object may also have a `failResponse` property with a response\nobject that will be used as the response from the step if the condition fails.\n\nFinally, the `condition` object may have a `break` property set to `true`, to\nsignal that the entire job should fail if the condition fails. If `break` is\nnot set, the step will just be skipped and the job flow continues.\n\n\u003e [!NOTE]\n\u003e The default behavior of breaking when a step returns an error, is implemented\n\u003e as a default pre-condition, so if you set your own `preconditions`, you\n\u003e will also have to handle breaking on error.\n\u003e\n\u003e This is not very intuitive, so we have introduced a\n\u003e `failOnErrorInPostconditions` flag that will change this to what will be the\n\u003e default in the next major version. [See more below.](#conditions-and-the-failOnErrorInPostconditions-flag)\n\n`postconditions` is also an array of condition objects, but this is used to\ndecide if the step should be regarded as having failed after its action or flow\nhas run. The condition pipelines are passed the same object as `postmutation`,\nbut _after_ `postmutation` has been applied. Just as for `preconditions`, the\n`break` property is `false` by default, so to stop the entire job, set it to\n`true`. An error will usually cause the job to fail even with `break: false`,\nbut the breaking may be handled by the `preconditions` on the next step, as\ndescribe above.\n\nPost-conditions specify what is required for a step to be succeessful, and\nsometimes you consider a certain error to be an indication of success. An\nexample may be when you're checking a cache and will only continue if a value\nis _not_ cached, meaning a `notfound` response status is a success in this\ncase. The condition pipeline for this should be straight forward, but as you\ncannot specify the response that will be used when the condition _passes_, you\nmay wonder what happens with the error response. When an error response causes\na post-condition to pass, Integreat will set the status of the response to\n`ok` and any `error` property will be changed to a `warning` property. Apart\nfrom that, the response will be unchanged.\n\nJobs may have `preconditions` too, and it will work in the same way as for a\nstep, determining whether a job will run of not. `break` will have no effect\nfor a condition on a job. Jobs may not have `postconditions`, but you can\nmutate the response with `postmutation` and accomplish the same.\n\n#### Conditions and the `failOnErrorInPostconditions` flag\n\nThe way `preconditions` controls whether the previous step will break on error,\nis not very intuitive, so we have introduced a feature flag to change this\nbehavior. The flag is called `failOnErrorInPostconditions`, and is set on the\n`flags` object in the definitions given to `Integreat.create()`). In the next\nmajor version of Integreat, this will be the default behavior, so it may\nbe a good idea to start using it now.\n\nWhen the flag is set to `true`, the default break on error behavior is set in\n`postconditions` instead. This means that you may set your own `preconditions`\nand still have the previous step break on error, the way you probably would\nexpect it to work. If you set your own `postconditions`, on the other hand,\nyou need to take care of how an error response from that step is handled, but\nthat is often why you would set your own `postconditions` anyway.\n\nAlso, `break` will be `true` by default for `postconditions` with this flag,\nand you need to set it to `break: false` if you have a post-condition and you\ndon't want it to stop the entire flow.\n\nFor `preconditions`, the `break` flag is `false` by default in all cases.\n\n### Dispatching several actions by iterating over an array\n\nSometimes you will want to dispatch several actions based on a data array, e.g.\nwhen you have an array of data items, but the relevant endpoint only accepts\none data item. This may be done with `iterate`, which is a special mutation\nthat is must return an array, and the job action will be dispatched once for\nevery item in this array. The item will be set as payload `data`.\n`premutation` may be used to modify the action before it is dispatched as\nusual, but note that the mutation is applied to every single action, after the\n`iterate`, so to speak.\n\nThis applies to both a job with an action and a step with an action in a flow.\n\nThe responses of each action are combined and set as a `response` object on the\nstep action (before the iteration), making an iterated step just like any\nother.\n\nWhen all actions are successful, the response will have status `ok`, and the\nresponse `data` will be an array of the data from each response in the order\nthey where run. When there are errors, Integreat will use any common status, if\npossible, otherwise the status will be `'error'`. The `error` string will\ninclude all the indidivual errors, separated by a pipe (`|`). The individual\nresponses will also be available on a `responses` property on the `response`\nobject.\n\nEvery single iterated action and response will also be available on the step id\nwith an index prefix, e.g. `getEntries` will have `getEntries_0`,\n`getEntries_1`, etc.\n\nBy default, the iterations are run in sequence, but you may run several in\nparallel by specifying the number of concurrent iterations on the\n`iterateConcurrency` property on the job step. The default is `1`.\n\n## Queues\n\nAs everything else in Integreat, a queue is also a service. You configure a\nqueue service, e.g.\n[`integreat-transporter-bull`](https://github.com/integreat-io/integreat-transporter-bull),\nand set its service id on the `queueService` property of the definition object\nyou give to `Integreat.create()`:\n\n```javascript\nimport bullQueue from `integreat-transporter-bull`\n\nconst services = [\n  {\n    id: 'queue',\n    transporter: 'bull',\n    // ...\n  }\n]\nconst transporters = {\n  bull: bullQueue\n}\n\nconst great = Integreat.create(\n  { services, queueService: 'queue' },\n  { transporters }\n)\n```\n\nTo queue an action instead of dispatching it right away, you set `queue: true`\non the `meta` object. If everything is set up correctly, Integreat will push\nthe action to the queue. When the action is later pulled from the queue, it\nwill be dispatched again, but without the `queue` property.\n\nYou may also set the meta `queue` property to a Unix timestamp, and if the\nqueue transporter supports it, it will be run at this time instead of being\nprocessed as soon as it is next in line in the queue.\n\nWhen a queue is not set up, a dispatched action with `queue: true` will just be\nrun right away as a normal action.\n\nYou may also use queues directly, by dispatching to it as a server and getting\nincoming actions from its `listen()` method. In that case, it's just as any\nother service with no need for any special handling.\n\n\u003e [!NOTE]\n\u003e Queueing actions are actually done through an action handler, but this\n\u003e handler is not available from outside Integreat.\n\n## Middleware\n\nIntegreat supports middleware, and there are two different middleware\n\"pipelines\":\n\n- The first one is run on dispatched actions. The action goes through the\n  middleware before the action handler takes over, but after the incoming\n  mutations have been run. Because of this, given that you have set up the\n  services with mutation and casting to schemas, you should always be dealing\n  with [typed data](#typed-data) in the middleware.\n- The action then passes through a second middleware \"pipeline\" just before it\n  is sent to the service. This happens _after_ all mutations have been run, so\n  you will be dealing with the data as it is sent to the service. Incoming\n  actions from a service also pass through this middleware on the way in,\n  _before_ it is mutated, giving you access to the data as it comes from the\n  service.\n\nTo set up a logger of what we recieve from and send to a service, you'll use\nthe second middleware \"pipeline\", while a logger of dispatched actions would be\nplaced in the first.\n\nWhen actions pass through middleware, they may modifiy the actions as\nappropriate, and you will have middleware that modifies (e.g. the\n[`completeIdent` middleware](#completeident-middleware)), and others that just\nmonitors what's coming through (e.g. a logger).\n\nMiddelware is passed to Integreat like this:\n\n```javascript\nconst great = Integreat.create(\n  defs,\n  resources,\n  [\n    // Dispatch middleware\n  ],\n  [\n    // Service middleware\n  ],\n)\n```\n\n### `completeIdent` middleware\n\nIf your access rules are based only on the information received from an\nauthenticator, you don't need the following. You will always get an id and\npotentially some other fields, like roles.\n\nBut when you need to match the ident id from the authenticator with user\ninformation held somewhere else, e.g. in a database, you need to configure a\nuser sch","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintegreat-io%2Fintegreat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fintegreat-io%2Fintegreat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintegreat-io%2Fintegreat/lists"}