I have a doubt, I have a URI that will bring me all user's account from another rest service. Example:
GET /users/21212/account
Now I have the need to bring 3 kinds of different groups of user's account, something like: "low-account", "medium-account" and "high-account".
But the discussion is, how should I enable this on my endpoint ?
GET /users/21212/accounts/low-account
GET /users/21212/accounts/medium-account
GET /users/21212/accounts/high-account
or
GET /users/21212/accounts/1?selector=low-account
GET /users/21212/accounts/1?selector=medium-account
GET /users/21212/accounts/1?selector=high-account
I'm inclinated to follow the first approach, I think is more intuitive and the manutenability of the code will be easily preserved... What do you think is conceptual right/better and why ?
I would choose the following option:
GET /users/12345/accounts?group=low-account
It will return you all accounts that belong to 'low-account' group for user 12345.
I tend to use query params if they represent something like filtering. Also I would recommend you to take a look at HATEOAS principle. Then you will be more flexible with your URIs.
Related
I have a simple API that return Something for a given ID and it must be used without any kind of authentication, the URL should be permanent and yet I want to avoid as much as possible it to be botted.
The Url is something like this:
https://url/{SomeId}/doSomething
The problem is that this is very predicable and a bot could easily try all the ID and get everything associated to it.
I'm looking for a way to make the URL non predictable like for example:
https://url/{SomeId}/doSomething?Key=SomeVeryLongCryptographicKey
In this way except if you run a supercalculator you shouldn't be able to deduce all the URLs
I know that there is probably a lot of ways to do that, like using a DB which I want to avoid.
I guess I'm looking for a kind a JWT associated to the ID without expiration but maybe there is better solution.
Edit: to give a better example i'm looking to do a bit like did Zoom for permanent invitation Links. They had predictable room Ids and they added a password making it unpredictable lie so:
https://us05web.zoom.us/j/132465789?pwd=SUxIU0pLankyhTRtWmlGbFlNZ21Ndz08
What would be the best/light/"secure" way to achieve that ?
Also I'm using .Net if there is a library doing that, it would be great.
I think your idea of using a JWT makes the most sense. Better to use something standard from a cryptographic point of view, and the JSON format allows for encoding whatever you need to provide to the receiving endpoint (user names, entity names, entity IDs, other things).
There are standard Microsoft libraries for building and validating JWTs, but I prefer the library Jwt.Net (https://www.nuget.org/packages/JWT). It lets you do something like this quite easily:
var token = JwtBuilder()
.WithAlgorithm(new RS256Algorithm(publicKey,privateKey))
.AddClaim("uri", String.Format("https://example.com/api/{0}/{1}", entityName, entityId))
.Encode();
Just add whatever claims you like, and the JWT will then contain what you want to transfer (I've used an example of the URI that you want to give to the entity) and a signature with your private key. You could even just give a URL like https://example.com/from_token/eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1cmkiOiJodHRwczovL2V4YW1wbGUuY29tL2FwaS90ZXN0RW50aXR5LzEyMzQifQ.K2P4wSF6g1Kt-IHMzlklWTV09_MIkoiNHQztSIYOohmOWe7aBfFSQLIKSbdTECj9XPjNNG_AjH9fdjFglkPuYfr2G9rtl2eh5vTjwdM-Uc0X6RkBu0Z2j7KyMKjkaI3zfkIwhtL1mH873xEBtNOGOW18fuBpgnm8zhMAj1oD3PlDW8-fYBrfLb6VK97DGh_DyxapbksgUeHst7cAGg3Nz97InDPtYcWDi6lDuVQsj3t4iaJBRL8IM785Q8xjlHHhzdfcX3xU4IhflyNHHXxP56_8ahNNMOZKWdwgbTSIxEEB98b3naY3XknC-ea7Nc1y4_5fszrYdy3LaQWV43jpaA
and have the handler decode the entity name and ID you want to retrieve directly from the URI while verifying the signature. Decoding with the library is just as easy:
var json = JwtBuilder.Create()
.WithAlgorithm(new RS256Algorithm(_key))
.MustVerifySignature()
.Decode(token);
I'm currently working on ng-admin.
I'm having a problem retrieving user data from my REST API (connected to a MongoDB) and displaying it.
I have identified the problem as the following:
When I enter http://localhost:3000/users into my browser, I get a list of all users in my database.
When I enter http://localhost:3000/users?_page=1&_perPage=30&_sortDir=DESC&_sortField=id,
I get [] as a result.
I am quite new to this, I used both my browser and the POSTMAN Chrome extension to test this and get the same result.
http://localhost:3000/users_end=30&_order=DESC&_sort=id&_start=0
This (/users_end) is a different request than /users.
It should be:
http://localhost:3000/users?end=30&_order=DESC&_sort=id&_start=0
Or, by looking at the other parameters:
http://localhost:3000/users?_end=30&_order=DESC&_sort=id&_start=0
with end or _end being the first parameter (mark the ?).
Update (it is ? and before the _, I have edited.):
If adding parameters to the request returns an empty list, try adding only one at a time to narrow down the problem (there's probably an error in the usage of those parameters - are you sure you need those underscores?).
Your REST API must have a way to handle pagination, sorting, and filtering. But ng-admin cannot determine exactly how, because REST is a style and not a standard. So ng-admin makes assumptions about how your API does that by default, that's why it adds these _end and _sort query parameters.
In order to transform these parameters into those that your API understands, you'll have to add an interceptor. This is all thoroughly explained in the ng-admin documentation: http://ng-admin-book.marmelab.com/doc/API-mapping.html
I am trying to write 2 Rest GET methods.
Get user by Id
Get user by userName.
I need to know if there is any resource naming convention for this. Both my id and username are strings.
I came up with:
/api/{v}/users/{userid}
/api/{v}/users/username/{username}
However, 2) doesn't seem correct and if I change 2) to /api/{v}/users/{username}, I am mapping to 1) as both id and username are strings. Or is it considered acceptable to use /api/{v}/userbyName/{username}?
How should I name my resource route in case 2) ?
First of all: https://vimeo.com/17785736 (15 minutes which will solve all your questions)
And what is unique? Is the username unique or only the id or both are unique?
There is a simple rule for all that:
/collection/item
However, 2) doesn't seem correct and if I change 2) to /api/{v}/users/{username}, I am mapping to 1) as both id and username are strings.
If your item can be identified with an id and also with an unique username - it doesn't matter if it's the username or the id - simply look for both (of course your backend needs to handle that) and retrieve it.
According to your needs this would be correct:
/api/{v}/users/{userid}
/api/{v}/users/{username}
but I would choose only to use: /api/{v}/users/{userid} and filter by username only with a query parameter (description for that down there below)
Also will I break any rules if I come up with
/api/{v}/userbyName/{username}
Yes - /api/{v}/userbyName/{username} will break the rule about /collection/item because userByName is clearly not a collection it would be a function - but with a real restful thinking api there is no function in the path.
Another way to get the user by name would be using a filter/query paramter - so the ID will be available for the PathParameter and the username only as filter. which than would look like this:
/api/{v}/users/?username={username}
This also don't break any rules - because the query parameter simply filters the whole collection and retrieves only the one where username = username.
How should I name my resource route in case 2) ?
Your 2) will break a rule - so I can't/won't suggest you a way to do it like this.
Have a look at this: https://vimeo.com/17785736 this simple presentation will help you a lot about understanding rest.
Why would you go this way?
Ever had a look at a javascript framework like - let's say ember. (Ember Rest-Adapter). If you follow the idea described up there and maybe also have a look at the json format used by ember and their rest adapter - you can make your frontend developer speed up their process and save a lot of money + time.
By REST you send back links, which can contain URI templates. For example: /api/{v}/users/{userid} in your case, where v and userid are template variables. Since the URI structure does not matter from a client perspective you can use whatever structure you want. Ofc. it is more convenient to use nice and short URIs, because it is easier to write the routing with them.
According to the URI standard the path contains the hierarchical while the query contains the non-hierarchical part of the URI, but this is just a loose constraint, in practice ppl use both one.
/api/{v}/users/name/{username}
/api/{v}/users/name:{username}
/api/{v}/users?name="{username}"
Ofc. you can use a custom convention, for example I use the following:
I don't use plural resource name by collections
I end collection path with slash
I use slash by reducing a collection to sub-collections or individual items
I don't use slash to give the value of a variable in the path, I use colon instead
I use as few variables and as short URI as I can
I use query by reducing a collection to sub-collections especially by defining complex filters with logical operators
So in you case my solution would be
/api/{v}/user/
/api/{v}/user/name:{username}
/api/{v}/user/{userid}
and
/api/{v}/user/?firstName="John"
/api/{v}/user/?firstName="John|Susan"&birthYear="1980-2005"
or
/api/{v}/user/firstName:John/
/api/{v}/user/firstName:John|Susan/birthYear:1980-2005/
etc...
But that's just my own set of constraints.
Each resource should have a unique URI.
GET /users/7
{
"id": 7,
"username": "jsmith",
"country": "USA"
}
Finding the user(s) that satisfy a certain predicate should be done with query parameters.
GET /users?username=jsmith
[
"/users/7"
]
Lets say I have a couple URI template endpoint for a REST API as such:
"/events" - gives me all events
"/events/{eventId}"
"/events/2234" - gives me the event specified by the eventId
What if I wanted all events by a specific broker. I was thinking maybe this:
"/events/{brokerId}"
e.g.
"events/2345" - gives me all the events for the specific BrokerId
problem is how would the service know if the incoming url is sending in a broker vs. eventId? the /2234 and /2345 are indistinguishable.
I'm wondering how this is handled in REST. Do most people typically do something like:
"events/broker/{brokerId}"
or do they somehow specify what type of Id it is some other way?
I'm not sure about others, but I would probably do something like:
/brokers/{brokerId}/events
For example:
/brokers/2345/events
Of course, you could always do something less rest-ful, such as:
/events?brokerId=2345
Is this a good structure for REST URLs?
Assuming:
GET /account <- get list of accounts
GET /account/1234 <- get account 1234
etc.
If the account resource has a collection that I want to interface, is this a good idea?
GET /account/1234/note <- get notes for account 1234
POST /account/1234/note <- add note to account 1234
DELETE /account/1234/note/321 <- delete note 321 on account 1234
Especially that last one gives me pause; typically I wouldn't require both the entity ID and the parent ID when deleting.
Or maybe something like this would be better?
GET /account-note/1234 <- get notes for account 1234
POST /account-note/1234 <- add note to account 1234
DELETE /account-note/321 <- delete note 321 on account 1234 (b/c note 321 is on account 1234)
But then I'd end up with a pretty shallow URL set.
Thanks
There's nothing wrong with your first api. In large part, the idea of the RESTful interface is to go with the natural tree structure of the web, and your first approach is in keeping with that. It's also going to be a structure that does the API's job of abstracting away implicit constraints of your datastore, because the second approach implicitly assumes that the id of note is globally unique. This may be true now, and will probably remain true, but it's also exactly the kind of bug that suddenly appears with disastrous consequences when, down the line, some kind of major db change happens.
I'd go with your first scheme. It's a familiar rest pattern, it's intuitive, and it's not going to blow up in a weird way down the line. Also, in response to #Corwin01 minimize the query params--they're not so RESTful.
I referenced this question in my comments on another question and between the feedback I got there and my own research this is what I've come up with.
Firstly, the question is sort of flawed. RESTful APIs, or to use the perferable term Hypermedia APIs should include the urls of related actions so that the interface is discoverable and changes won't break existing clients, therefore the exact structure has significantly less importance than I was placing on it, these can be changed later.
Secondly, the note in the example will be retrieved as part of an account query, maybe something like this:
<account>
...
<notes>
<note>
...
<link href="/account-note/123" rel="note">
</note>
</notes>
</account>
The client will never be assembling urls to the account on their own, the client will use the link provided. Since the note ID is globally unique in this case, there is no need to include the key twice. Hence the answer to the question is no, the first example is not a good REST url structure, the second one is better. (Although still maybe not the best...)
Mind you, my experience is with JAX-RS and Jersey, so I'm not sure what the exact differences are.
However, this is what I would do:
#GET
#Path ("/account/note/{id}")
public void displayNotes(#PathParam ("accountId") String accountId)
{
//do stuff
}
#POST
#Path ("/account/note")
public void addNote(#FormParam ("accountId") String accountId)
{
//do stuff
}
#POST
#Path ("/account/note/delete")
public void deleteNote(#FormParam ("accountId") String accountId, #FormParam ("noteId") String noteId)
{
//do stuff
}
This way, you don't have cluttered and confusing URLs that the user doesn't need to see anyways, especially if the user tries to navigate there on their own. Which is ok for displaying the accounts, but would confuse them for the URLs that POST, since they would get a 404 and not understand why.
Keep it simple, and just user #FormParams since the user doesn't need to see that anyways.