Conditonally return different REST API response object for same endpoint
Asked Answered
H

2

8

The following scenario does not follow RESTful standards and would be keen to know how best to structure my API to achieve the same goal.

For a given GET request against a resource, e.g. GET /api/person/1, if the principle contains a claim I would like to return additional properties.

E.g.

GET /api/person/1 (Without IsAdmin claim)
{
  name: "Buck Rogers",
  dateOfBirth: 2000-01-01T00:00:00.000
}
GET /api/person/1 (With IsAdmin claim)
{
  name: "Buck Rogers",
  dateOfBirth: 2000-01-01T00:00:00.000,
  adminNote: "Something private"
}

So I'd conditionally be returning two different DTOs for the same resource request, which isn't allowed.

How can I achieve this in a RESTful way?

Update:

It was suggested I could define the adminNote property and NULL it based on the condition. How would I deal with the case where there might be multiple conditionals that determine which properties are included? E.g.

GET /api/person/1 (With IsModerator claim)
{
  name: "Buck Rogers",
  dateOfBirth: 2000-01-01T00:00:00.000,
  moderatorNote: "Something else private"
}

I would be keen to avoid adding extra properties that will only ever not be null in one particular case.

Howell answered 29/7, 2020 at 15:49 Comment(5)
I'm not sure this goes against REST: you're still producing the same resource (a "person"), just the representation of it changes depending on the caller. If documenting this API, you could simply flag adminNote as "optional, valued only for admins" or something along those lines.Wilser
I agree with @sp00m. Could you provide us with the information where you found it is going against REST principles?Sumpter
I couldn't find anything absolute that gave an answer either way. In my mind a RESTful API should be deterministic given an identical request. I appreciate what you're saying, that both responses are logically a Person resource, but I'd be returning a different DTO type based on the condition. I did read that OpenAPI/swagger doesn't support this.Howell
Yes, OAS 3 does support that FYI, thanks to anyOf for instance, see swagger.io/docs/specification/data-models/oneof-anyof-allof-not. Whether you use two distinct DTOs or a single one with optional fields though is a very technical concern IMHO, I'm not sure REST goes that deep in the implementation details. I still see a single resource personally. I get your point though, interested to see other opinions about that :)Wilser
But it's quite obvious the path /api/person/1 suits both cases, meaning we're actually representing the same resource. If this really bothers you, you could still have a query param view that would take minimal or detailed for instance, but then it's more job for the callers, plus again, it's a very "technical param" (maybe more the job of a header, e.g. Accept?). I wouldn't bother too much though personally, a well written documentation should do the job :)Wilser
A
0

you can create a Person resource with all the three fields and when the IsAdmin is false or without claim , you could set adminNotes to null.And to not return that property you can use @JsonInclude(Include.NON_NULL) (its for java, something like this might be available in asp.net) as well.So you don't need to create two separate entities or DTO's.

Adoptive answered 29/7, 2020 at 16:0 Comment(2)
Thanks for the idea, however I'd prefer to explicitly define the response objects. My question is obviously a very simplistic example, but say I had 10 conditionals, all of which resulted in different properties being included, I don't think this approach would be very tidy.Howell
@ChrisPickford just came across this as I had a similar requirement to achieve. I know it has been quite sometime, but do you remember what approach you had taken? Thanks.Gomulka
U
0

I think you've got the right idea here based on your update.

It's perfectly fine to define a schema that has a set of fields that are only populated given certain circumstances. One example is the "with admin" claim, but another is the idea of a user-provided field mask or views (see AIP-161 for an example of this).

If you're concerned about a lot of these piling up, you could always group the field to be adminInfo that contains all the admin fields, but that's probably not necessary...

Uranology answered 4/10 at 5:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.