Batch Request in Business Central APIs

 When calling Business Central APIs you do one operation at a time. For example, you can only insert or modify one customer, or create one sales invoice. With deep inserts, it is possible to create header and lines together, and then you can create multiple lines. But that’s only possible on the line records, you still create one header at a time. With a batch request, it is possible to combine multiple operations in one request. The batch request is submitted as a single HTTP POST request to the $batch endpoint and the body contains a set of operations. From an optimization point of view, batching minimizes the number of requests from an API consumer because you combine multiple requests into one.

Batch URL

The $batch endpoint is available on all API endpoints. For the Business Central SaaS environment you can use this URL:

https://{{baseurl}}/api/v2.0/$batch

Everything I describe here also works for custom APIs. The URL will then look like:

https://{{baseurl}}/api/[publisher]/[group]/[version]/$batch

Request headers

Because the request body will be JSON the Content-Type header of the request must be set to appication/json. If the response should be JSON as well (and you want that!), then the Accept header must also be set to application/json. If you leave the Accept header out, then the response will be multipart/mixed. Here is the basic structure of the request, without the body. I leave out the Authorization header, but you need to add that obviously. 🙂


POST {{baseurl}}/api/v2.0/$batch Content-Type: application/json Accept: application/json

The requests array must contain one ore more operations, and each of them must contains an id, the method and a URL and optionally also headers and a body. Here is an example of an operation to insert a single journal line.

{

"method": "POST",

"id": "r1",

"url": "companies({{companyId}})/journals({{journalId}})/journalLines",

"headers": {

"Content-Type": "application/json"

},

"body": {

        "accountId": "{{accountId}}",

        "postingDate": "2020-10-20",

"documentNumber": "SALARY2020-10",

"amount": -3250,

"description": "Salary to Bob"

}

}



Let’s take the operation above, to insert a single journal line and compose a batch request that inserts multiple journal lines in one go. The request body looks like:

{

  "requests": [

    {

      "method": "POST",

      "id": "r1",

      "url": "companies({{companyId}})/journals({{journalId}})/journalLines",

      "headers": {

        "Content-Type": "application/json"

      },

      "body": {

        "accountId": "{{accountId}}",

        "postingDate": "2020-10-20",

        "documentNumber": "SALARY2020-12",

        "amount": -3250,

        "description": "Salary to Bob"

      }

    },

    {

      "method": "POST",

      "id": "r2",

      "url": "companies({{companyId}})/journals({{journalId}})/journalLines",

      "headers": {

        "Content-Type": "application/json"

      },

      "body": {

        "accountId": "{{accountId}}",

        "postingDate": "2020-10-20",

        "documentNumber": "SALARY2020-12",

        "amount": -3500,

        "description": "Salary to John"

      }

    },

    {

      "method": "POST",

      "id": "r3",

      "url": "companies({{companyId}})/journals({{journalId}})/journalLines",

      "headers": {

        "Content-Type": "application/json"

      },

      "body": {

        "accountId": "{{accountId2}}",

        "postingDate": "2020-10-20",

        "documentNumber": "SALARY2020-12",

        "amount": 6750,

        "description": "Salaries December 2020"

      }

    }

  ]

}

As you can see, each operation has a corresponding result in the response, identified by the id. You should always use the id of the individual operation to find the corresponding result. Don’t assume that the results will always be in the same order as the request! They may seem to be in the same order, but the OData standard describes that the results can be in any order.

Each operation result has a status, which is the HTTP status that you would normally get for a single request. It includes the response headers and response body of the individual operation. The response of the batch request itself will always have status 200 if the server was able to read the batch request. Even if the batch request has operations that couldn’t be processed because of an error condition, the batch response status will still be 200. So don’t only look at the response status of the batch request, you need to read the response body to find out the results of each operation. Only when the batch request body is malformed, or you are not authorized, then the whole batch request will fail and you will get a status 500 (malformed batch request) or status 401 (not authorized).

So far it’s really simple, isn’t it? In the next blog posts in this series, I will cover topics like:

  • What happens if an error occurs in one of the requests
  • Process a batch as one big transaction
  • Combine multiple operations types, like POST and GET
  • Define the order of operations
  • Reduce the size of the response payload



No comments:

Post a Comment