46

What's the best/restful way to design an API endpoint for checking existence of resources?

For example there is a user database. While new user tries to sign up I want to check if email has been used on-the-fly.

My idea is: POST /user/exists and payload would be something like {"email": "foo@bar.com"}. The response would be either 200 OK or 409 Conflict.

Is this a proper way?

Thanks!

x1a0
  • 9,984
  • 5
  • 22
  • 30
  • You could do that, but Tragedian's solution is better because a) you don't have to construct a body, b) the response is cacheable and c) it accurately describes the request as being SAFE by using a GET. – Darrel Miller Jan 13 '14 at 12:32

4 Answers4

49

HEAD is the most effecient for existence checks:

HEAD /users/{username}

Request a user's path, and return a 200 if they exist, or a 404 if they don't.

Mind you, you probably don't want to be exposing endpoints that check email addresses. It opens a security and privacy hole. Usernames that are already publicly displayed around a site, like on reddit, could be ok.

Baz
  • 2,167
  • 1
  • 22
  • 27
  • 1
    I think this will only work if username is the "primary key" for users. The most common is an API have a `GET /users/{id}`, but this resource will conflict with a `HEAD /users/{username}`, because in the theory the HEAD method asks for a response identical to that of a GET request. – Dherik Feb 06 '18 at 15:06
  • 1
    Note that if such requests go from a web browser (e.g. AJAX calls), JS console will get polluted with 404 errors. While non-tech users won't see them, and Google says such errors don't affect SEO (https://support.google.com/webmasters/answer/35120), that's still a bit less than ideal.. :( – Jan Żankowski Jul 04 '18 at 10:25
  • Considering that the normal READ is done as `/user/id`. and the task at hand is to check for existence of account for "foo@bar.com". my answer would be, it depends: 1. if id is email; as `/user/jdoe@gmail.com` then solution provided by @Baz makes sense `HEAD /users/{username}`. (HTTP docs) 2. when id is the acount ID (integer); as `/user/456734` then solution provided by @x1a0 `POST /user/exists` makes sense. I would just change it to a "GET" as `GET /user/exists?email=foo@bar.com` since you are doing a READ (asking for a yes/no answer), you are not trying to "create" any resorce. – Chaim Klar Jul 19 '19 at 18:25
  • 2
    The head method is simple but cannot return meaningful error message, additional information to the client about what go wrong (sometimes 4xx 5xx error code is not sufficient) – Frankie Hung Oct 10 '19 at 03:22
6
GET /users?email=foo@bar.com

This is a basic search query: find me the users which have the email address specified. Respond with an empty collection if no users exist, or respond with the users which match the condition.

Paul Turner
  • 38,949
  • 15
  • 102
  • 166
  • 1
    You could return a 204 and no body or 404 to indicate that none were found. That would save having to parse the body in the negative case. – Darrel Miller Jan 13 '14 at 12:30
  • 1
    I'd argue against the use of 404 here: the users resource does exist and handled the request acceptably. A 204 is closer to the truth, but I personally would find an empty collection easier to parse. – Paul Turner Jan 13 '14 at 13:24
  • 3
    Sure `/Users` exists but that's not the resource that I'm trying to GET. I'm trying to GET `/users?email=foo@bar.com` and that resource does not exists. Query params are just as much part of resource identification as path params. – Darrel Miller Jan 13 '14 at 13:29
  • 2
    Tragedian/Darrel , I'd say you're both potentially right; it depends on whether the OP defines the media type of the resource at "/users?email=foo@bar.com" as representing "the user with email address foo@bar.com" or "the collection of users with email address foo@bar.com". If the former, then 404 is appropriate, if the latter, then 204. – Andy Dennie Jan 14 '14 at 14:57
  • @AndyDennie s/media type/semantics of – Darrel Miller Jan 14 '14 at 23:13
  • @Darrel I agree, that would have been more correct. All media types for a resource should adhere to a common semantic. – Andy Dennie Jan 15 '14 at 13:45
  • 3
    This is not a great solution because data should *not* be returned with the request, just a simple boolean. – Baz Nov 28 '16 at 08:19
6

I believe the proper way to just check for existence is to use a HEAD verb for whatever resource you would normally get with a GET request.

I recently came across a situation where I wanted to check the existence of a potentially large video file on the server. I didn't want the server to try and start streaming the bytes to any client so I implemented a HEAD response that just returned the headers that the client would receive when doing a GET request for that video.

You can check out the W3 specification here or read this blog post about practical uses of the HEAD verb.

I think this is awesome because you don't have to think about how to form your route any differently from a normal RESTful route in order to check for the existence of any resource, Whether that's a file or a typical resource, like a user or something.

arjabbar
  • 6,044
  • 4
  • 30
  • 46
3

I prefer:

HEAD /users/email/foo@bar.com

Explanation: You are trying to find through all the users someone that are using the e-mail foo@bar.com. I'm assuming here that the e-mail is not the key of your resource and you would like to have some flexibility on your endpoint, because if you need another endpoint to check availability of another information from the user (like username, number, etc) , this approach can fit very well:

HEAD /users/email/foo@bar.com
HEAD /users/username/foobar
HEAD /users/number/56534324

As response, you only need to return 200 (exists, so it's not available) or 404 (not exists, so it's available) as http code response.

You can also use:

HEAD /emails/foo@bar.com

if the HEAD /users/email/foo@bar.com conflict with an existing rest resource, like a GET /users/email/foo@bar.com with a different business rule. As described on Mozilla's documentation:

The HEAD method asks for a response identical to that of a GET request, but without the response body.*.

So, have a GET and HEAD with different rules is not good.

A HEAD /users/foo@bar.com is a good option too if the e-mail is the "key" of the users, because you (probably) have a GET /users/foo@bar.com.

Dherik
  • 17,757
  • 11
  • 115
  • 164
  • I believe you meant '200 (if it's available) or 404 (if it's not available)' – Eduardo Mayer Dec 05 '18 at 18:16
  • Hi! The 200 is returned when the user with some e-mail is found. So, if there is a user with the e-mail, the e-mail is not available to be used. – Dherik Dec 05 '18 at 18:51