Home > Mobile >  What are the RESTful API best practices in this case?
What are the RESTful API best practices in this case?

Time:10-10

I am new to Node.js and have an application in which there are multiple organizations with multiple admins and multiple groups with multiple users who can make multiple posts. Like this:

  • Organization
    • Admins
    • Groups
      • Users
        • Posts

Admins have access to everything within the organization. Their primary goal is to observe and analyze the posts their users are making. Admins can:

  • Get all posts by organization
  • Get all posts by group
  • Get all posts by user

Because there are three specific ways in which posts can be queried, I have built three separate routes and handler functions for each of the ways to query posts:

  • /api/posts/organization/:organizationID
  • /api/posts/group/:groupID
  • /api/posts/user/:userID

As I have learned more about RESTful APIs, everything I see tells me that "path params are used to identify a specific resource or resources, while query parameters are used to sort/filter those resources."

This is a bit confusing for a beginner like me. It seems like "posts" are the "specific resource" here, so I should change my API to have one api/posts/ route and use query params to filter them. Is that right?

CodePudding user response:

There is no single best practice, and your approach seems reasonable. Typically I would structure this as:

/users/:id/posts
/groups/:id:/posts
/organization/:id/posts

As this makes the relationship more clear (posts that belong to users).

But your approach, and using 1 endpoint with query parameters are all reasonable approaches. The most important thing is to be consistent and ideally find an existing style guide to follow.

CodePudding user response:

What matters here that you need to be able to identify your resources. The simplest approach is:

/api/posts/{postId}
/api/organizations/{organizationID}
/api/groups/{groupID}
/api/users/{userID}

As of the admin vs regular user, it is called role-based access control (RBAC). So roles can be another resource, though if you never want to edit them, then just hardcode them and manage roles by users and groups.

The path is for the hierarchical part of resource identification the query is for non-hierarchical part, but they sort of overlap, which is not a big deal, you can even support both. So the URI is a unique identifier, but it is not exlusive, you can have multiple URIs, which identify the same resource.

As of the query part, I like to use it for filtering collections and return always an array even if it contains one or zero items. So with my approach:

/api/users/{userID}
/api/users/?id={userID}

These two are not exactly the same, because the second one returns an array with a single item. But this is not a standard, just my preferred approach.

I like the upper simple URIs instead of the heavily nested ones and add more depth only if it grows really big. It is like namespacing in a programming language. For a while you are ok with the global namespace, but if it grows big, then you split it up into multiple namespaces.

In your case I think I would do it the opposite direction as you did:

  • Get all posts by organization: /posts/?organizationId={id}
  • Get all posts by group: /posts/?groupId={id}
  • Get all posts by user: /posts/userId={id}

Another approach is:

  • Get all posts by organization: /organizations/{id}/posts/
  • Get all posts by group: /groups/{id}/posts
  • Get all posts by user: /users/{id}/posts

You can even support both approaches simultaneously or a different approach you like better.

Tbh. when you do something that is really REST, then the URI structure does not matter this much from REST client perspective, because it checks the description of the hyperlink it gots from the server and does not care much about the URI structure. So the response should contain something like the following:

{
 "type":"link",
 "operation":"listPostsForOrganization",
 "method": "get",
 "uri": "/api/organizations/123/posts/",
}

And you use the API with the client like this:

let organization = await api.getOrganizationForUser(session.currentUser.id)
let posts = await organization.listPostsForOrganization()
  • Related