Client IDs in Multitenant RESTful API URIs

Multitenant RESTful API

Wow, that title is a mouthful of acronyms. In my continuing journey with developing Collabinate, a Multitenant RESTful API for building activity streams, I have come to a place where I need to decide how to route my clients to the correct resource. There is a potential problem because resource identifiers can in theory be duplicated across tenants (user accounts). This means I need to decide whether or not to include some kind of identifier per tenant in the URIs for the service, or find some other way to have the code route the requests. There seem to be three schools of thought on this. I’ll break down all three with pros and cons. Let’s assume a tenant with an ID of acme.

1. Tenant? What Tenant?

The first option is to not change the URI at all, and use only metadata to identify the tenant, as such:

https://api.example.com/v1/some/path

The rationale behind this is that the tenant can be identified in headers, or via the authentication mechanism such as OAuth 2.0 with bearer tokens.

Pros

  • Keeps the URI “clean”. I’m not sure what this means regarding an API that will mostly be used by machines, but we’ll go with it.
  • Allows full URIs to be used in documentation or configuration without per-tenant modifications.
  • Allows a client that can generate the appropriate metadata to access the same resource across multiple tenants without changing the URI. In theory this could be good for administration tasks.
  • Avoids the need for dealing with the tenant in the path space in code, which keeps things simpler.

Cons

  • This can mess with cache-ability. Any proxy that caches the resource will need the ability to pass the metadata to the origin.
  • The ability to access more than one tenant at a time becomes difficult. For example, if you’re using the auth mechanism above, you need a separate token to access each tenant.
  • You need to deal with getting your tenant info in code in a separate and much different way than your resource information.

2. Subdomain Mania

The second option is to include the client ID in the authority (the DNS host name), like this:

https://acme.example.com/v1/some/path

This entails setting up a DNS entry for each tenant and handling the routing by reading the authority in code, or rerouting before it gets there.

Pros

  • Having their own domain name gives users a strong sense of ownership.
  • This is the easiest method to handle QoS issues – if the client needs special treatment just put them on a special server and point the DNS there.
  • Also keeps the tenant out of the path space like #1 above, which can keep the code simpler.
  • Cookies can be used at the “service” level (example.com) or at the tenant level (acme.example.com).
  • URL rewriting can be used on the server to combine the benefits of both this method (#2) and #3 below.

Cons

  • DNS provisioning and management can become a chore. This is especially true as your user base grows if parts of it are manual.
  • This usually requires wildcard SSL certificates, which aren’t as well supported as single host certs.
  • Depending on your network layout, cache-ability can still be an issue.
  • The ability to have users authorized across multiple tenants (e.g. super users or admins) can be a pain.

3. RESTafarian

The final option is to include the tenant ID as part of the URI path, like the following:

https://api.example.com/v1/acme/some/path

The idea here is that /some/path can actually represent different resources based on tenant, so the URI should be as explicit as possible in identifying those resources. This is achieved by including all information needed for resource identification in the URI, specifically the tenant ID. Do note that for security reasons, you’ll likely want to expose a surrogate or slug instead of an actual database ID for the tenant.

Pros

  • Clarity. Ain’t nothin’ like having it spelled out for you.
  • You can route based on tenant in the URI right from the load balancer.
  • You can do special mapping based on special tenant IDs, e.g default to match the tenant for the current authenticated tenant or admin for super users.
  • The code can remain FQDN agnostic, at least for tenant identification reasons.

Cons

  • URIs may be longer. Boo-hoo.
  • URIs are different for every tenant. This might be a pain with documentation or configuration.
  • Tools that access multiple tenants must utilize different URIs even for resources that are conceptually the same across tenants.

Summary

After weighing these, it seems best to start with option 3, then move to option 2 with routes to the way you had option 3 set up if and when needed. Option 1 seems painful for services that are built to be multitenant from the start, but if you have good arguments for it, please let me know in the comments. Happy API coding!

The following two tabs change content below.
Hi! My name is Jack and I would love to connect with you. I am a family man, an entrepreneur, a software developer, an API lover, and a clean energy pusher. I love meeting new people, and I'm here to help! Connect with me on Twitter, LinkedIn, or Google+.

Latest posts by Jack Jones (see all)

Tags:

3 Responses to “Client IDs in Multitenant RESTful API URIs”

  1. Josh Gough 2014-12-08 at 11:25 am #

    Thanks Jack,
    This was a helpful summation. We are using EventStore as a technology in something similar we’re building called CommitStream that integrated with VersionOne and source control tools: https://github.com/openAgile/CommitStream.Web.

    Josh

    • Jack Jones 2014-12-09 at 11:25 pm #

      Hi Josh, glad you found it helpful. I took a quick peek at CommitStream, but I couldn’t figure out what it does just from the readme and a quick browse. Is it an feed system for source control activity streams?

  2. Félix Delval 2016-10-09 at 8:31 am #

    Good summary.

    In this matter you should also take into account wether the identifier is unique to your resource.

    So yes, you wouldn’t be able to cache https://api.example.com/v1/some/path because it might have different value for each client.

    But https://api.example.com/v1/some/path/unique-id would have the same content for each clients. The only difference would be wether they have permission to access it or not.

    To go with the 3rd way, I would expect that would use unique-id per client for your resources. Then having the client in the URI is the only to access unique resource. By example both client have a product “product1”. So the URI would be:

    https://api.example.com/v1/client/some/path/product1
    https://api.example.com/v1/acme/some/path/product1

Leave a Reply