Make it possible for the client to show organization icons (logo) and, at some point in the near future, organization names in the drop-down list of groups available to a user at a particular URI.
This discussion centers around how to make that possible from the API-endpoint perspective, based on what is in place now and what we know now.
An endpoint (GET /api/groups
) that returns an appropriately-sorted list of groups pertinent to the current URI, but without any notion of organization metadata.
We do not (yet) have API endpoints for individual group resources or any endpoints for organizations.
We have a basic table and model for organization
. organization
s have a one-to-many relationship with group
s. At some point in the near future, every group
will be required to have a relation to an organization
, defaulting to the default organization hypothesis
.
At present, logo images are stored directly as markup in the database.
There is a groups
service in the client that is a dependency for several components. The client solidly thinks of groups as, well, a list or collection of groups.
Though the client now needs to think of groups in the context of organizations, I would assert that the client is not asking the question “What are the valid organizations for this request?” At the end of the day, the client is still trying to get at the appropriate list of groups for the current request (user + URI).
Even if the former (“Gimme my organizations”) were accurate, there’s some challenges with the current state of organization
as we have it in its youthful modeling: I believe that organizations aren’t (yet) well enough defined to stand on their own as a first-class entity. We’re not quite certain yet where we’re going with them.
To illustrate, the only way at present to determine what is an “appropriate list of organizations” to give to the client at any given time is through the groups’ relationships with organizations. Organizations themselves are “dumb” as of yet: though they are associated with an authority, they don’t have any scoping.
Also, though we certainly want to keep business logic out of clients, I want to avoid us doing backflips in our API design to solve a UI-level problem.
In this approach, the response from the groups endpoint would be restructured to put group
objects under their parent organization
objects.
- Would provide the client with a response that exactly mirrors what it wants to do with it
- Would keep any sorting or grouping out of the client
- Violates RESTful principles as this is an endpoint about groups
- Breaks existing
groups
endpoint - Would replace an endpoint structure that may well have future use
In this approach, new endpoints would be created for retrieving an individual organization resource or collections of organizations. Groups returned via the groups endpoint would contain only an ID reference to their associated organization.
- Keeps complete representations of organization data—which may be unnecessary for some or most requests to this endpoint—out of the response.
- Probably endpoints we’ll want in the future anyway.
- Will generate extra HTTP requests
- There is no meaningful way to query for organizations yet—the correct/valid/accessible organizations can only be determined through group relationships at present. Thus it could be tough to design this endpoint now.
- Thus there is some fragility with keeping organizations and groups in parity at present…
- Would require more work to support in the client—the client would need to iterate over groups and make the right request(s) for additional organization information
In this approach, an endpoint could be added that expresses that we’re actually dealing with a hybrid resource collection here: the combination of groups and organizations, e.g. organization_groups
. The response would be structured by organizations but all groups would be unfurled within their organization objects.
There are some examples of this in certain production APIs, though it’s not overly common (possibly because it points at an API-design smell).
- Avoids pollution of
groups
ororganizations
endpoint(s) with relational data. - Follows certain established RESTful patterns
- Still convoluted
- Requires rework in the client that might be confusing
- Might back us into a weird corner
In this approach, an expand
parameter could be provided to certain endpoints to allow expansion of sub-resources within them. e.g. GET /api/groups?expand=organizations
.
- Allows clients to designate how much data they get back
- Reduces HTTP requests
- Simpler implementation in client as it builds on existing
groups
- Backwards-compatible
- Requires
list-groups
component to do some traversal over the response to format it as needed in the UI
At present, I am leaning toward the resource-expansion approach. This would adapt the existing groups
endpoint to provide the ability to expand
sub-resource organization
objects (e.g. GET /api/groups?expand=organizations
)
This is based on the following arguments in favor:
Even though the client will need to add a function to traverse and organize the groups response in the list-groups
component, I believe that will be a relatively constrained change. Unlike some of the other approaches, it won’t require the creation of a new service or the total restructuring of the groups
service in the client.
Initially it won’t require the creation of a new endpoint, but the adaptation of an existing one.
This is up for debate still, but I believe at the core that the client is still asking the question “what groups apply here?” not “what organizations are there?” (We also don’t have a way to answer “What organizations are there?” yet).
The response from the groups endpoint is used in other places in the client (to reference group objects in general) and is more generic than the display in a drop-down.
If the preceding assertion proves false—if indeed, as we evolve, the client really needs a list of organizations, not groups, we can opt to add additional endpoints (e.g. GET /api/organizations
) with or without resource expansion and add services to the client as well.
Put another way, I believe this won’t design us into a corner. It won’t hurt to have an expandable groups
response and the investment into this approach is a smaller amount of work: we can change our minds.
There are a good amount of examples of large-scale APIs following this resource-expansion pattern, including Netflix (Netflix is a dead API, whoops), GitHub, Jira, Stripe, Atlassian, etc., in one way or another. It is an established pattern, in other words.
It also leaves the groups
endpoint (non-expanded and expanded both) for use for other purposes.
Choosing resource expansion is a decision that puts emphasis on reducing requests and simplifying client logic—coarse-grained, that is.
If later, as we scale, we realize that optimizing for payload or other things is more vital, we can swap over to not expanding resources and instead making additional requests against (to-be-built) organizations
(or other) endpoints.
We’ve discussed before how important it is to keep business logic out of clients (ours or others’). However, at the end of the day here, I believe that structuring the response in a way to match the client’s groups drop-down menu could be short-sighted and might be bending the design to an over-specific application of it. That is, I believe it is display logic at this time, not business logic. This may change as time goes on, but refer to some preceding arguments for ways we can address that.
I do think that we may well end up wanting to add organizations
endpoints in the future (maybe even the near future). I believe the proposed approach shouldn’t get in the way of that.