Hacker News new | ask | show | jobs
by tchow 3294 days ago
When doing something like `student/1245/courses` there will certainly be a scenario where you want a list of courses on their own as well.

If planning ahead, does that warrant designing your route such as:

`/courses` where you get a list of courses and `/courses?studentId=12345` Where you get courses scoped to a student...

Or is it better to just recreate a separate route such that you have /courses AND student/2345/courses?

Im concerned that the latter results in duplication of code and possibly more confusion (ie not clear if the API require POST to /courses or /student/12345/courses to create a course for a student ) while the former results may cause (lack of) caching problems.

Suggestions?

6 comments

I follow JSON API v1.1, mostly anyway, see [1].

I not only create both paths, I also specifically create `relationships` links to control the relationship itself. That way, I have a restful way of, say, making a signed article anonymous. DELETE articles/2/relationships/author not that this doesn't delete the author just the relationship itself.

Your worry about the duplication of code is well founded, but slightly off. For what are called "related" links (articles/4/author) we only ever issue GETs. There are many reasons for this and you've started to catch onto a few of them.

This means a _lot_ of routes (over 300 right now), but with smart abstractions it's not so bad. I've been thinking of doing a weird fork of Rails based on how productive I've become. Basically I want to follow Rails but I want to override a lot of their decisions that don't fit JSON API's needs. Maybe one day.

[1] With a minor addition that I find helpful: I add an `also` link in the relationship body. GET articles/5/author would have `also` set to `users/45`. In this manner I can easily get a reference to the related resource and I make it explicit that the resource isn't dependant on it's relationship continuing to exist with the parent resource.

I believe '/courses' and 'student/1234/courses' mean two different things. The former implies a list of courses and the later implies an association between a student and a course.

POSTing to '/courses' creates a new course. However POSTing to a 'student/1234/courses' creates a relationship/association between a student and a course.

So, it's two different things IMO and not a duplication of logic.

This isn't true. Or at least not necessarily true. If you're referring to the JSON API spec you're conflating related and relationship links. The former is strictly for GETs (GET students/5/courses) and the later is for controlling the relationship itself (PATCH students/5/relationship/courses).

See here for more information:

http://jsonapi.org/format/upcoming/#crud-updating-to-many-re...

I think it depends on what makes sense for your problem domain. Think about what kind of HTTP methods and the associated data with each and decide accordingly.

Nesting of resources in a REST API can become problematic, for these reasons, so another option would be to get rid of the nesting and use query parameters to filter based on whatever criteria you want. This is the motivation for PostgREST [1] where the author views the shortcomings of nested API resources as similar to those of hierarchical databases. GraphQL solves this similarly, it also has a richer query language, but it's missing some of the benefits of REST like caching, etc.

[1]. https://postgrest.com/en/v4.1/intro.html

If it's an API then you could bypass the aforementioned constraints of spitting your request up by URI "directories" and instead have your query's parameters passed via form data or serialised as JSON* in the HTTP request body.

URIs are easier to deal with but not _that_ much easier if you're programmatically sending the requests as one might expect to do with an API

* Other data formats also exist

Adding stuff to a request body in GET is considered an antipattern
If your APIs are login sensitive then you'd want your APIs to be POST requests anyway.
Wat?
If your REST APIs do updates or retrieve sensitive information then odds are you'll have some kind of authentication method, possibly with session cookies or whitelisted IPs. Allowing sensitive APIs to be sent via GET means that you open yourself up to a couple of easy vectors of attack. eg a malicious webpage with a <img src="http://example.com"> tag using the API end point as the src URI. This means the target person opening the page will send the API request as themselves, using their IP and cookies. Granted the API wont return an image so the "image" will fail to render but that doesn't matter as the API will still execute successfully. However if your APIs only accept POST requests then you mitigate this particularly attack.

It's pretty common advice to recommend any APIs that require user authentication to be sent via POST. In fact it's one of the first things pen testers will check for and you'd also fail PCI DSS vulnerability scans for exposing APIs via GET as well.

Disclaimer: I've works on multiple projects that have been pen tested, been audited by the UK Gambling Commission and/or had to adhere to PCI Data Security Standards.

> I've works on multiple projects that have been pen tested, been audited by the UK Gambling Commission and/or had to adhere to PCI Data Security Standards.

Why do you want to 'disclaim' that? Assuming it's true, you might have meant 'disclosure', but I think what you really mean is much closer to 'source' - i.e. 'why I know this'.

I just had this exact 'problem' with an API I'm building. I just ended up creating both routes which accessed the same internal function to return the same results - no duplication of code necessary. Personally I think this is a good resolve as it answers both use cases depending on the accesssors context.
To add, my endpoints were:

/accounts/{id}/users

And

/users/account/{id}

Both return the users attached to a particular account.

just drop this REST hype altogether and use adequate protocols for communication
As in design a custom TCP/IP protocol for every application?
I think there are a few GraphQL enthusiasts entering this thread right now, which has its own merits, but can live perfectly next to REST.
In my company we use graphql for getting, and REST for all other operations.

Might be an antipattern, but has served us well.

We're also using a special flavor of graphql that returns the data as flat dictionaries instead of deeply nested dictionaries. Whether you need that or not of course depends on the client design.

As in using GraphQL for this purpose, I presume. Or even offering an SQL-API. Seems more ideal than overly complicated URL schemes.