RESTful URL design: public vs private API, hierhachy API design pattern, URI vs URL design?
Asked Answered
L

3

4

I often get into issue like this, very similar to this Hierarchical RESTful URL design

Supposed the service only offers the user to upload a document.

POST, GET /accounts
PUT, DELETE /accounts/{name}  or /accounts/{id}

Now a document is attached to a specific user, whether it is public or not at all is not of concerned here.

The two ways are POST /documents vs POST /users/documents

Why? Because later when a document resource is created, that document is under the user's control. So I would expect to have

GET, PUT, DELETE /users/{name}/documents for getting, changing and deleting a bunk of documents the users own.

I can have GET, PUT, DELETE /users/{name}/documents/{name/id}

But the same can be achieved just /documents/{users}/.... or /documents/{id}. And this is similar to how one might organize unix files (though /users/... is also another way to organize files...) You see, there is also a philosophy[phy of uri vs url design.

Another consideration is whether the API is visible to user or not. If this is simply a backend API, only the developer has access (backend <- frontend server <- frontend ajax), then a site user may be happier with /users/{name}/documents/{id/name} while some programmers will dislike this long url if the API is public (like twitter api).

What do people think about these issues?

Loath answered 19/12, 2013 at 23:0 Comment(0)
S
2

Pragmatically, obviously all of the above are correct.

But philosophically, I think it comes down to the "S" in REST ... This is really a question of what the resource actually is that you are managing state for. If your app is intended to deal with documents, then that needs to be resource that is apparent in the URL. If your app is more about Users performing some workflow, then it is likely that you want to make the User apparent in the URL.

Internally in an app, things quickly get built for the sake of convenience, so that you can have URLs that mix and match resources to make ownership apparent (as in your example). Think of it this way. Users come to use your app to handle their stuff, some of which are documents. They know they logged in, and then they have access to their documents, some of their information is traveling around with them in a session, and from that perspective

user/{name}/documents

makes sense. They have context.

If you make that API public ... that is, if people who don't have the same perspective as the user in your App are consuming your API ... then that public face needs to be semantically clear - are users exposed? documents? do consumers care who owns that document? That API is like a table of contents; it should be helpful.

So if your api's contract with other apps is that your app is exposing documents, then I would say that you are better off making that apparent in your URL. The notion of a route to a user doesn't make sense here because the context is not relevant per the contract.

You bring up an interesting example that shows this pretty clearly. Take

document/id versus document/name

The naming of files can follow conventions or be app specific. Good example are image sharing apps or apps for particular industries like accounting. But the public API cannot assume that the naming convention is apparent or obvious (unless you allow consumers of the API access to this kind of information). Because it is a public facing API, you are probably better off going with

document/id

because ids are typically understood to be immutable, even across an API and the semantic is pretty clear.

Ultimately, the technology can do anything we want. But things like routes and URLs are important to understand the semantic of the API itself. If you are managing something, that should be obvious when consuming the API, and should not get bogged down by your local or technologically-specific conventions.

Stotts answered 19/12, 2013 at 23:18 Comment(0)
G
1

Ok, so there are some conventions that you have undoubtedly ran into, researched or stumbled upon. However, while I am probably not 100% correct here, just put aside the conventions for now.

When writing RESTful services, think first about the hierarchy of the process and who owns what. In other words. I wouldn't write a url like /user/doc/{id} if I wanted to delete a document that belonged to a group of people. Something like /user/doc/{id} generally means logically that you are dealing first with something that is related to a user, then that is a document and thirdly a specific document to that user.

So what I am trying to say, is that I generally try to keep the GET,POST,PUT,DELETE methods to entities and the URL's associated to the hierarchy of the entity. Does it work 100%, no, but does it make it obvious for any coder coming behind me, almost always the answer is yes.

Your example of /documents versus /user/docs to me means you have two different documents. Documents that will live beyond the life of a user and documents that would live and die by the user account. If in my example you had /documents/usr/{id} then I would say even if you "deleted" that document using /documents/usr/{id} I would say you are only deleting that users reference to the document as the document is higher in the hierarchy than the user. Likewise, if you had /user/document/{id}, I would say that the document is user specific and will die with the user.

Hopefully that makes sense. And again, not saying I am 100% right and there are always special cases.

Gladiatorial answered 20/12, 2013 at 0:55 Comment(0)
I
1

I don't see any issues here. REST does not have URI constraints, that is an implementation detail, which depends completely on the service developers.

REST is about M2M communication and not about H2M communication, so end users won't see anything about the REST URIs.

Clients does not concern about URIs either, if you apply the HATEOAS constraint. If you don't like hypermedia response and rather document the URI templates, then I think shorter URIs are better, because client developers mistype them harder.

off:

I am currently reading about DDD aggregates in Vernon's book and there is an interesting resemblance here. If you want to access user documents, then you have 2 modeling options.

  • The first option to put documents behind the user aggregate, so you have to use
    user1 = userRepo.findById(1); user1docs = user1.documents
    to access them by using the user aggregate.
  • The second option to define a document aggregate:
    user1docs = docRepo.findByUserId(1).

This is somewhat similar to users/1/docs vs docs?user=1.

By DDD the solution depends on the business need. If you need transactional (immediate) consistency between the user and their documents in some scenario, then the first one is the good choice, otherwise you can stick with eventual consistency and the second option.
Maybe this could be applied to REST too. So if there are invariants between the docs and user properties, then you have to use the

PUT /users/1 {
    prop: 123,
    docs: [
        {},
        {},
        ...
    ]
}

to avoid inconsistent states. Otherwise it is better to stick with the following:

PUT /users/1 {
    prop:123
}

POST /docs/ [
    {},
    {},
    {}
]

POST /user-docs/ [
    {user:1, doc: 1},
    {},
    {},
    ...
]

It is hard to find out any validation rule which depends on both user properties and user docs, so I would probably choose the second option.

Ofc. there can be exceptions to this rule, for example if the second option is unacceptably slow or you want to ease adding user docs to the client's developers then it is better to use the first option.

Individuality answered 30/11, 2015 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.