Search code examples
apiasp.net-coreapi-design

Call an HTTP POST API with Flatten or not flatten body?


I am working on an API which can return a list of Users:

"users": [
  {
    "userId": 1,
    "name": "John Smith"
    "address": {
      "countryCode": "gb",
      "street": "Orford Street 20",
      "locality": "London"
    }
    "skills": [
      { "skillId": 1, "name": "design" },
      { "skillId": 2, "name": "logotype" }
    ]
  }
]

Consider I need to create a User with address and skills.

Which body format is more commonly used? Something kind of flatten:

"users": [
  {
    "name": "John Smith"
    "addressCountryCode": "gb",
    "addressStreet": "Orford Street 20",
    "addressLocality": "London"
    "skills": [ "design", "logotype" ]
  }
]

Maybe respecting the same format as the GET response:

"users": [
  {
    "name": "John Smith"
    "address": {
      "countryCode": "gb",
      "street": "Orford Street 20",
      "locality": "London"
    }
    "skills": [
      { "name": "design" },
      { "name": "logotype" }
    ]
  }
]

Or maybe creating the User in Steps:

  1. Create user;
  2. Add address to user;
  3. Add skills to user.

I believe that, even if the API has the steps endpoints, one that accepts a User with all data would prevent multiple Post queries to create a single user.

Which approach is more commonly used?

Or maybe even use another one?


Solution

  • If you want to follow the Rest Design, it's better to have an endpoint for each operation, So the 3rd approach is the one you should use. This way has some advantages:

    1. Imagine you have an existing user and you want to assign an address to him. If you follow the first or second approach, now you need to create another endpoint which gets a UserId and Address, to add it to the user.

      But if you've followed the 3rd approach there's already an endpoint which does this and you can reuse that for this situation.

    2. In the first and approach, it's not clear that what a single action method does. There's a giant method which creates a user, creates an address and assign that to the user, creates some skills and assign them to the user and ...

      In the 3rd approach, it's clear that what an endpoint does. One endpoint creates a user, one creates an address for it, one creates skills for it and so on. There would be only one reason to change one of these method bodies.

      You observed the Single Responsibility Principle here.

    One question that might occur with the third approach is that in this way, we'll have more API calls to create a user and assign an address to it. At least there would be 2 API calls!

    The answer is you can use Batch to call multiple endpoints at once with just a single API call. There are some good implementations of Batch requests like OData Batch that you can make use of them.