0

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
  • 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?
    – Laiv
    May 8, 2023 at 6:55

1 Answer 1

2

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.

2
  • 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 a ValueObject inside an Aggregate root. 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. May 8, 2023 at 7:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.