An Exercise in RESTful Thinking: Implementing a Search Endpoint
Our fancy new RESTful API does have a endpoint POST /searchPosts
. Now we are sad as it means we aren't really doing REST at all. Let's see what we can do about that.
First lets see what that endpoint actually means in terms of RESTful design: "Create a SearchPosts". Now that doesn't really make sense now does it? So what went wrong here?
Never Have Verbs in Your Paths
Having verbs like search
in your endpoints is a clear indicator that our API does RPC instead of REST. RPC pretty much is a function call across networks. What we do in this example is call a method searchPosts
. We are literally defining your own custom function to call remotely.
In REST there is only a very small, fixed set of verbs/functions:
- POST/create
- GET
- PUT/replace
- PATCH/update
- DELETE
Since the verbs/functions are given, all that's left for us is define the resources/objects they operate on. Resources are always nouns, so the simplest step in the right direction would be to nounify searchPosts
to /postSearch
.
So now what our endpoint POST /postSearch
does, is to "Create a PostSearch". Sounds about right.
Most Paths Should Be Plural
In RESTful API design we know two kinds of resources: singletons and collections. Singleton resources are things you will always have exactly one of. A user probably has exactly one profile. A store probably only has exactly one shopping cart per user. Stuff like that.
But do we really have exactly one PostSearch in our example? That would mean we can never create another search after we created the first one.
We want a collection then. That would make our path look like this: /postSearches
.
Going about it this way also brings a few other benefits to the table:
If we ever wanted to persist searches (and their results?) we could add GET /postSearches
to retrieve all post searches that were ever done. Add /postSearches/{postSearchId}
to make it easy to work with single PostSearches and have all the options.
A Few Open Questions
Now you might say wait a second - a search does not change the application state and as such shouldn't use POST!
.
And you would be absolutley right. Yet our examplary search might be complicated and not easily expressed via query parameters. Since GET requests cannot contain a body (practically) we might be forced to use POST to send a complex payload. It also future-proofs our endpoint somewhat as I explained above.
By the way: nobody says a POST request must change application state. It is considered 'non-safe', though, which means search engines etc. will not just willy-nilly request POST endpoints.
Okay, but we are creating a PostSearch here. Yet we would have to return results, what about that?
Good point. But resources are allowed to have relationships between them. That allows our PostSearch resource to define relationships to posts which should be a-okay to include in a PostSearch resource.
In Conclusion
I guess the takeaway is this: When in doubt think about what your endpoint actually means in a REST API sense. Try to make a sentence like we did above. If the result makes sense you probably are moving in the right direction.
Update 15.12.2020: Turns out there is a pretty good, in-depth article on stackoverflow.blog that goes into everything I wrote about and more: https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/.