15 ms·
Use links not keys to represent relationships in APIs
- the_arun 7y agoIn micro services world this makes perfect sense. In the legacy land - this is slightly tricky as dependent application (where our foreign key points to) may or may not be in services world. But I get the idea.
- the_arun 7y agoI don't know what made my above comment to get negative votes (-3). I was sharing an honest opinion here.
- currriuosly 7y agoThis is what Django REST framework had done right for years.
- rich-tea 7y agoIndeed. Hyperlinked serialisers are built in and super easy to use. And it makes so much sense when you use DRF's API browser too.
- raquo 7y agoI never had any uncertainty regarding where I can use a given entity id in a well designed API. Fix the naming and organization of your API if that's a problem for your users. Conversely, I often need to log or store API-provided entity ids on my side, and having to parse it out of a URL or store irrelevant URL bytes in my own database would be really annoying. You're not going to avoid the need to compile entity URLs on the client side either, unless you only make requests to entities returned by the API, which would be a weird constraint to design client code around. I really don't see the point to any of this.
- underwater 7y agoI would never feel comfortable `fetch`-ing the URI provided by the API without validation. That sounds like a security issue waiting to happen.
- fagnerbrack 7y agoThe browser already does it with the anchor tag and the user decides to navigate or not, the difference in an API is that it's a machine driving. What's the security issue you're talking about?
- cyphar 7y agoSSRF is the most obvious (potential) issue.
- pmontra 7y agoDoes an API returning only relative URLs address your concern? The validation would be intrinsic in the client side concatenation of the server address with the relative URI.
- twblalock 7y agoIf you save the relative URL in your database and then the API changes its URL schema, you will need to migrate everything you stored to the new schema. If all you stored was the ID, all you would need to change is the logic in your API client which accepts the ID and constructs the URL for it.
- chii 7y agoa change that necessitate a URL schema change would be just as far reaching had the system been designed with using IDs. For example, if the entity ID changes from being an integer, to being a GUID, you'd still have to write code to update your schema (presumably, from an int column to a GUID/string column).
- 7y ago
- geezerjay 7y agoWhy did the author of this blog post decided to pass web links in resources and completely ignored standard practices such as RFC 8288 which employs the Link HTTP header? https://tools.ietf.org/html/rfc8288 https://tools.ietf.org/html/rfc8288 Additionally, compact URIs (CURIES) are also widely used in this context. https://www.w3.org/TR/2010/NOTE-curie-20101216/ https://www.w3.org/TR/2010/NOTE-curie-20101216/ I feel that the author tried to reinvent HATEOAS but skipped a cursory bibliographical review and jumped right into reinventing the wheel, and one which has already been reinvented multiple times (HAL, JSON-LD, etc...)
- peteretep 7y ago> completely ignored standard practices Google NIH syndrome
- geezerjay 7y agoIt's ok if the author tried to come up with solutions for the problems he feels he has, but it is a complete waste of time -- his own an of those who spend any time reading this sort of blog posts -- if the post fails to take into account standard practices that are being employed for years. As another example of how the author failed to take into account basic standard practices, the author asserts that there is no standard for HTTP headers that specify API versions, but this baseless assertion requires ignoring the fact that media type versioning does exist and has been used for ages. The author even realizes that a solution based on the Accept header, which he prefers to invent a Accept-Version header, would fit his requirements. Ok, so why doesn't he just follow what has been standard for quite a few years and simply state the document version in the media type, which already is supported by any content-based routing scheme and is cached quite nicely?
- mpnally 7y agoYour suggestion for using content_type for versioning makes sense if you believe it makes sense to invent a new media type for every entity in your domain model (customer, invoice, ...). I don't think this is a good practice. I always stick to the standard media types (application/json, text/html, ...)
- sbr464 7y agoI don’t mind using a link, but I’d prefer to have both the exact id and the link to avoid having to parse a link in an unreliable way to get the actual id. It’s interesting how GraphQL changes the base point of the article concerning documentation/ease of api use. In our GraphQL resolvers we typically add two fields, thing_id which resolves to the id string, and thing which resolves an object that you can drill into as desired. I was starting to see a lot of GraphQL APIs only add “thing”, which meant you had to do a lot of queries like below just to get the id. thing { id }
- rileymat2 7y agoI suspect the link is the id, as in uniform resource identifier. So parsing would be a mistake. At least on the client side.
- mypalmike 7y agoWhen you are orchestrating an interaction between several services where they are all using a shared identifier somewhere in their API, you need to extract that identifier if it's embedded in a URI. So tonight we're going to regex like it's 1999.
- smt88 7y agoMost modern ecosystems have fantastic URL parsing that does not rely on regex and should not be written again by hand.
- tuukkah 7y agoThe identifier is not embedded in a URI, the identifier is a URI: instead of an integer id 1234 or a string id "1234", you can use a string id "http://example.com/id/1234" http://example.com/id/1234". Typically, one service is responsible for creating the id but after that, it can be used to refer to the same entity in any number of services. EDIT: What makes it better than a free-format string id is that every person and tool knows how to de-reference it / look it up.
- siscia 7y agoI just don't understand why mixing two different concepts at two different abstraction levels only for some, apparent, simplicity. On one level we got ID unique identifier of a resource, on another level we have URL, how to get a specific resources. They are just different things that shouldn't be mixed. What if tomorrow I want to get the same resource via graphql? Or in a message bus?
- frosted-flakes 7y agoWhy can't the ID also be a link? Serious question. I don't see any real downside to it. If the ID is also a link, it is guaranteed to be globally unique (like a Relay Node ID in GraphQL). If you want to get the same resource via GraphQL, just use the same URL-ID. In fact, the author mentioned the possibility of base64-ing the link to prevent clients from relying on its structure, which is also a common pattern with IDs in GraphQL.
- siscia 7y agoIs not that they can't, it is just that link are not designed to be ID. The ID is the maximum cardinality of an entity (informally the smallest set of values of such entity necessary to uniquely identify it), the link is how to find the entity. What happen if you want to deprecated your API? There are other way to share the link of a resource,like use headers. Then again, from a strictly practical point of view in 99.9% of the case it won't change anything, but: 1. Eventually there will be cases where it does matter, and then it will be an huge mess. 2. Why shut yourself a door only to don't read an header and obtain exactly the same information?
- kd5bjo 7y agoThe original idea was that you had URLs that told you where to find a thing, and URNs that identified a particular thing regardless of where it appeared. These used a unified schema (called URIs) because when you’re telling somebody else about a thing, you want to be able to refer to it by location (“the red building in the next block”) or by identity (“the main post office”). Presumably, there would be services that could take a name and tell you where to find it; not a bad assumption as every library in the world has such a system for its own collection. While this looks good on paper, in practice URLs were relatively stable in the early days of the internet and so they turned into de facto names before lots of effort was put into making URNs work. Now, we’re struggling with the issues they originally foresaw, but weren’t able to follow through with a working implementation. (For more details, see https://danielmiessler.com/study/url-uri/ https://danielmiessler.com/study/url-uri/ where I got much of this information)
- abetlen 7y agoI think the issue brought up in this blog post pales in comparison to the two biggest problems faced when working with REST APIs: querying for nested data, and the limitations of CRUD interfaces to model complex behavior.
- treve 7y agoI've been building REST apis for 13 years and never had either of these problems. I might be reading too much into this, but it sounds like you enjoy GraphQL or RPC-like apis and have trouble mapping their respective concepts 1:1 to REST. You shouldn't. These architectures aren't equivalent alternatives. Pick the right tools for the job, and use the right tool appriately.
- abetlen 7y agoNo I work mostly with REST apis and I find that api consumers don't really care about having nice relative links or references to self. If anything the popularity of GraphQL indicates that they want more flexibility than is usually available through REST and posts like this are the ones really addressing non-problems.
- treve 7y ago> If anything the popularity of GraphQL indicates that they want more flexibility than is usually available through REST I absolutely agree. GraphQL clearly addresses a pain point, or a gap. I just don't think it should be seen as a replacement. Pick the right tool for the job.
- wvenable 7y agoThe caveats section of this article is longer than the content -- it makes a better case for not using links as keys.
- WanderingWaves 7y agoI think this takes an overly-simplitisic view of APIs. Going by the primary example in the article, by representing a pet's owner as a link instead of an id, they're basically discounting the idea that there may be separate endpoints that take in an owner id. For example, if there was an endpoint that let you get the invoices by customer, you would still need to understand the templates for that endpoint. More fundamentally, I think it's trying to solve a smaller problem in the face of a much bigger one, you still need to know what the response of any given endpoint is going to be. Just because they've passed me a link, doesn't me I don't need documentation on what endpoint that link points to. I still need to know that the owner is a link to the people endpoint so I can properly parse that result. That in turn requires just as much documentation (IMO) to describe the relationships as it would to properly document your URI templates. Obviously, the primary reason to use links over ids is to give the developers of the API more control over changing things like routes and Ids and whatnot, but I feel like it is a bit disingenuous to make it out to be a much better user experience or something, since it really isn't.
- rich-tea 7y agoThere shouldn't be separate endpoints that take an owner's ID. That's bad design. The owner endpoint should contain a list of invoices, ie. links to the invoice endpoint.
- viraptor 7y agoBut the invoice endpoint is likely going to be something like "/customer/(id)/invoice/...". So what did we gain from getting it from the customer description first? (vs getting the customer id from the response and the link pattern from the docs)
- rich-tea 7y agoYou gained the ability to find the invoice. There is no need to have customer id in the invoice url. Just /invoice/invoice_id is enough.
- chvid 7y agoHypermedia As The Engine Of Application State (HATEOAS) https://en.wikipedia.org/wiki/HATEOAS https://en.wikipedia.org/wiki/HATEOAS The idea has been around for a while; I personally don't think it is a good idea. There is even a content type (or two) for it: application/hal+json and application/hal+xml. http://stateless.co/hal_specification.html http://stateless.co/hal_specification.html
- niftich 7y agoThe article's recommendations don't achieve HATEOAS, because even though the foreign-key IDs are replaced with URLs, they're not actually links because they don't specify an explicit relationship. Instead, the relationship between the response document and the URL's target is implicit, probably guessed from the naming of the key or maybe noted in the response document's definition. The point of HATEOAS is that a client who understands the meaning of certain link relations (aka "rels"), such as ones in the IANA registry [1], can interact with these referred-to resources using the Standard Interface (of GET, POST, PUT, etc). Only in the section where the article talks about ways to express links in JSON do link relations appear. [1] https://www.iana.org/assignments/link-relations/link-relations.xhtml https://www.iana.org/assignments/link-relations/link-relatio...
- mpnally 7y agoIn the style of JSON I like to use, and whih GitHub and Google Drive use, the relationship name is given by the JSON name. So if you see 'owner: /person/12345', then 'owner' is the relationship name. There are other JSON styles for expressing the relationship name — the blog post mentions some of them. You might quibble about whether these names are the names of the relationship, or just names of one end of the relationship.
- dragonwriter 7y ago> Hypermedia As The Engine Of Application State (HATEOAS) > [...] > There is even a content type (or two) for it: application/hal+json and application/hal+xml. Also, the original content-type for REST, including HATEOAS, text/html.
- twblalock 7y agoHere is the problem with links in a nutshell: > The server is now free to change the format of new URLs at any time without affecting clients (of course, the server must continue to honor all previously-issued URLs). If you have to honor all previously-issued URLs then you aren't changing your format -- you are supporting two formats from now on, the old one and the new one. You can of course tell your users that you will deprecate the old format, but unless you are as powerful as Google your users may prevent you from enforcing a deadline for deprecation. If the URLs in your API responses are FQDNs rather than relative paths, all of this gets significantly harder to deal with. Even if you figured all of that out, links are not idiomatic if your users consume your api via an RPC or GraphQL.
- joshuamorton 7y agoThe point here is that you can do this more easily. Consider a really dumb example /people/12345 becoming /people/v2/12345 Sure, you still have to support the old version (maybe only temporarily), but migrating schemas gets marginally easier (you can do it via an entirely different service, for example)
- jayd16 7y ago>(of course, the server must continue to honor all previously-issued URLs). Why is this true for non-user facing urls? Seems like an argument could be made that you only need to support the urls for as long as the response was cacheable.
- emn13 7y agoAnd the problem isn't really limited to links in the response; the same goes for computed links based on keys too. In fact, links in responses are probably easier to update, because waiting for client software to upgrade can take a long time - but waiting for old versions to fall out of caches or at least dwindle to a small enough number is likely a much shorter wait. Furthermore, when your clients are composing links based on keys, there's no good way for the server to tell a client that the uri has changed - the composition is in the client, and the key (presumably) hasn't changed, so the thing the server would want to update is out of conventional reach. But with uri's there's at least a conventional solution - redirects. That's not going to be an ideal solution; but as a way of limiting transition pain it might have a place. All in all, I think links are likely less painful to use than keys when it comes to uri routing changes. (But really, try to avoid changing uri's in the first place, because whatever you do it's likely going to be painful somewhere).
- gigatexal 7y agoSometimes I wish REStful/REST the whole idea was a lot more opinionated. Sure you can have opinionated frameworks but nothing is stopping you from using a patch like I would a delete... (not the best example but you get the gist).
- westurner 7y agoA thing may be identified by a URI (/person/123) for which there are zero or more URL routes (/person/123, /v1/person/123). Each additional route complicates caching; redirects are cheap for the server but slower for clients. JSONLD does define a standard way to indicate that a value is a link: @id (which can be specified in a/an @context) https://www.w3.org/TR/json-ld11/ https://www.w3.org/TR/json-ld11/ One additional downside to storing URIs instead of bare references is that it's more complicated to validate a URI template than a simple regex like \d+ or [abcdef\=\d+]+
- Twisell 7y agoAnd on a more fundamental standpoint I get that disk space is cheap theses days. But you just doubled, if not worse, the storage space required for a key for a vague reason. It may never make any difference on a small dataset, where storage was anyway unaware of differences between integer and text. But it would be hiding in the dark. And maybe in a few year a new recruit will have the outstanding idea to convert text-link to bigint to save some space...
- anbop 7y agoBasically, advocating for dynamic typing rather than static typing, across an API boundary. You’ll save code constructing API requests but need to create a lot of application logic to handle an owner link and pet link separately, since they have different semantics.
- EugeneOZ 7y agoWhat is the source of knowledge for the client about fields, where they can read link to the entity? For human it's obvious that dog has an owner, so field "owner" should be used, but for code - you need to write it, "document it". So if you're going to "document" every field containing link to external resource, you'll end up with even more code, than just "documenting" API endpoints. Also, pretty often you need multiple IDs of entities to send POST/PUT request - just to create a relation. POST /adoption, owner_id=5, dog_id=7. How should it look with links? Will it be issue for the server to parse them? And it's just simple case with 1 to 1 relation, sometimes you need to add sets of objects to another entity. It's a really bad advice and after reading this I'm not sure I should trust other articles from that source.
- frosted-flakes 7y ago> Also, pretty often you need multiple IDs of entities to send POST/PUT request - just to create a relation. > How should it look with links? Like this: POST /adoption, owner=/people/5, dog=/pets/7 > Will it be issue for the server to parse them? Why would it be?
- EugeneOZ 7y agoBecause it's more difficult than parse "5" and "7", especially when you need to parse not only numeral IDs. "More difficult" always means "more bugs" and "more vulnerabilities". There is no single reason for this complication. The ONLY motivation author had - less knowledge about API endpoints on the client. But simultaneously it means more knowledge about the fields where links are stored, so it doesn't save nothing.
- imtringued 7y agoI believe "so it doesn't save nothing." should be written as "so it doesn't save anything." [0] Feel free to ignore me. [0] https://www.quora.com/Is-the-sentence-dont-do-nothing-correct https://www.quora.com/Is-the-sentence-dont-do-nothing-correc...
- kartan 7y agoThe article knowledge has been lost to time. In "relational databases" you always name the foreign key as the relationship between tables. From "A Practical Guide to Relational Database Design" from the year 2000. "Each relationship line should carry a pair of descriptive elements, which define the nature of the association between entities. A name is a single word or descriptive phrase; it should always contain a verb such as: owns, owned by, holds, administered by, etc. Examples from our simple model are: A PART is sold on an ORDER LINE. An ORDER LINE is placed for a PART." But, this has been lost because the practicality is that it is hard to know what is the element. As other comments points. Probably the best is both worlds: PersonId_Owner. PersonId_Veterinary. Or something similar. It seems that such a discussion should have been solved decades ago. And here we are. :)
- carmate383 7y agoWhy on earth would one trade off a short, static unique identifier for a potentially long, dynamic "link" that essentially binds all data to some crappy API that will be outdated in a few years? Is it really _that_ hard to use keys?
- i386 7y agoWhy on earth would you blow out your request size for the sake of purity? Calling GET /pets is going to return a lot of instances of pet with very similar URLs.
- hit8run 7y agoJSONAPI Specification also makes use of URLs in links to resources: https://jsonapi.org https://jsonapi.org
- Seb-C 7y agoI have implemented JsonApi a few times and so far it is the best one. It solves all problems I had about APIs in a very nice way. The `include` strategy is very simple and effective for dealing with relationships.
- sam0x17 7y agoI think I am missing the core concept here. This still uses IDs, only now you have to grep them out of a URL construct instead of just getting them directly? I don't get the intent at all here, but I have a suspicion whatever problem this tries to solve is better solved by UUIDs or by doing nothing out of the ordinary.
- treve 7y agoThe URL doesn't contain the ID, it _is_ the id. If you stop treating the URL as an opaque string but start parsing things out, you are definitely not getting any benefits. One benefit of using urls as ids is that it no longer is just an id, it also describes where you can get it's representation.
- asavinov 7y agoConceptual and data modeling aspects of this problem are discussed in [1]. It compares links with joins (and foreign keys) by proposing a solution (concept-oriented model) which does not use joins at all but rather relies on links only. Essentially, a foreign key is viewed as a relational workaround for representing links with some significant drawbacks and the question is why not to use links directly without relational wrapping. [1] Joins vs. Links or Relational Join Considered Harmful: https://www.researchgate.net/publication/301764816_Joins_vs_Links_or_Relational_Join_Considered_Harmful https://www.researchgate.net/publication/301764816_Joins_vs_...
- tuukkah 7y agoThat seems like a highly relevant aspect to explore. For example, GraphQL does not specify joins and nesting is semantically a reference (directed link in the graph) instead. Any update on the cliffhanger? > Yet, classical references miss some properties which are of crucial importance for data modeling. How links can be revisited in order to overcome these drawbacks will be our focus for future research.
- vasilakisfil 7y agoThe fact that JSON is just a format standard and doesn't have specified components (like links etc) but instead we have to built those on top has cost us a lot in APIs. Btw, according to RFC 8288 Web Linking (and before that 5988), a link consists of 3 parts + 1 optional part: "In this specification, a link is a typed connection between two resources and is comprised of: o a link context, o a link relation type (Section 2.1), o a link target, and o optionally, target attributes (Section 2.2). A link can be viewed as a statement of the form "link context has a link relation type resource at link target, which has target attributes". For example, "https://www.example.com/" has a "canonical" resource at "https://example.com", which has a "type" of "text/html". " That's why you need a standardized link component that is globally accepted/understood that takes into account all parts of the linking, instead of having various ways depending on the API/JSON-based Media Type to communicate that something is a link.
- deleted 7y ago[deleted]
- ragerino 7y agoReminds me of HATEOAS see here: https://en.wikipedia.org/wiki/HATEOAS https://en.wikipedia.org/wiki/HATEOAS Also RDF endpoints usually use resolvable URI's to connect concepts and objects with each other.
- deleted 7y ago[deleted]
- k_bx 7y agoThere's literally not a single upside of this shown in the article. > The server is now free to change the format of new URLs at any time without affecting clients (of course, the server must continue to honour all previously-issued URLs). No more than it was previously. > The URL passed out to the client by the server will have to include the primary key of the entity in a database plus some routing information, but because the client just echoes the URL back to the server and the client is never required to parse the URL, clients do not have to know the format of the URL. Instead, you now have to require new kind of knowledge, one of the keys which must be present in schema and their meaning. E.g. knowing that "pets" key is present and leads to a relationship of a particular kind, with all the implicit logic added and documented. And what if you want to get pet's owners with some additional parameter, like only getting ones which are exclusively yours? Would you need to edit that "pets" url adding "&exclusively_owned=true"?
- jfengel 7y agoI can think of one upside: it does make your keys more distinct. I can tell at a glance that /pets/12345 is a different category of entity from /people/98765. That could aid in debugging. It's a bit like a type system. And of course since it is a type system, it now means you've got yet another type system to deal with in your universe. One with unknown and inconsistent semantics, and no natural support built in. Conceivably those semantics could be added and software built to support it, but rolling your own is going go yield a lot of effort with much less benefit.
- k_bx 7y agoYeah, but it would essentially still be more of a "string id" rather than a URL, e.g. you can just make your ID to look like "person-12345" and "pet-52435" without tying it to URL. Showing as valid URL can be a "nice artefact" to remove the need to lookup API docs for collection names.
- Illniyar 7y agoThe idea of using uris instead of keys is not a new one (as has been mentioned by other commenters). Every few years the idea gets a resurgence of people who say that REST apis should be HATEOS and that we are doing it wrong. It seems obvious that the cost-value for this is simply not there, if it was good enough, you'd see developers requesting it and many more vendors implementing it. So far I haven't seen any recent changes that might skew the cost-value towards the uri's favor, only the opposite (cue GraphQl). Using uris have little benefits, but it does have the following problems: As a user of the api: * You need to keep an arbitrary length key in your database if you save references. It can cause some issues with certain setups (less so these days though). * If you keep the entire URI as identity, then you can't use multiple endpoints. For instance lots of companies have an endpoint for production and one for reports - using URI for one endpoint in another is quite awkward. * Working with queries is troublesome, especially with get request. Consider searching for all transaction of a specific account, where the account's identifier is `https://api.google.com/v1/account/123` https://api.google.com/v1/account/123` * Upgrading to a new version of the api (one with a different url like v1/v2) now not only requires you to change your code to work with the new version, but also migrate all previous ids you kept in your database, which is a much different and more error-prone issue then simply changing code.
- mpnally 7y agoOne simple strategy is not to change your approach to storage of IDs, which can continue to be "simple" keys. If you use that strategy, the only change from a "conventional" application is that the server is doing all the URL<->simpleId conversions instead of pushing that responsibility onto the client. Another good strategy is to store Ids in the form that the URI spec calls "path-absolute" URIs — basically you lop off the scheme and authority. This strategy works well, but may require a bit more care, and may cause problems if you have to integrate with other tables that have a different approach to keys.
- vbsteven 7y agoWhat really convinced me about HATEOAS and links was the first time I used a HAL browser and started clicking around to discover an API using only its entry point and navigating from there. From that point on I try to use it as much as possible. A typical API response for my projects looks like this: { "id": "3ccf0f1b-dd3f-48d9-911a-ddf479078c37", "name": "Quantus Tasks", "description": "Quantus Tasks Desktop Application", "license_key_type": "alphanumeric_32", "created_at": "2019-05-12T10:45:42.089406Z", "updated_at": "2019-05-12T10:45:42.089406Z", "_links": { "self": { "href": "http://localhost:8000/v1/applications/3ccf0f1b-dd3f-48d9-911a-ddf479078c37" }, "licenses": { "href": "http://localhost:8000/v1/applications/3ccf0f1b-dd3f-48d9-911a-ddf479078c37/licenses" }, "templates": { "href": "http://localhost:8000/v1/applications/3ccf0f1b-dd3f-48d9-911a-ddf479078c37/templates" }, "apikeys": { "href": "http://localhost:8000/v1/applications/3ccf0f1b-dd3f-48d9-911a-ddf479078c37/apikeys" } } } It still has the ID field in there for cases where the client needs to store the id itself but it should not be used to template URI's for related resources, the links are there for that.
- nebulous1 7y agoI've always been a bit undecided on this. I can see some obvious upsides, but on the other hand (apart from the downsides mentioned in the article and elsewhere) you've added 500 bytes to this entity for functionality that's only useful to the developer. Is the ability to click around in a HAL browser instead of a Swagger document worth the verbosity? Is there an argument that we might produce clients that can meaningfully deal with new links being presented without a developer being involved? I'd be surprised if that was realistic.
- vbsteven 7y agoYes, 500 bytes extra but it's not only for the developer discovering the API in a browser. If the client developer is exploring the API and then manually implementing an API client something is not right. Hal clients for this use case do exist. you point them to an entrypoint and they can discover and generate the necessary code/methods for interacting with the API. For example: https://github.com/pezra/hal-client https://github.com/pezra/hal-client
- perfunctory 7y agoOne advantage I see is that now you can do /pets?owner=/people/98765 or /pets?owner=/org/98765 which makes your api more polymorphic. Having said that I don't think URL is the right term to describe this. It's more like a <type, id> tuple.
- polskibus 7y agoIs the main reason for this is that crawlers could figure out and index more by themselves?
- austincheney 7y agoI am surprised the article didn't mention RDF. In every data facet of RDF the data is uniquely identified by URI. In the case of RDF the URI is merely a unique identifier that can resolve to a HTTP resource, but doesn't have to.
- schnable 7y agoHow do you write the article and never mention REST, hypermedia or HATEOS once?
- mpnally 7y agoIt required some discipline. I had to catch myself several times. The reason I avoided those terms is that I wanted readers to focus on one simple idea and not get distracted by all the baggage those terms bring with them. You can see by the comments that I wasn't totally successful.
- miguelmota 7y agoI wasn't fully convinced by this article. Language specific API wrapper clients can abstract all these complexities. Having links for IDs felt very unnatural but I guess that's because I have never came across an API that uses links like the article suggested
- vbezhenar 7y agoI used links but I'm gonna rewrite this code to simply pass IDs. The reason is simple: I need additional configuration for my server to know its hostname and I don't want to do that. May be my server even have few different hostnames for different clients? So I must parse client request and extract Hostname? But it's served via reverse-proxy, so I must do some complex configurations to pass this information. So many issues. But client knows perfectly well which server he's talking to, so he can just append server-base and id. Yes, client must know about its structure, but it's nonsense that client can somehow learn something. I'll code that anyway. May be it makes sense when you're writing an API and some different person writes a client and she's so shy that she don't want to even ask you. Yeah, she can inspect answer and find out that this seems like a link to query further. I never was in that situation, I was always building all software myself, so for me this does not make sense.
- theptip 7y agoI'm not sure about the verdict on URL versioning. I've used header versioning extensively and while flexible, it also carries some big downsides, mainly that it's confusing for new developers, and makes it real hard to casually explore the API in a browser (bad DX). I'm also not sure you do want to encourage mixing v1 and v2 API representations; I have certainly seen cases where it makes progressive upgrade easier, but it can also bring inconsistencies, so having a default new integrator path of "start at v2/login and use whatever links you get" is appealing. I do like the idea from Stripe of having Accept header versioning, but pinning every new client to default to the newest GA version. Gets around most of the DX concerns I raised, but it's a bit more machinery to wire up.
- tveita 7y agoNo one thinks twice about using links for images. You wouldn't make an API that specified images as "image id 2345, which the client can find at /images/{id}"
- minitech 7y agoI would and have…. If you’re in a position to specify that images can consistently be identified by a small key, that’s nice and simple. Go for it.
- gridlockd 7y agoNo. What's the point? None of this is useful to me, all of this is extra complexity. Why would I want to expose every addressable entity through URLs and HTTP? That's not what IDs are for. I'm aware that this fits into the whole REST idea. I still don't care.
- kabes 7y agoThe document also forgets that API's are not read-only. So let's say you have users and usergroups and you can request a usergroup with its list of users and you can add users to usergroups. If you use links for read, you should also use them for writes, otherwise it's quite inconsistent. So now you need to add a lot of parsing everywhere to extract the id's out of the urls, just for the sake of being more dogmatic
- treve 7y agoJust a thought: If in your example your usergroup and user are managed by the same service, then usually you should already have a feature in your framework that parses `/user/123` into its individual component and finds the relevant entity. Ideally you would use that.
- mpnally 7y agoI have implemented many APIs in this style, both read and write. Depending on the design of the storage layer, the server may or may not have to parse ids out of URLs. The client never has to; for the client, the URL is the only Id.
- rhacker 7y agoI feel like the author has never used graphql. We're also just finally graduating past REST to something more meaningful. This advice feels 15 years late and now totally wrong. An API shouldn't be tied to a protocol like http, it should be able to move on to other things. Ahh I was correct: > I have never used GraphQL, so I can't endorse it, but you may want to evaluate it as an alternative to designing and implementing your own API query capability. You really shouldn't write this giant article without having tried that.
- coding123 7y agoThis is all assuming REST is still a good idea.
- whack 7y agoI've literally spent 2 years working on a project that did exactly what this article is recommending. There were some places which needed the relative-url as an identifier, and other places which needed the "database id" as the identifier. We constantly had to extract the id from the URL, or convert the id into a URL, and keep a mental map of which format each input was using, and which format was needed for each output. It was a mess. I would personally not recommended this at all.
- deleted 7y ago[deleted]
- mpnally 7y agoIf some parts of your API used database keys as identifiers and other parts used URLs, then I can see that could be confusing. All one or all the other would probably be better.
- whack 7y agoAnytime you're interacting with the database, you'll need to use database keys. Anytime you're interacting at the API interface level, you'll need URLs. Anything in the middle is then going to be a gray area, especially when you have multiple people working on this implementation together.