REST API URI Naming Conventions and Best Practices

In REST, having a strong and consistent REST resource naming strategy – will prove one of the best design decisions in the long term. Let’s discuss.

1. What is a Resource?

In REST, the primary data representation is called resource. Having a consistent and robust REST resource naming strategy – will prove one of the best design decisions in the long term.

The key abstraction of information in REST is a resource. Any information that can be named can be a resource: a document or image, a temporal service (e.g. “today’s weather in Los Angeles”), a collection of other resources, a non-virtual object (e.g., a person), and so on.

In other words, any concept that might be the target of an author’s hypertext reference must fit within the definition of a resource.

A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time.

Roy Fielding’s dissertation

1.1. Singleton and Collection Resources

A resource can be a singleton or a collection.

For example, “customers” is a collection resource and “customer” is a singleton resource (in a banking domain).

We can identify “customers” collection resource using the URI “/customers“. We can identify a single “customer” resource using the URI “/customers/{customerId}“.

/customers			//is a collection resource

/customers/{id}		// is a singleton resource

1.2. Collection and Sub-collection Resources

A resource may contain sub-collection resources also.

For example, sub-collection resource “accounts” of a particular “customer” can be identified using the URN “/customers/{customerId}/accounts” (in a banking domain).

Similarly, a singleton resource “account” inside the sub-collection resource “accounts” can be identified as follows: “/customers/{customerId}/accounts/{accountId}“.

/customers						//is a collection resource

/customers/{id}/accounts		// is a sub-collection resource

1.3. URI

REST APIs use Uniform Resource Identifiers (URIs) to address resources. REST API designers should create URIs that convey a REST API’s resource model to the potential clients of the API. When resources are named well, an API is intuitive and easy to use. If done poorly, that same API can be challenging to use and understand.

The constraint of a uniform interface is partially addressed by the combination of URIs and HTTP verbs and using them in line with the standards and conventions.

Below are a few tips to get you going when creating the resource URIs for your new API.

2. Best Practices

2.1. Use nouns to represent resources

RESTful URI should refer to a resource that is a thing (noun) instead of referring to an action (verb) because nouns have properties that verbs do not have – similarly, resources have attributes. Some examples of a resource are:

  • Users of the system
  • User Accounts
  • Network Devices etc.

and their resource URIs can be designed as below:

/device-management/managed-devices 

/device-management/managed-devices/{device-id} 

/user-management/users

/user-management/users/{id}

For more clarity, let’s divide the resource archetypes into three categories (document, collection, and store). Then it would be best if you always targeted to put a resource into one archetype and then use its naming convention consistently.

For uniformity’s sake, resist the temptation to design resources that are hybrids of more than one archetype.

2.1.1. document

A document resource is a singular concept that is akin to an object instance or database record.

In REST, you can view it as a single resource inside a resource collection. A document’s state representation typically includes both fields with values and links to other related resources.

Use the “singular” name to denote the document resource archetype.

http://api.example.com/device-management/managed-devices/{device-id}
http://api.example.com/user-management/users/{id}
http://api.example.com/user-management/users/admin

2.1.2. collection

A collection resource is a server-managed directory of resources.

Clients may propose new resources to be added to a collection. However, it is up to the collection resource to choose whether to create a new resource or not.

A collection resource chooses what it wants to contain and also decides the URIs of each contained resource.

Use the “plural” name to denote the collection resource archetype.

/device-management/managed-devices
/user-management/users
/user-management/users/{id}/accounts

2.1.3. store

A store is a client-managed resource repository. A store resource lets an API client put resources in, get them back out, and decide when to delete them.

A store never generates new URIs. Instead, each stored resource has a URI. The URI was chosen by a client when the resource was initially put into the store.

Use “plural” name to denote store resource archetype.

/song-management/users/{id}/playlists

2.2. Consistency is the key

Use consistent resource naming conventions and URI formatting for minimum ambiguity and maximum readability and maintainability. You may implement the below design hints to achieve consistency:

2.2.1. Use forward slash (/) to indicate hierarchical relationships

The forward-slash (/) character is used in the path portion of the URI to indicate a hierarchical relationship between resources. e.g.

/device-management
/device-management/managed-devices
/device-management/managed-devices/{id}
/device-management/managed-devices/{id}/scripts
/device-management/managed-devices/{id}/scripts/{id}

2.2.2. Do not use trailing forward slash (/) in URIs

As the last character within a URI’s path, a forward slash (/) adds no semantic value and may confuse. It’s better to drop it from the URI.

http://api.example.com/device-management/managed-devices/ 
http://api.example.com/device-management/managed-devices         /*This is much better version*/

2.2.3. Use hyphens (-) to improve the readability of URIs

To make your URIs easy for people to scan and interpret, use the hyphen (-) character to improve the readability of names in long-path segments.

http://api.example.com/devicemanagement/manageddevices/
http://api.example.com/device-management/managed-devices 	/*This is much better version*/

2.2.4. Do not use underscores ( _ )

It’s possible to use an underscore in place of a hyphen to be used as a separator – But depending on the application’s font, it is possible that the underscore (_) character can either get partially obscured or completely hidden in some browsers or screens.

To avoid this confusion, use hyphens (-) instead of underscores ( _ ).

http://api.example.com/inventory-management/managed-entities/{id}/install-script-location  //More readable

http://api.example.com/inventory-management/managedEntities/{id}/installScriptLocation  //Less readable

2.2.5. Use lowercase letters in URIs

When convenient, lowercase letters should be consistently preferred in URI paths.

http://api.example.org/my-folder/my-doc       //1
HTTP://API.EXAMPLE.ORG/my-folder/my-doc     //2
http://api.example.org/My-Folder/my-doc       //3

In the above examples, 1 and 2 are the same, but 3 is not as it uses My-Folder in capital letters.

2.3. Do not use file extensions

File extensions look bad and do not add any advantage. Removing them decreases the length of URIs as well. No reason to keep them.

Apart from the above reason, if you want to highlight the media type of API using file extension, then you should rely on the media type, as communicated through the Content-Type header, to determine how to process the body’s content.

/device-management/managed-devices.xml  /*Do not use it*/

/device-management/managed-devices 	/*This is correct URI*/

2.4. Never use CRUD function names in URIs

We should not use URIs to indicate a CRUD function. URIs should only be used to identify the resources and not any action upon them uniquely.

We should use HTTP request methods to indicate which CRUD function is performed.

HTTP GET /device-management/managed-devices  			//Get all devices
HTTP POST /device-management/managed-devices  			//Create new Device

HTTP GET /device-management/managed-devices/{id}  		//Get device for given Id
HTTP PUT /device-management/managed-devices/{id}  		//Update device for given Id
HTTP DELETE /device-management/managed-devices/{id}  	//Delete device for given Id

2.5. Use query component to filter URI collection

Often, you will encounter requirements where you will need a collection of resources sorted, filtered, or limited based on some specific resource attribute.

For this requirement, do not create new APIs – instead, enable sorting, filtering, and pagination capabilities in resource collection API and pass the input parameters as query parameters. e.g.

/device-management/managed-devices
/device-management/managed-devices?region=USA
/device-management/managed-devices?region=USA&brand=XYZ
/device-management/managed-devices?region=USA&brand=XYZ&sort=installation-date

3. Do not Use Verbs in the URI

It is not correct to put the verbs in REST URIs. REST uses nouns to represent resources, and HTTP methods (GET, POST, PUT, DELETE, etc.) are then used to perform actions on those resources, effectively acting as verbs.

If we use verbs in the URI, we are most probably creating an RPC-style method call having a JSON or XML request/response format. It would be incorrect to call it REST.

/device-management/managed-devices/{id}/scripts/{id}/execute    //It is RPC, and not REST

In cases, where we need to perform some action that does not apply naturally to the definition of resources, we can create the custom URIs that can be considered nouns/resources and perform an action over them.

For example, instead of invoking /scripts/{id}/execute , we can create a resource for all scripts currently executing and submit a script to it if we want to execute a script.

/device-management/managed-devices/{id}/scripts/{id}/execute	//DON't DO THIS!

/device-management/managed-devices/{id}/scripts/{id}/status		//POST request with action=execute

You may also consider using the custom methods with colon(:) as described in custom methods by Google Cloud Docs, although many may find it in violation of REST principles.

4. Conclusion

In conclusion, we can say that resources are primarily nouns (objects) and we should avoid deviating from this principle. Anytime, we feel put verbs in the URI, we are creating an RPC call.

Leave a Comment

  1. Great article!

    This article currently mentions that there are 4 categories of resource archetypes (document, collection, store, and controller).

    However, there does not seem to be a section describing the controller archetype (I think I may have seen such section the last time I referred to this article, but I no longer can find it here).

    Where can we find more information in this article about the controller archetype?

    Reply
    • Hi Cris, after few serious discussions with more knowledgeable people and spending time on understanding the REST, I decided to remove it as I am convinced that the controller resource is just an RPC. Rather than creating controller archtype, rethink of your domain model and align it with existing three archtypes.

      For example, instead of creating POST /users/id/activate, consider using POST /activatedUsers with id in the request body. The first is RPC, and second is REST just like we create a new user.

      Reply
      • Hi Lokesh, great article, thanks for sharing. I don’t know why, but POST /activated-users looks more confusing than just POST /users/id/activate
        activated-users is not an entity in your database, it looks weird for someone who consumes my API to read that, sounds unnatural

        Reply
  2. Nice article. Thanks for it!

    I have a question though,
    What will be the pattern if I am having a resource that is identified by two unique attributes in isolation. And they are two different types. For eg lets say type will be id and string
    Say a resource can be identified either by the uuid or some other unique identifier

    http://www.domain.com/somepath/myresource?firstid=1
    http://www.domain.com/somepath/myresource?secondid=aname
    OR
    http://www.domain.com/somepath/myresource/1
    http://www.domain.com/somepath/myresource/aname

    Reply
    • Yeh, there can be cases when a resource has two unique attributes. For example, a user account can have a unique id and email. Still, the identifier shall remain only one. The other attribute can have its separate API.

      HTTP GET /api-path/users/id
      HTTP GET /api-path/users?email={email}
      HTTP POST /api-path/search/users [pass email id as search criteria]

      Reply
      • The semantics are different though no?

        1) HTTP GET /api-path/users?email={email} => “Give me the users with email X”
        2) HTTP GET /api-path/users/byEmail={email} => “Give me the user with email X”

        1) the result is an ARRAY, and returns 200 []
        2) the result is an OBJECT, and returns 404 on not found

        Reply
      • Thanks for this article. I don’t know how much time I speeded in discussions like this one 😉

        Today we tried to find a solution exactly for this UseCase. We have a kind of customer-contract-role table and we like this

        id – contractnumber – usernumber – role
        12 – con123 – user456 – contractpartner
        13 – con123 – user567 – insuredperson
        14 – con123 – user456 – paysthebill
        15 – con123 – user789 – receivespayment

        So we discussed the best design and thought, that the root element is called a relationship – and so we got

        GET /relationships/{id}
        GET /relationships/?contract={contractnumber}
        GET /relationships/?user={usernumber}
        GET /relationships/?contract={role}

        We also thought about

        GET /relationships/{id}
        GET /contracts/{contractnumber}/relationships
        GET /contracts/{contractnumber}/users/{usernumber}/relationships
        GET /users/{usernumber}/relationships/

        Here is relationships not the root element, but we are not sure, if we are more flexible, using this API because, we could to things like

        GET /contracts -> get all Numbers that have relationships
        GET /users/insuredpersons -> get all usernumbers of insured persons

        We are not sure about the best practice. In fact – every response is subelement of relationship. So does it have to be the root element?

        Even the aspect of sending 404’s if a special for example a special contract is not found seems to be a nice aspect. What is your opinion?

        Reply
  3. Given the URIs below, since there is no ‘all-devices’ convention, the service has to interpret the lack of a device-id as a “get all devices” request. But isn’t that opening up a potential exposure problem where if the front-end intends to send a device-id, but does not for some unintended reason (i.e. a bug), then the backend returns all devices, which in certain cases could be a security risk? What if this was a banking service and managed-devices was bank-accounts?? That wouldn’t be a good thing to be exposing all accounts.

    Is there some convention to prevent this behavior?

    http://api.example.com/device-management/managed-devices
    http://api.example.com/device-management/managed-devices/{device-id}

    Reply
    • Interesting thought! But that is the reason we should create custom media types. In this case, it could be “application/vnd.app.devices.v1+json” and “application/vnd.app.device.v1+json”. This way you can not go wrong. Even if the defect miss out the id field, mediatype will make sure you do not call another API accidentally.

      Reply
    • Yes, you would use authentication/authorization to control who can see what devices. If they aren’t allowed to see any devices, or a device by ID, you return 403 Permission denied. If they are allowed to see a subset, you return that list.

      Reply
  4. I wonder if there is any best practice when filtering by unassigned/null values?

    For example, let’s say I have a tasks domain, with the following endpoints

    * GET http://yoursite.com/api/v1/tasks
    * GET http://yoursite.com/api/v1/tasks/{id}
    * …

    Let’s say that, in the first example, I also have filtering, and I can filter by assignee (i.e. http://yoursite.com/api/v1/tasks?assignee=%5B1,2,3%5D).

    Although, I also want to see the unassigned tasks, which basically means that “assigne=null” in the DB. Therefore, in the current implementation, I’m doing something like http://yoursite.com/api/v1/tasks?assignee=%5B0%5D.

    I would say it’s not the worst solution, but I’m also wondering if there’s any better one? Thanks in advance.

    Reply
  5. Hi,
    A question on resource path and naming best practice.
    For example, a “Product API” path can be specified as below considering also its versioning –
    1. http://api.example.com/products/v1/products
    or
    2. http://api.example.com/product-management/v1/products
    or
    3. http://api.example.com/product-service/v1/products

    Option 1 creates ambiguity by duplication of the same “products” noun in the path, whereas option 2 and 3 appear to be over worded just to get around the problem of option 1 and not addressing the fact that it’s essentially a Product API that deals with product resources.
    Any thoughts on how to improve upon the above will be much appreciated. Thanks.

    Reply
  6. Hi,

    does it make sense to define a resource with an ’empty’ name, e.g.

    GET / HTTP/1.1
    Host: somehost.com
    ….

    I found something like this by my contributor; the server answers with some data from/about .net world. I requested actually some information (meta data) about the given API in the HEAD method.
    Is it a good solution?

    Reply
    • I find it pretty straight HTTP GET .../managed-devices?name=NOKIA shall return all devices with name containing Nokia (case-insensitive).

      Do you see any challenge in this?

      Reply
  7. Hi,

    I have a question regarding resources with validity period. What should be resource identifier?

    Let say I have a resource document-template with attributes code, template, validFrom, validTo, e.g.:
    [GET] /document-templates returns:
    [
    {“code”: “doc1”, “template”: “This is template of doc1 ${code}”, “validFrom”: “2020-07-10T15:00:00”, “validTo”: “2021-07-10T15:00:00”},
    {“code”: “doc1”, “template”: “This is newer version of template of doc1 ${code}”, “validFrom”: “2021-07-10T15:00:01”, “validTo”: null}
    ]

    In database there would be an artificial ID for every resource, and ofc it could be a resource identifier, but what if I don’t want to communicate with such resource identifier but with code instead?

    Is there some propper way to do it?
    e.g. /document-templates/{id} would return only currently valid resource and if not valid resource is required, then it could be accessible through
    /document-templates?code=doc1&validAt=2021-01-01T15:00:00

    or is it correct that resource has no resource identifier and there is no endpoint /document-templates/{id} and if you want a resource you have to search for it using query params?

    Thanks

    Reply
    • Are you simply referring to “versions” of documents?

      e.g. /document-template/versions/current

      for previous versions:
      /document-template/versions -> then have a list of previous document-templates versions on this page?

      This way, users can access the ‘current’ document-template always at /versions/current and if they require older versions they can simply select from a list of previous versions at /document-template/versions

      Obviously “versions” naming can be changed to whatever is more appropriate and I am just using it here to help communicate my point.

      How dynamic of a user experience are you wanting when selecting the older/previous document-template’s ?

      Reply
  8. Hello beautiful people 🙂

    I have a very common question which I can’t seem to find the answer for.

    I’m developing an API which has different areas to access; an admins area, a business area and users area.

    To be RESTful, I divided the code for each of them. But how do I design the routes? I don’t know if this approach is RESTful:

    1- something.com/api/admins/some-resource
    2- something.com/api/businesses/some-resource
    3- something.com/api/users/some-resource

    I would be more than happy to get some RESTful ideas, thank you 🙂

    Reply
    • Ideally, you are sending a token / authorisation key of some sort when hitting these apis.

      This should tie into the user in the backend and the server should be able to determine if the current user is an admin / business owner / typical user, so there really should just be one endpoint.

      GET something.com/api/some-resource.

      This should also determine user access not to resources as a whole, but also on a row by row basis.
      For example, if we have users that can post,
      DELETE/user/3/post/5
      should only occur if current user (via token) = owner (i.e. he/she is user 3).

      Reply
      • Hi Nick & Red,
        I also pay attention to this question. But I don’t think Nick’s answer is good.
        For changing password:
        If you are admin, it should be:
        + PUT: /users/{id}/password. // change password a user
        But if you are a user, who is changing his password, it should be:
        + PUT /{proper_word}/password. // change his password
        + Suggest: /auth/password

        For getting devices, If you are admin, it should be:
        + GET: /devices // get all devices
        + GET: /users/{id}/devices // get devices of a user
        But if you are a user, who get his device
        + GET /auth/devices

        Because we shouldn’t add {id} in this path and verify {id} again with token. And need a distinction for 2 purposes.
        + /users/{id} -> get id from path
        + /auth -> get id from token

        Reply
  9. Thanks for a great article!

    The resource archetypes don’t seem to clarify the differences between a collection and a store enough.
    I cant recognize how they differ through your URIs examples.

    Reply
    • A store is simply a collection of resources where we do not create any new resource in the system when we add something to the store; or we do not delete the resource from the system when we delete something from the store.

      You can think of a store as a shopping cart. When we can add and remove items from the cart, but actual items in the system do not change.

      Reply
  10. Hello friends, good morning

    I have a service as follows:

    PUT api.com/v1/sellers/1

    My problem is that this service is divided into several parts (status editing, name editing) because not all users have access to both services, I have to convert the service into two separate services. Is my method wrong ??

    PUT api.com/v1/sellers/1/status
    PUT api.com/v1/sellers/1/name

    If so, what is the correct method?

    Reply
      • Not OP, but I have a follow-up question.

        My situation is I have a resource–an item in a to-do list–that I want to perform different actions on. I have a button to increase priority and another button to decrease priority, both of which make a PUT request to my server.

        Right now, I have a single route that handles both actions, but given what you said above about not combining everything, I want to split it into several routes. I was thinking:

        PUT /items/{id}/increase-priority
        PUT /items/{id}/decrease-priority

        Does that sound okay to you? Above you wrote, “URIs should only be used to uniquely identify the resources and not any action upon them”, but I thought this might be an exception to that rule.

        Reply
        • PUT /items/{id}/priority sounds good enough to me. It’s a simple PUT API where we pass the latest number associated with the priority. Increase or decrease action is not important here. Even if the action is important, can’t we conclude that on the server-side by comparing the last stored priority in DB and the latest priority in the PUT request?

          A bigger number means increased priority and a lesser number means decreased priority.

          Reply
  11. Great article, thank you!
    Is there a common convention on how to differentiate an internal service API (used by own clients) from the external API (used by other applications)? For the external API, a stable contract and versioning is required. E.g.

    /internal/users
    vs.
    /api/v1/users

    Reply
    • Your case is a common one that in Laravel framework for PHP, it is handled by separating them in two files : web.php and api.php.
      The /internal/users route or endpoint is placed in web.php file, while /api/v1/users is placed in api.php.
      That is exactly I will do in my application using Laravel if I have that similar case.
      I don’t know how it is handled in Node.Js/Express, Java, ASP.net or other server side techs.

      Reply
    • i question the premise “for external api, a stable contract and versioning is required”. this seems to be a principle relevant to all apis, internal, external, whatever. i think the point of differentiation will be the resource name itself. i’m not convinced that the internal definition of user resource is same as the external definition. perhaps the uri needs more clarity as to the context under which the user is relevant (system user, app user, account user, etc.).. hth

      Reply
  12. I have a question regarding the rest api.
    When I have a product and a pricing domain and want to search for a product priced by pricing, how do I determine the uri? For example, I have a product with an id of 1 and pricing policies A and B. How do I get the uri to retrieve a product with id 1 priced through pricing A?

    1. Get /products/1/pricing/a
    2. Get /products/1/calculate/pricing/a

    The uri I was thinking about is the two above. Do you have any other suggestions?

    Reply
    • Hey wonjin what about query parameter? since you are filtering products it looks like a query parameter instead of a path parameter. I think that you return products with price=a and not pricing objects.

      GET /products/1?pricing=a

      Reply
  13. Great article I have a doubt I have the next endpoints:

    localhost:8080/api/product: //can return one or more products given an array of ids
    maps to : (@RequestBody List<IdContainer> companyId)

    localhost:8080/api/product: // return 0 or N products
    maps to: getProductsAll() ;

    How should I name each endpoint?

    Reply
    • HTTP GET localhost:8080/api/products shall return all products. For a set of product ids, pass them as query parameter to filter i.e. HTTP GET localhost:8080/api/products?ids=1,2,3,4

      Reply
  14. First URI: Courses seems to be the “collection” and year & semester are filters.

    1. /courses?year=2019&semester=fall

     
    Second URI: Grades belong to each student so they could be a collection under students. These are filtered by semester. Grades could also be their own collection (maybe you want to get all grades for visualization?). If it’s useful for grades to exist independent from students, you can make grades their own collection.

    1. /students/{id}/grades?year=2019&semester=fall
    2. /grades?student_id=123&year=2019&semester=fall

     
     

    Reply
  15. Hello,

    I’m a part-time developer, so I don’t keep up with these types of issues on a regular basis:

    Is there any discussion of an API standards definition for base object classes? For example, using this site’s terminology, will there ever be a standard Document for a Person base class, Location base class, etc?

    I find it interesting that almost every API I’ve developed has a couple of Document types that are reused to the point that I just copy/paste from my personal library when they’re needed. It would be nice if there was a universally recognized Person class that I could extend to meet my needs. More importantly, if I consumed someone else’s API, I would know that their RegisteredUser class, for example, extends this Person base class as well.

    In my opinion, APIs are THE next major advancement interoperability, personal devices/cool-bells-and-whistles notwithstanding. I think we’re still in the Wild West days of API technology but I would love to see some structure put around this, if it doesn’t already exist.

    Regards

    Zac

    Reply
  16. Really helpful article, thank you for taking your time and writing this.

    I have a question regarding naming of resource. If I want to fetch all the document-links in a document based on the type and version.

    Option 1: GET /documents/{type}/links?version=x&other=params

    Option 2: GET /documents/{type}/{version}/links?other=params

    Which endpoint naming should I follow?

    Reply
    • Hello. I think the better option will be GET http://documents/{document-id}/document-links?type=YOUR-TYPE&version=YOUR-VERSION
      Ps. You can simplify it to links from document-links, as you want.
      Hope, it will be helpful for you 🙂

      Reply
      • Hi.
         
        First. A very good guide on naming! Thanks.
         
        It is worth mentioning that hierarchical URLs may lead to problems if we (possibly at a later stage) want to enable filtering that span document-links of different document-ids. How can we represent such collection?  
         
        Alternative 1 – use some wildcard notation (*):
        http://documents/*/document-links?type=YOUR-TYPE&version=YOUR-VERSION

        The ‘*’ would need to be escaped and it is hard to find a good word that is intuitive. Maybe ‘null’ or ‘any’ would work?
         
        http://documents/null/document-links?type=YOUR-TYPE&version=YOUR-VERSION
        http://documents/any/document-links?type=YOUR-TYPE&version=YOUR-VERSION
         
        …but this is a bit unconventional and not intuitive. We will not get support in standards like OpenAPI and in many server-side implementation frameworks you would have to do some workaround because it does not fit with out-of-the-box functionality.
         
        Alternative 2 – have all collections on the same level
         
        http://documents/{id}
        http://document-links/{id}
         
        A search that spans multiple documents:
        http://document-links?type=YOUR-TYPE&version=YOUR-VERSION
         
        …and we would still be able to filter inside a specific document (documentId=abc123) by adding one more filter:
         
        http://document-links?documentId=abc123&type=YOUR-TYPE&version=YOUR-VERSION

        With this URL style we are able to introduce filtering based on fields without restricting them to be inside a specific document:
        http://document-links?type=YOUR-TYPE&version=YOUR-VERSION
         
        and are still able to filter within the scope of a specific document
        http://document-links?documentId=1type=YOUR-TYPE&version=YOUR-VERSION
         
        The hierarchical URL style is a bit easier to read but putting all collections on the same level makes it easier to extend the API with new filtering options and thus may be more suited for future requirements. 

        Reply
        • For your use case, I think you should provide both endpoints for document links.

          Documents being the key for access for document-links, the clients would call:
          /documents/{id}/document-links?type=YOUR-TYPE&version=YOUR-VERSION

          If they have specific type of links they are interested irrespective of , then they could call
          /document-links/{id}/document-links?documentId=abc123&type=YOUR-TYPE&version=YOUR-VERSION

          Allow both ways to access for clients depending on the need.

          Reply
  17. Hi there, Thanks for this rules list.
    I was wondering is there a naming convention for secured resources (endpoint protected by login/password ?)

    Reply
    • I think, those aforementioned conventions are enough, so as to comply with uniform interface constraint. The difference is to access secured resources, it must follow authentication mechanism which is a common practice.

      Reply
  18. Excuse me, I have a question about hierarchical relationships. How to define the number of layers, whether the parameter in the path are counted as one layer.
    For example, if “/device-management/managed-devices” has two hierarchies, then how many hierarchies in “/device-management/managed-devices/{id}“? three? or still two?

    Reply
    • I am curious to know why you want to count the number of layers in the URI. But, anyway, for the given case, there are 3 layers, as the forward slashes are used to define relationships.
      /device-management/managed-devices” is a collection
      while
      /device-management/managed-devices/{id}” is a document (per definition of resource archetype)

      Reply
  19. The problem with your logic is that collections aren’t the only resource that can act as a “directory”. All resources could potentially contain an infinite tree structure, so you might as well say all resources should have a trailing slash.

    A user could have a list of accounts and a separate URI for its address as you could consider addressing a separate resource. So you have /users/{userId}/accounts and /users/{userId}/address, therefore /users/{userId} is also a “directory” even though it only represents a singular resource, not a collection.

    Reply
  20. Hey,
    Regarding using query component to filter URI collection – how should I do it if I want only managed-devices that has region field exists? Not specific region=USA, only that region exists?

    Reply
  21. Hi,

    Great article thank you,
    I have a question for naming of resource:
    – There are two api in my project. One of them is sending single message to my service. The other one sending multiple message to my service. How to naming of them correctly ?

    I am using this one, but i am sure there is better way to naming them.

    POST: api/v1/tickets/message // Single message
    POST api/v1/tickets/messages // Multiple message

    Reply
    • Without understanding the whole use-case, it would not be correct to suggest appropriate naming. Though, one suggestion is to have only one API "POST api/v1/tickets/messages". It should be accept 1 to N messages.

      Reply
  22. How to name Controller ? eg. If i have resource(Folder) called ‘Customers’ inside this should I create controller called CustomerController or CustomersConstroller ?

    Reply
    • Focus on resource naming. Here, the primary resource is “Customer” so CustomerController is a better name. “Customers” is like a collection of “Customer” resources.

      Reply
    • If you need to keep the “/”, just replace it with something else for your request, as Admin suggested, and then when you retrieve it, parse it and replace that other symbol with “/” again. Example: “AB/124747 / B1” => in your request: “AB@124747 @ B1” => after reading it from your request, transform it back to: “AB/124747 / B1”

      Reply
  23. Should the store endpoint have the GET method that returns everything stored or is it up to the client to “remember” the ids?

    Reply
  24. How do you actually logout of the RESTful API? It is supposed to be stateless isn’t it.

    We have an endpoint /authenticate that requires Http Basic Auth credentials and generates bearer token.

    Client then must send the token with each subsequent request.

    The token is self contained and contains all the info to authenticate the user as well as limited validity.

    User can refresh the token prolonging the validity using the old, soon to expire token.

    Reply
  25. Login and Logout are verbs and should not be part of the resource URI.

    Instead consider something a like

    POST /auth
    DELETE /auth

    Login by POST-ing your credentials
    Logoff by DELETE-ing the auth

    If that doesn’t offer enough options for you, or to support additional admin resources or other use cases, this can be extended with additional URIs or query params:

    /auth?user={user_id}
    /auth?action=logout
    /auth/{auth_id}

    Session history for a user can be made available at:

    /user/{user_id}/auth

    To see currently active sessions:

    /user/{user_id}/auth?active=yes

    And the list goes on….

    Reply
  26. This page presents a common myth of CRUD & URIs.

    Roy Fielding writes:
    A REST API must not define fixed resource names or hierarchies (an obvious coupling of client and server). Servers must have the freedom to control their own namespace. Instead, allow servers to instruct clients on how to construct appropriate URIs, such as is done in HTML forms and URI templates, by defining those instructions within media types and link relations.
    https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

    Reply
  27. Let us say there is a single resource that we need to retrieve via two unique references (id or code) separately:
    *. Option 1.
    /v2/suppliers/{id}
    /v2/suppliers/{code}

    *. Option 2.
    /v2/suppliers/{id}
    /v2/suppliers?code={code}

    So from the above please let me know the preferred option or is there another way?

    Thanks.

    Reply
    • My question would be the reason why you have two URIs for the same resource? I’m here thinking about your database and how you have two unique identifiers for the same entity.

      TLDR: drop the /{code} or drop the /{id}. Use one of them. Query strings are meant to sort collections, not to get a single document.

      Reply
    • Option 1 cant work as the API wouldnt know which field to parse as. You could do
      /v2/suppliers/id/{id}
      /v2/suppliers/code/{code}

      Reply
      • No! What kind of resource is /v2/suppliers/id in this example? Or /v2/suppliers/code ? That’s right, there’s no good answer to that, because this is bad use of REST.
        Toni Maunde’s was just fine. Pick your identifier and stick with it.

        Reply
      • Let’s critical think it through,
        /v2/suppliers/id/{id}
        /v2/suppliers/code/{code}

        Is there a resource collection: /v2/suppliers/id or /v2/suppliers/code? Probably not — that’s an oops. {id} and {code} have no collection to belong to.

        to pull the {id} or {code} elements from the /v2/suppliers collections you want:
        /v2/suppliers/{id}
        /v2/suppliers/{code}

        Obviously, that creates a huge potential for ambiguity, which makes all things involved Client, server, and API consumer, work much harder to know which supplier the URI references.

        To simplify the engine (and the lives of your API consumers), pick one to identify the target resource and use the query pattern for all others. ID seems the better of the two in this case.

        Your API sould use:
        /v2/suppliers/{id}
        /v2/suppliers?code={code}

        Reply
        • What about really singular document URI:
          /v2/supplier/{id}
          /v2/suppliers?code=}code}

          And then you can also have a qualified collections, that does not mix itself up with single resource identifier:
          /v2/suppliers/top

          Or maybe better would be
          /v2/top-suppliers
          ?

          Reply
          • We’ve addressed this use-case by just using the following:

            /api/v1/users/{id}
            /api/v1/users?filter[code]=code

            The first is *the* method of returning a single resource by its primary identifier. This is usually a database primary key in our case.

            The second is the method of returning a list of resources, where any number of fields in the resource can be used to filter (there is also sorting and paging implemented as query options as well). In the case where “code” is referring to a single resource, a list of one item is returned.

  28. Great article!, how do you think access roles should be handled? for example we have two types of users (client and manager), a car can be created by each one of them, but the manager can create cars for other clients, and the client only for himself.

    We have the identity of the user from the token, so when designing the endpoint we are considering several options:

    1. one unique DTO that has the ids for the client and manager
    the endpoint would be the same
    api/cars/
    {
    “carName”:””,
    “ownerId”:””,
    “assignerId”:””
    }

    2. have different endpoints with different DTOs for each one, something like
    api/manager-management/cars
    with this DTO
    {
    “carName”:””,
    “ownerId”:””,
    “assignerId”:””
    }

    api/client-management/cars
    with this DTO
    {
    “carName”:””,
    “ownerId”:””
    }

    3. other….
    What do you think would be a good way to go?

    Reply
    • Use 1.

      Since in the frontend clients shouldn’t be able to set the ownerId and managers should, you must know the role for that (send it in the token).
      In the backend you can use the role in the token to validate the request, ex. if role is client the ownerid must be equal to assignerId if not the client could be hacking the API request.

      best regards.

      Reply
      • I agree with Jorge. Saying it in a different way:
        Your API needs to be able to authorize the user to perform the task. Otherwise, in option 2, anybody could call the manager API and still create cars for others. This could be done by a different client, or by someone maintaining your code (on purpose or by mistake). This is a huge security hole.
        Yeah, I know. It means you need to manage authorization code on the client and in the API.
        There are numerous options for this.
        role-based
        resource-based
        claims-based
        and more
        https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction?view=aspnetcore-2.2

        Reply
  29. If accountId is an unique global identifier on system , and I want to DELETE an account
    which is better ?

    DELETE /customers/{customerId}/accounts/{accountId}

    or

    DELETE /customers/accounts/{accountId}

    or

    DELETE /accounts/{accountId} ?

    My question is about if I must provide the customerId identifier ( although its not necessary to find the resource ,accountId is enough )

    Regards

    Reply
  30. What should I name my api if I want to populate some fields? For example, I would like to get the course with university field and the city field of university populated, what is the best practice to do it? Would GET api/course/populated=[university,university.city] work fine?

    Reply
    • You can use JSON object to send data to the API.
      Typical Spring boot example will look like below.

      As there we use User objects, you can use University Object, where you can include all the details you need.

      
      @PostMapping("/users")
          public User create(@RequestBody User user) {
              return userService.create(user);
          }
      

      This is how the User class looks like.
      You can use implementation like this on your language too.

      
      public class User {
          private int id;
          private String firstName;
          private String lastName;
          private String email;
      
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getFirstName() {
              return firstName;
          }
      
          public void setFirstName(String firstName) {
              this.firstName = firstName;
          }
      
          public String getLastName() {
              return lastName;
          }
      
          public void setLastName(String lastName) {
              this.lastName = lastName;
          }
      
          public String getEmail() {
              return email;
          }
      
          public void setEmail(String email) {
              this.email = email;
          }
      }
      
      Reply
    • I personally prefer to use the filter _fields in query parameter to limit the fields i want to return from the backend.

      Ex.
      GET api/courses/populated?_fields=university,university.city&_sort=-university.city
      I don’t understand the populated resource, maybe you can remove it.

      —–Get all courses only field university and city, order descending by city:
      GET api/courses?_fields=university,university.city&_sort=-university.city

      Reply
  31. http://api.example.com/song-management/users/{id}/playlists

    in php does this mean this?

    $_GET[‘song-management’] = ‘users’;
    $_GET[{id}] = ‘playlists’;

    or it’s actually..

    $_GET[‘page’] = ‘song-management’;
    $_GET[‘find’] = ‘users’;
    $_GET[‘select’] = {id};
    $_GET[‘show’] = ‘playlists’;

    i’m a bit lost in how the API’s URI should behave and how in php would think.

    Reply
    • In PHP the superglobal variable $_GET is used to access query string parameters. If you pass a URL with …/document?foo=bar then you would access the value of ‘foo’ with $_GET[‘foo’]. For managing URIs in a RESTful API written in PHP I recommend Slim Framework. There you define the routes individually or by using groups to form a hierarchy, including variables like {id} in your example. If you try to assign each part of the URI to an associative array you will lose flexibility. What happens when in your example we go to …/playlists/3/songs/7/history? You have to think of an index name for each part of that, which isn’t going to happen. More likely you’ll convert the entire URI into a string and use regular expressions to match predefined patterns that refer to a particular controller or controllers. But this would be better handled by a well-maintained framework, and Slim is the best I’ve found on PHP for lightweight RESTful APIs.

      Reply
  32. Any thoughts on how to handle something like tiers/grades/reliability levels in the API resource naming? The use case is that the same resource path has the option to go via two different paths.

    Functionally, both the paths do the same thing, such that hitting either path would have the same result (based on what API is expected to do), but the approach taken is different, which leads to some non-functional differences. It is not also an optional thing in the sense that a certain set of objects are meant to hit one-tier vs the other.

    A simple example being a logging API

    
    POST /v1/logs
    

    whose job is to dump the log into the system. But based on what tier was chosen, the availability/indexing/replication of logs may change.

    Should this tier information be incorporated as part of the url or should it be placed somewhere else?

    Something like

    
    POST /v1/tier1/logs
    
    Reply
    • POST are not intended to do what PUT does. A POST should always create a new resource where a PUT replaces an existing one.

      You’re free to use HTTP however you want, but if you go down that road, it’s no longer a REST API. I know if I come across a partner’s API that uses POST for every operation I tend to roll my eyes and it gives me the impression that they don’t quite know what they’re doing.

      If I were you, I would reconsider your stance on other HTTP verbs and study their purpose before committing to another non-REST, HTTP based API structure simply for “convenience”.

      Reply
      • > A POST should always create a new resource where a PUT replaces an existing one.

        There are edge cases. PUT need not result in actual resource creation. POST need not either.

        The more important invariant here is for PUT the subordinate resource identifier is *already known* by the client and the request is idempotent (meaning no new resources with new identities can ever be created with a PUT). POST is not idempotent and allows for the creation of new resources with new identities, with this is not necessary for its use.

        Reply
  33. Hi, I’m new to API.

    The example u given using the “/” where it seems to be using “path name” as “argument” or they are actually different directories?

    http://api.example.com/device-management/managed-devices/{device-id}

    meaning?
    api.example.com/index.php
    — device-management/index.php
    — — managed-devices/index.php
    — — — {device-id}/index.php

    i got a feeling im getting this wrong. (seriously wrong)

    Reply
  34. Thats a very opinionated response.

    Lewis couldn’t be more precise and logic about the functioning.

    Case 1:

    When you compose APIs in a navigating through the hierarchy you add the name of the resource without an initial slash because it will specified that your are defining the root path.

    Eg. If you define an URI for a resource an compose an HTML based on that that uri refers to the root so will be wrong

    Case 2:

    Developer define endpoint in his/her application without trailing slash and the wants to compile a full UIR with and ID to get sub-resources why you force to pass and ID with and / obviously this goes in the endpoint and you don’t want to define 2 endpoints one for listing and one for the sub-resource.

    Reply
  35. Hi,
    You say elsewhere that a resource should have a single logical URI. But the example “/customers/{customerId}/accounts/{accountId}” could also be written “/accounts/{accountId}”, given that account IDs should be unique within a bank. So the URI is not unique.

    Reply
    • Very good question. I will argue two things.

      1) Account Id and account number are two different things. Account numbers are NPI (sensitive) data so I will never put them in URI/URL. Account id is some generated number (like primary key) for account record.

      2) accountIds in both URIs can be different numbers. In /accounts/{accountId}, it will be primary key for account record, while in second case it will be primary key which holds the relationship between account and customer. (It is just one example).

      Please note I am saying these ids to be primary keys in respective tables just me make things more relatable. An id can be a derived value as well.

      Reply
  36. Excellent article, thank you for your hard work. Could you please provide a suggestion on:

    1. if we were to use a sensitive information like insuranceId as a search criteria, should the search be made GET or POST? (since everyone says that providing sensitive information in GET URL is not a good idea)

    2. How to handle GET search if we have complicated search criteria like:- searching on a user where first_name starts with a and age is greater than 17 and sort by last_name and show pages 3-7

    Reply
    • Search criteria should be public information, so if you’re searching on something that is not public, you might want to take another look at your requirements.
      What does sensitive mean? Your entire request should go via HTTPS anyway, so both the URL and the headers and the body would be encrypted. What you might be worried about is access logs where the URLs are logged (so if these are readable by a wider audience than the users of your system who already have access to that information, that might be a problem).

      How to handle GET if we have complicated search criteria:
      – you could have a min_age query parameter and pass in 18
      – you could have an age query paramater and allow values like ‘>17’
      – you could define a whole query language and pass the query in via a ‘query’ or ‘q’ parameter (like Google search)

      For pagination, check out http://otac0n.com/blog/2012/11/21/range-header-i-choose-you.html which I think is an elegant solution. Alternatively, have your API accept query parameters like the ‘$top’ and ‘$skip” parameters that OData declares so you can do /accounts?skip=10&top=20 to get results 10 through 30.

      Reply
  37. Thanks for the article! Been looking for something just like this! One part still confuses me however:

    You recommend above to use the following for collections:

    
    http://api.example.com/user-management/users/{id}
    http://api.example.com/user-management/users/admin
    

    But wouldn’t the system just look at /users/admin and think that “admin” is a user ID? This is a problem I’ve run into before, and is actually what I was researching when I came across this post 🙂 As soon as you allow resources/{variable}, you can no longer put any controller verbs or anything after /resources/ because it’ll get interpreted as your variable.

    One workaround for this seems to be changing around the order you list your URLs in in your backend, putting the catch-all URL looking for a variable last. But that’s always seemed a little hack-y and I don’t like that it imposes restrictions on how I might want to organize my URLs in the backend.

    Any suggestions for this? Is that workaround in fact standard practice?

    Thanks.

    Reply
    • It’s also possible to have only numeric IDs. So the server script could distinguish wether it’s an ID or a role/username/whatever. Of course that also imposes some additional limitations like prohibiting numeric and duplicate usernames. Having “/users/{username}” and “/users/{user-id}” wouldn’t seem too illogical though (but on the other hand: what’s the purpose?). For roles I’d use another way so no one gets confused. You could even use “/users/{numeric-userid}” and “/users/roles/{role-id}” on the same api if you wanted to.

      Reply
  38. Very good article, but I have a question,

    How could you do if you wanted an API that has the following functions with differents QueryString/ ParameterString/ Body/ Headers in a Messaging App?

    Example:

    SendMessage
    RedirectMessage
    RedirectToMessageBlock
    ForwardMessage
    UpdateAttributesInMessage

    All these functions are POST, but If you have a resource name “Message”, you can only have one POST,

    //yoursite.com/api/v1/messages

    Reply
      • Avoid uses of verbs in the resource. Also avoid api versioning in the resource.

        You can use PATCH to update specific attributes within a message, or PUT to update the entire message.

        As for the other options it depends on what your intentions are for each as to what type of solution to use. One option is to POST to the /message/{message_id} with a param such as action=forward as part of the payload.

        Reply
  39. Use “singular” name to denote document resource archetype.

    /api.example.com/device-management/managed-devices/{device-id}
    /api.example.com/user-management/users/{id}
    /api.example.com/user-management/users/admin

    But why plural is used ??

    Reply
  40. About Document archetype. We can do a get/put/delete/patch because we pass an “id”, it’s fine! Is there a situation where you can use a POST on a document. In which case we can we use POST on a Document archetype?

    If you see this presentation
    https://fr.slideshare.net/domenicdenicola/creating-truly-res-tful-apis

    What do you thing about =>page 4 Resource Archetype Document /users/0987/settings. Following you this example would not it be rather relative to controller Archetype ?

    What do you thing about => page 5 Archetypye collection : Could you explain the usage of PUT for a collection? It would be in the case where one wishes to replace ALL documents. My understanding A collection is not related to a specific ID. I doesn’t understand why the author mention PATCH and DELETE on an archetype collection also? Can you enlighten me on what is “true” of “false”

    Reply
  41. What are the rules for identifying a complex resource by separating a url path? For eg., one of my resource is grouped by 2 tokens. Token one can have many other tokens. The format I am using is:

    abc.com/tokens/5678/2345

    But there is no page for abc.com/tokens/5678. Should I club “5678/2345” as “5678-2345” or using any other character?

    Reply
  42. Is it RESTful to use URI with query parameters for performing a POST/PATCH to a subset of a collection? For example, if I want to set all the live hosts in the Miami datacenter to status “dead”:

    POST /hosts?datacenter=miami&status=live
    { “status”: “dead” }

    In the response, I would include all the updated hosts and new status. Some fields (e..g hostname, IP address, etc.) would not be modifiable via this method, which would lead a 4XX status code and appropriate error message.

    Reply
  43. I like to use this way of calling custom methods or what you call “controllers”.
    https://cloud.google.com/apis/design/custom_methods

    Basically when you need to perform a special action over a resource that can’t be represented you add the action after a colon at the end.

    For example I have this /groups/{id} resource I want to clone, but there is no such method or similar in HTTP, I could just use POST /groups/{id} with the fetched data of the group I want to clone but If I want to execute that in just one action, so I add /groups/{id}:clone .

    As stated in the link, there are some considerations when doing this, like using only POST method, but is more clear this way when are you executing something or not.

    Also this maps seamlessly with auth scopes. In this case my scope would be ‘groups:clone’.

    Thanks for all this huge documentation, it is really helpful.

    Reply
  44. Wich pattern i can use in a model with a composite key?

    I have an entity called leaderboard that has a key composed by frontend + packagename, how would my endpoint for get a single leaderboard?

    
    GET /leaderboard/{frontend}?packagename={packagename} ?
    GET /leaderboard/{frontend}-{packagename} ?
    GET /leaderboard/{frontend}/{packagename} ?
    
    Reply
    • 1) If a frontend has many packages (in sinle screen) then GET /leaderboard/{frontend}-{packagename} does not make sense to me. Same for /leaderboard/{frontend}/{packagename}. I would not like to make N API calls to load one screen.
      2) GET /leaderboard/{frontend}?packagename={packagename} looks more flexible to me because it gives you freedom to fetch 1-N packages in single call.

      I am expressing my thoughts purely on assumption of your system. I may be incorrect, so please research.

      Reply
  45. Hi,

    I have a requirement where within the hierarchical data one of the resource is a collection instead of single id. What is the best way of modelling it? Example below

    Requirement: Give me access control list for specific list of roles. One way of modelling it is to allow only one role id, let the consumer call the API multiple times.
    access-management/roles/{id}/acls

    However, I want to receive all roles in one request and the response to be then able to return ACLs for all roles. Something along the lines
    access-management/roles/{ids}/acls

    What is the best way to model the API to adhere to REST and URI guidelines?

    Reply
    • Though, multiple “ACL”s can belong to single role (as sub-collection), still I see it independent resource different from “Role”. So model them accordingly.

      I will suggest to create following APIs.

      HTTP GET access-management/roles
      HTTP GET access-management/roles/{id}
      HTTP GET access-management/roles/{id}/acls

      HTTP GET access-management/acls
      HTTP GET access-management/acls/{id}

      So, to reply your query, you can use “access-management/roles” having response like this:

      <roles>
      	<role id="1" uri="/access-management/roles/1">
      		<name>admin</name>
      		<enabled>true</enabled>
      		<acls>
      			<acl id="11" uri="/access-management/acls/11">
      				<name>ADD</name>
      				<enabled>true</enabled>
      			</acl>
      			<acl id="12" uri="/access-management/acls/12">
      				<name>DELETE</name>
      				<enabled>true</enabled>
      			</acl>
      		</acls>
      	</role>
      	<role id="2" uri="/access-management/roles/2">
      		<name>user</name>
      		<enabled>true</enabled>
      		<acls>
      			<acl id="11" uri="/access-management/acls/11">
      				<name>ADD</name>
      				<enabled>true</enabled>
      			</acl>
      		</acls>
      	</role>
      </roles>

      Here you can iterate all rows and find related ACLs. If you want to fetch full details of any single ACL the follow its uri attribute. Feel free to add/remove fields as per design.

      Reply
      • In your API suggestions you list “HTTP GET access-management/roles/{id}/acls” and “access-management/acls” separately. That’s two distinct URI’s for the same collection resource “ACLs”

        Reply
  46. Hi,
    Great article, but i have a question.
    How can I get the filtered list of managed-devices?
    My first idea was something like this:

    HTTP POST http://api.example.com/device-management/managed-devices

    with my list of device-id in body, but you wrote that this URI is for create.
    So what should i do then?

    Thanks.

    Reply
    • 1) In REST, HTTP POST is more close to “create”. I will suggest to use “HTTP GET” with query parameters.
      2) Why somebody will filter a collection by device ids? He will use search functionality (text box) directly.
      3) Filtering make sense on other parameters e.g. locations, IP ranges etc. For those usecases, use query parameters. e.g.

      HTTP GET http://api.example.com/device-management/managed-devices?states=CA,LS
      HTTP GET http://api.example.com/device-management/managed-devices?ip-range=127-0-0-1,127-0-0-10
      HTTP GET http://api.example.com/device-management/managed-devices?routes=route1,route2,route3

      This approach has benefit that you can use/share these URLs (e.g. bookmarks) again and again over the time.

      Reply
      • In my interface I have a list of vehicles names and checkbox next to each of them. When a user chooses few and want to e.g. display them on map, I would like to make only one request for the whole data.

        Thanks for the answer, I will pass list of ids in querystring.

        Reply

Leave a Comment