Masonite API is a package designed to make it dead simple to add externally facing API's with various types of authentication and permission scopes. There is a new concept called "API Resources" which you will use to build your specific endpoint. In this documentation we will walk through how to make a User Resource so we can walk through the various moving parts.
Install the PyPi package:
Add the Service Provider to your provider list in config/providers.py
:
Masonite API comes with an api
guard which you can use to handle fetching users as you would in a normal web app using the request.user()
method or auth()
function.
You can add this config to your config/auth.py
:
We can create API Resources by building them wherever you want to. In this documentation we will put them in app/resources
. We just need to create a simple resource class which inherits from api.resources.Resource
.
You should also specify a model by importing it and specifying the model attribute:
Lastly for simplicity sake, we can specify a serializer. This will take any Orator models or Collection we return and serialize them into a dictionary which will be picked up via the JsonResponseMiddleware
.
Awesome! Now we are ready to go!
Our resource has several routes that it will build based on the information we provided so let's import it into our web.py file so it will build the routes for us. We can also specify the base route which will be used for all routes.
This will build a route list that looks like:
We can also specify the routes that we want to create by setting the methods
attribute:
This will only build those routes dependent on the methods specified:
You can easily add middleware to routes by specifying them using the middleware
method:
Or of course you can add the middleware to a group:
You may want to override some methods that are used internally by the API endpoint to return the necessary data.
The methods are: create, index, show, update, delete.
You can check the repo on how these methods are used and how you should modify them but it's pretty straight forward. The show method is used to show all the results are is what is returned when the endpoint is something like /api/user
.
Overriding a method will look something like:
This will not only return all the results where active is 1
. Keep in mind as well that these methods are resolved via the container so we can use dependency injection:
The index method is ran when getting all records with: POST /api/user
.
Currently our response may look something like:
You might not want to display all the model attributes like id
, email
or password
. We can choose to remove these with the without
class attribute:
Now our response will look like:
Yes, it's that easy.
For any API package to be awesome, it really needs to have powerful and simple authentication.
We can specify our authentication for our specific endpoint by inheriting from a class:
Now our endpoint is being JWT Authentication. If we hit our endpoint now in the browser by sending a GET request to http://localhost:8000/api/user
. we will see:
Great! If we specify a token by hitting http://localhost:8000/api/user?token=1234 then we will see a different error:
We can easily create an endpoint for giving out and refreshing API tokens by adding some routes to our web.py:
Now we can make a POST request to http://localhost:8000/token
with your username and password which will give us back a JWT token.
POST
http://localhost:8000/token
Use this endpoint to retrieve new tokens using a username and password. The username and password will default to your regular authentication model located in config/auth.py
.
we can now use this token to make our calls by using that new token. This token is good for 5 minutes and it will be required to refresh the token once expired.
Once our JWT token expires, we need to refresh it by sending our old expired token to
POST
http://localhost:8000/token/refresh
You can also specify any permission scopes you need. Most of the time some API endpoints will need to have more restrictive scopes than others. We can specify whatever scopes we need to with the scopes
attribute as well as inheriting another class.
Now all API endpoint will need to have the correct permission scopes. Hitting this API endpoint now will result in:
We can request the scopes we need by spending a POST request back to the endpoint with a scopes input. The scopes should be comma separated. The request should look like:
This will generate a new token with the correct permission scopes.
Filter scopes is an extension of the scopes above. It will filter the data based on the scope level. This is useful if you want a specific scope to have more permission than other scopes.
To do this we can extend our resource with the FilterScopes
class:
Now when you make this request it will return the columns in accordance with the user scopes.
Authentication classes are extremely simple classes. They just need to inherit the BaseAuthentication class and contain 2 methods: authenticate
and get_token
.
This method is resolved via the container. it is important to note that if the authenticate is successful, it should not return anything. Masonite will only look for exceptions thrown in this method and then correlate an error response to it.
For example if we want to return an error because the token was not found, we can raise that exception:
Which will result in an error response:
This method is used to return a dictionary which is the decrypted version of the token. So however your authentication class should decrypt the token, it needs to do so in this method. This all depends on how the token was encrypted in the first place. This may look something like:
Serializers are simple classes with a single serialize
method on them. The serialize
method takes a single parameter which is the response returned from one of the create, index, show, update, delete methods.
For example if we return something like a model instance:
We will receive this output into our serialize method:
which we can then serialize how we need to and return it. Here is an example of a JSON serializer:
notice we take the response and then convert that response into a dictionary depending on the response type.
Once we convert to a dictionary here, the JSONResponseMiddleware
will pick that dictionary up and return a JSON response.
We have access to a few builtin methods from anywhere in our resource.
The token can be in several different forms coming into the request. It can be either a JWT token or a regular token or some other form of token. Either way it needs to be "unencrypted" in order for it to be used and since the authentication class is responsible for un-encrypting it, it is the responsibility of the authentication class to get the token.
This method is only available when inheriting from an authentication class like JWTAuthentication
which requires this method and should return the un-encrypted version of the token
There are a few locations the token can be. The two main locations are in the query string itself (/endpoint?token=123..
) or inside a header (the HTTP_AUTHORIZATION
header). We can get the token regardless of where it is with the fetch_token()
method:
Name | Type | Description |
---|---|---|
Name | Type | Description |
---|---|---|
username
string
The username to authenticate using your authentication model
password
string
The password to authenticate using your authentication model
token
string
The expired JWT token