Naming convetion for REST API design

Hey all,

Possibly a subjective question… Which of these is the best way of designing an API for an issue tracker project

api/issues/{project}?status=open

api/projects/{project}/issues?status=open

thanks

James

Neither in my opinion :slight_smile:

But if I were to choose between these two, then definitely the second. Your endpoint has to describe one-to-many relationship: example.com/one/many?which=one

1 Like

Hello!

I’d use the first, because it’s more concise and clearly separates the terms, however, as you said, it’s subjective (unless it affects the results).

1 Like

@jamesfmac, definitely a subjective question :laughing:

2 Likes

@snigo If you were to design one what would it look like? Any suggestions would be super helpful :slight_smile:

@skaparate Agree that the first is cleaner. The problem I have with it is that issues belong to a project (and not the other way around). Maybe pushing the project into the query rather than a URL param would make sense?

api/issues?project={project}&status=open

Since the main purpose is to track issues, IMO, the first one is better.

However, in my project I used both.

1 Like

Yes, this is what I would do. Think of it in SQL logic, in your API you need to find issues table and select all fields where project is this and status is that. Why would you give project a path status.

On other hand, in NoSQL logic, you would store issues under projects, and if you want to sync that logic onto API, then you should do: api/projects/:project/issues?status=open

2 Likes

This makes sense! The API structure that FCC uses assumes Mongo. When I use Postgres instead the API structures don’t seem to make as much sense.

Thanks!

API is not a mapping of database structure though: REST assumes nothing at all about DB structure, there shouldn’t really be any reason to purposely reflect it. A REST API maps resources to endpoints, defining how you access them. The DB is how they are stored: there is an entire layer above that so that you don’t have something tied to a specific storage architecture.

Hey @DanCouper Thanks for your thoughts! Super interesting :slight_smile:

Would love your opinion on how best to design (or think about designing) a rest API for something like this:

  • Projects - can have multiple issues
  • Issues - must belong to a single project
  • Need to be able to do crud operations both issues and projects

What else should I be thinking about when designing an API?

Thanks
James

Normally most REST-like interfaces do it like this, which gives one endpoint per resource.

projects
projects/:project_id
projects/:project_id/issues
projects/:project_id/issues/:issue_id

Ideally the entry point of the API should respond with metadata and links to resources available from it, and them those links should respond with metadata and links and so on (hypermedia as the engine of application state, HATEOAS). Mostly this isn’t done it’s a pain to do imo & the benefits of actually implementing it aren’t exactly overwhelming.

APIs tend to be REST-style, only picking a few principles. Query params used to access specific resources I’d say aren’t really RESTful. (This is fine btw, most stuff isn’t actually REST as described).*

As an example of a REST API that actually implements HATEOAS, see the GitHub API.

CRUD has nothing to do with REST, that’s a seperate thing.

* EDIT: for completeness: because each endpoint describes itself and the URIs that link to it, the structure of the URI is completely irrelevant if you implement it. So as long a big enough UUID code was chosen, every resource everywhere on the internet could just, for example, be referred to with a UUID rather than like my-site.com/api/foo/bar/baz, which is much easier to generate and avoids confusion (not very readable but hey ho)_

And you (or an automated tool) could just follow links returned by the REST APIs. The entire internet would work like following links in Wikipedia. This is the entire point of REST. Reality isn’t so idealistic though (see also: other utopian stuff like, say, microdata or most stuff Ward Cunningham designs).

1 Like

I think REST works best when you keep in mind that each individual object needs an Idempotent way of getting to it. Ask yourself these questions about an issue.

How will I access the object? For example, with jira, the issue is going to be referenced by a tag that is comprised of the short project name AND the issue id.

The underlying design has to inform the api design. But it should not on the other hand constrain it to be a mirror image of a relational or hierarchical architecture.

Clearly you have 2 different object types you are working with:

  • Project
  • Issue

So you need rest api points for each.

/api/issue
/api/project

An Idempotent way to access an issue:

/api/issue/{id}
/api/project/{id}

And of course these all assume they are GET methods.

To create a new project:

POST /api/project -> returns the Idempotent URL of the newly created project on success!

To create a new issue:

POST /api/issue

Now you want to search for issues. In my experience, the cleanest way to do that is to assume that you are going to search or find using some variable criteria. Even though you may have a hierarchical relationship between an issue and a project, there is no reason to bake that into the api.

GET /api/issues?project=id&status=open&owner=bob

The main technique employed is to reference an issue using /api/issue and to work with a collection of these things, use the plural ie. /api/issues

You’ve decoupled finding an issue from knowing in advance its project. Thus the api more naturally lets you find issues across projects

GET /api/issues?status=open

For convenience you can always add a seperate route if you want that passes parameters to the code you will write for the /api/issues system:

GET /api/project/{id}/issues?status=open

I don’t see a big win in having these more specific paths, but sometimes people want them for simplicity. They are much easier to do when you didn’t start out by hardwiring the specific paths in advance.

4 Likes

This is good advice.

I guess the hierarchical structure normally drops out of some framework (ie Rails/ActiveRecord and everything that’s come after that), where it’s [sometimes] easier to write it that way in the routes file. As you say, it just makes it easier to reason about the structure of the API when it’s fairly simple. The trade-off being that it doesn’t work very well once it’s not simple, it’s pretty inflexible.

1 Like