Many resources in the REST API I'm designing have a graphical representation. My fist idea was to include the image URL into the resources:
Resource1:
type: object
properties:
image:
type: string
format: uri
Same for Resource2 etc.
The problem is it's not always the same URL. Web clients want an SVG, while Android clients want a PNG, possibly in different sizes, and, additionally, a version for a light or a dark mode.
So the next idea (Idea 1) is to include all possible URLs:
Resource1:
type: object
properties:
images:
smallPng:
type: string
format: uri
svg:
type: string
format: uri
Would work, but it's like 10 URLs in each resource, so if you get a collection of 100 resources on a mobile, it would increase the traffic considerably.
Idea 2 would be that clients would specify the format they want in some way. So we are back to the model schema with one URL, but:
- We have additional parameters on all resource URLs, like
GET /resources1?format=xxx&size=yyy&mode=dark
, same for single GET with ID, same for resource 2-10. - What if resource1 contains resource2, and a client would want different sizes for the images?
My last idea (Idea 3) is to create an additional endpoint like GET images/{id}?format=xxx&size=yyy&mode=dark
, put a single URL into each resource which will contain only the ID-part, like:
{
image: "images/asd123"
}
Clients won't be able just to use this URL as-is, but will have to do some "manual work" and compliment this URL with query parameters themselves.
I currently incline towards Idea 3, although I imagine client developers would prefer some kind of URL they just can use and bind directly to their graphical controls.
Any more ideas? Any best practices to follow? Which solution looks most viable to you?
1 Answer 1
Idea 2 would be that clients would specify the format they want in some way.
Clients already do this in the form of the Accept
Request header. If I request a resource and in that request I ask that you give it back to me as application/json
, I would expect the response to be JSON encoded. Similar logic applies if a client requests text/html
or application/xml
, respectively. The server of course is free to decide what happens if a client requests something that it doesn't support, either throwing some form of 4xx
error or, in my opinion a better idea, gracefully fall back with some form of default representation for the resource depending on the circumstances since having an Accept
header at all is not required by HTTP. An exception might be if an image representation of a resource is requested for which a server only has textual representations available, in that case a response code of 415 Unsupported Media Type
could be appropriate.
Note that the standard states that the contents of the Accept
header can represent a list, so clients can be free to request more than one different representation at the same time. For instance, if a client requests something like: Accept: image/png, image/jpeg
and the server doesn't support image/png
for a particular resource but does have an image/jpeg
representation, I wouldn't expect to see JSON as a response.
Wildcards are also possible, so a client could just request any kind of image with image/*
and, so long as you give it an image in response it should be satisfactory. How far you go in supporting every aspect of the standard though generally depends on how open and available your API will be coupled with how generic a client you wish to support. In most cases I would argue supporting wildcards would be more trouble than it is worth, but your mileage may vary.
If you have multiple different image/png
representations for a single resource, specifying how to disambiguate within the URL I think makes sense. The standard does say that additional information within the Accept
header can be used to request a specific quality of the response, but using it to distinguish between a smallPng
and bigPng
I think would be abusing the standard. An example request using query parameters I can imagine would look like this:
GET /some-resource?size=500x500&mode=dark
Accept: image/png
You can of course choose to standardize your sizes to be big
, small
, or extraSmallPng
, but you may be boxing yourself into a corner when a new client exists in the future which has a completely different design or physical size itself, or even if the current designer gets a new job and a new one hired with a different idea of what big
and small
mean. There may not be a client today that generally runs on large displays, but tomorrow there may be, and that client might need superBig
and evenSuperBigger
image representations. This may or may not be an actual concern given your problem domain, but I feel it's appropriate to point out.
One final note: your Idea 3 could make more sense depending on context. If your application is built mainly to serve media, then having images or videos as independent top-level resources could wind up reducing complexity in your overall application. In fact, in many cases the approaches can be combined given that some images could in fact be related to only stylistic/design issues and others could be considered to be data all on their own. An example for this could be products that include a list of display images all of the same dimensions. I can easily need to request information about the list of images in JSON format, for instance if I just want to know how many associated images there are and not actually display them.
-
Makes sense what you say, thank you for your answer. I cannot use however the "accept" header, because the content is always application/json, the image is just a URL inside the returned JSON resource, pointing to its graphical representation (e.g. an image of a cat or a dog for
/animals
endpoint). It's hardly a resource in its own right, more like aValueObject
inside anAggregate root
.Maxim Zabolotskikh– Maxim Zabolotskikh05/08/2023 06:55:47Commented May 8, 2023 at 6:55 -
@MaximZabolotskikh In that case I would lean more towards your option 3 also. If they're important enough that there's lists of them and clients will want to request a list of them, treat them like a top-level resource like any other.Jeff Lambert– Jeff Lambert05/08/2023 07:32:07Commented May 8, 2023 at 7:32
Would work, but it's like 10 URLs in each resource, so if you get a collection of 100 resources on a mobile, it will increase the traffic considerably.
have you measured the increase? Is it high enough for you to add additional complexity to the API? Wouldn't be simpler to limit the number of resources per request?