In my Flask-RESTful API, imagine I have two objects, users and cities. It is a 1-to-many relationship. Now when I create my API and add resources to it, all I can seem to do is map very easy and general URLs to them. Here is the code (with useless stuff not included):
class UserAPI(Resource): # The API class that handles a single user def __init__(self): # Initialize def get(self, id): # GET requests def put(self, id): # PUT requests def delete(self, id): # DELETE requests class UserListAPI(Resource): # The API class that handles the whole group of Users def __init__(self): def get(self): def post(self): api.add_resource(UserAPI, '/api/user/<int:id>', endpoint='user') api.add_resource(UserListAPI, '/api/users/', endpoint='users') class CityAPI(Resource): def __init__(self): def get(self, id): def put(self, id): def delete(self, id): class CityListAPI(Resource): def __init__(self): def get(self): def post(self): api.add_resource(CityListAPI, '/api/cities/', endpoint='cities') api.add_resource(CityAPI, '/api/city/<int:id>', endpoint='city')
As you can see, I can do everything I want to implement a very basic functionality. I can GET, POST, PUT, and DELETE both objects. However, my goal is two-fold:
(1) To be able to request with other parameters like city name instead of just city id. It would look something like:api.add_resource(CityAPI, '/api/city/<string:name>', endpoint='city')
except it wouldn't throw me this error:
AssertionError: View function mapping is overwriting an existing endpoint function
(2) To be able to combine the two Resources in a Request. Say I wanted to get all the users associated with some city. In REST URLs, it should look something like:/api/cities/<int:id>/users
How do I do that with Flask? What endpoint do I map it to?
Basically, I'm looking for ways to take my API from basic to useful.
Often, each REST API offers multiple endpoints from which you can get the data.
Flask-Restful is handy but not absolutely necessary. You can go a long way with just blueprints and class-based views. Flask extensions for all needs are plentiful and they're easy to write if you need custom functionality.
Flask is a highly flexible Python web framework built with a small core and easy-to-extend philosophy. Flask-Restful is an extension of Flask that offers extension to enable writing a clean object-oriented code for fast API development.
There is no way of programmatically discovering REST services as they do not have a standard registry service. Apart from doing something insane brute-force search there is no way of finding the right URLs ( not to mention the right parameters). So the only option is documenting your API.
We are building a simple RESTful API, thus Flask is perfect for our needs. Per its official definition, Flask is a lightweight WSGI web application framework. It is designed to make getting started quick and easy. Flask offers suggestions, but doesn’t enforce any dependencies or a project layout.
"Flask allows Python developers to create lightweight RESTful APIs." Why Python? Why Flask? Why Python? Nowadays, choosing Python to develop applications is becoming a very popular choice.
As mentioned above, Flask is very extensible, and Flask-RESTful is an extension for Flask that adds support for quickly building REST APIs. It is a lightweight abstraction that works with your existing ORM/libraries. Flask-RESTful encourages best practices with minimal setup.
Adds a resource to the api. urls ( str) – one or more url routes to match for the resource, standard flask routing rules apply. Any url variables will be passed to the resource method as args. resource_class_args ( tuple) – args to be forwarded to the constructor of the resource.
Your are making two mistakes.
First, Flask-RESTful leads you to think that a resource is implemented with a single URL. In reality, you can have many different URLs that return resources of the same type. In Flask-RESTful you will need to create a different Resource
subclass for each URL, but conceptually those URLs belong to the same resource. Note that you have, in fact, created two instances per resource already to handle the list and the individual requests.
The second mistake that you are making is that you expect the client to know all the URLs in your API. This is not a good way to build APIs, ideally the client only knows a few top-level URLs and then discovers the rest from data in the responses from the top-level ones.
In your API you may want to expose the /api/users
and /api/cities
as top-level APIs. The URLs to individual cities and users will be included in the responses. For example, if I invoke http://example.com/api/users
to get the list of users I may get this response:
{ "users": [ { "url": "http://example.com/api/user/1", "name": "John Smith", "city": "http://example.com/api/city/35" }, { "url": "http://example.com/api/user/2", "name": "Susan Jones", "city": "http://example.com/api/city/2" } ] }
Note that the JSON representation of a user includes the URL for that user, and also the URL for the city. The client does not need to know how to build these, because they are given to it.
The URL for a city is /api/city/<id>
, and the URL to get the complete list of cities is /api/cities
, as you have it defined.
If you also need to search for cities by their name you can extend the "cities" endpoint to do that. For example, you could have URLs in the form /api/cities/<name>
return the list of cities that match the search term given as <name>
.
With Flask-RESTful you will need to define a new Resource
subclass for that, for example:
class CitiesByNameAPI(Resource): def __init__(self): # ... def get(self, name): # ... api.add_resource(CitiesByNameAPI, '/api/cities/<name>', endpoint = 'cities_by_name')
When the client asks for a city it should get a response that includes a URL to get the users in that city. For example, let's say that from the /api/users
response above I want to find out about the city of the first user. So now I send a request to http://example/api/city/35
, and I get back the following JSON response:
{ "url": "http://example.com/api/city/35", "name": "San Francisco", "users": "http://example/com/api/city/35/users" }
Now I have the city, and that gave me a URL that I can use to get all the users in that city.
Note that it does not matter that your URLs are ugly or hard to construct, because the client never needs to build most of these from scratch, it just gets them from the server. This also enables you to change the format of the URLs in the future.
To implement the URL that gets users by city you add yet another Resource
subclass:
class UsersByCityAPI(Resource): def __init__(self): # ... def get(self, id): # ... api.add_resource(UsersByCityAPI, '/api/cities/<int:id>/users', endpoint = 'users_by_city')
I hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With