The Future of the Asana Client Libraries

Hello everyone!

We are working on upgrading the tooling of our client libraries. We have run into a decision point where we would like your input!

For typed languages, we’re discussing how the models should look and interact with request functions. The main issue is that it’s difficult to have a single Model represent both the request object & the response object, when they have different properties and/or un-returned properties.

Asana’s API has asymmetric requests & responses. Take the Task object for example:

POST /tasks

{
  name: “Example”,
  assignee: “1234567890”,
  workspace: “123457890"
}

GET /tasks/1234567890

{
  gid: “1234567890”,
  name: “Example”,
  assignee: {
    gid: “1234567890”,
    name: “Ross Grambo”
  },
  workspace: {
    gid: “1234567890”,
    name: “Main Workspace”
  }
}

For languages that are not using types, this is not an issue. However, when types are introduced, what is the type of assignee? Is it Option<String> or Option<User>?

Additionally, does assignee: null mean that the assignee field was not returned in the response or does it mean that the assignee is unassigned?

Our API allows for a lot of flexibility, which makes it hard to capture in a typed language.

Here’s some approaches we’ve discussed:

We have Request & Response models

  1. Developer experience takes a hit, as you’re now dealing with different types depending on the context.
  2. The response models would probably use something like Option<Option<User>> to represent
    assignee being unassigned vs not returned.

Requests & Responses use nested Dictionaries (or Gson)

  1. Developer experience takes a hit, as developers will need to convert to and from models. The model will only represent 1 of the forms (assignee probably being Option)
  2. Assuming developers are converting to a Model, they are expected to look into the Dictionary to see if assignee was returned or not, and optionally convert Dictionary.assignee to a User object

We choose 1 model, but do magic under the hood to have it work for requests and responses

  1. The developer experience might be unintuitive (sometimes an assignee is a user, sometimes it’s just an id, and you’re not in charge of it).
  2. This approach would require more maintenance and one-off handling for things like custom fields.
  3. We might still need the Option<Option<User>> format. We might be able to due without it the first Option<> layer, if we include the raw_response on models.

Does anyone have a preference? Or an idea? Or agree with one approach vs another? I currently prefer a single model, with a raw_response field, where we try and handle the asymmetry gracefully.

Note: we will most likely move to the new tooling and keep the libraries the same until we’ve decided on a consistent methodology & justified the breaking changes it makes

cc: @Phil_Seeman & @Bastien_Siebman

Thanks all,
Ross

3 Likes

I’d vote for We have Request & Response models

I’ve used other APIs where there are different models for requests and responses, so I don’t find it unusual and have never found it to be cumbersome. It feels to me like it follows the Single Responsibility Principle.

Additional thoughts:

Requests & Responses use nested Dictionaries (or Gson)
This would be my second choice, but I think it’s more complex to understand and implement than the separate-model approach.

We choose 1 model, but do magic under the hood to have it work for requests and responses
My least favorite (sorry, Ross!).
I really don’t like this:

The developer experience might be unintuitive (sometimes an assignee is a user, sometimes it’s just an id, and you’re not in charge of it).

This approach feels to me like it violates the SRP.

6 Likes

Hi,

I agree with @Phil_Seeman and I prefer when API sticks to the same rules everywhere.
It’s much easier for dev to work with API from IDE when you can see what actually is returned. Like this, you even don’t need to go to help center to know how to get the field you need.

So, if User class is going to have only 1 field gid it sounds absolutely good for me.

What if you decide to add another field in 1 year? In case of plain string you will have to change this in multiple places where this plain string is used and in case of a model you will just add a new field. The same is applicable for auto-docs, refactoring, syntax highlighting, auto-suggestions e.t.c.

Plain JSON might be the solution as well but then I don’t see much difference from using API without the library (you can make direct requests and get plain JSON without the need of installing additional dependencies).

Regarding the null and the value is not returned it would be great if you could just skip the fields that are not returned because they are not selected by opt_fields. I was quite disappointed when I started using API and I had null values returned for the fields. I thought that something’s wrong as far as values are null. After some time of experimenting it turned out that you just need to ask for additional fields and they will get theirs values. It’s not very dev friendly.

Thank you :wink:

2 Likes

Good point, I agree with this suggestion - it can be confusing.

1 Like