Building The API

Last updated 6 months ago

Introduction

Now that we have created an API resource in Creating Resources, we can now get down to making the API more usable. Although having CRUD operations is great, we may want to limit or expand what our resource can do.

Getting Started

Whenever we added our model to the resource, the API URI was the name our model but plural.

We added the app.User.User class so our API route is api/users. If we added a app.Profile.Profile class then our API route would be api/profiles. More about changing this in a bit.

By default, this resource:

from entry.api.Resource import Resource
from entry.api.JsonSerialize import JsonSerialize
from app.User import User
class UserResource(Resource, JsonSerialize):
model = User

will have access to:

GET /api/users

Which reads all the data of the model.

GET /api/users/1

Which gets only a single record from the model.

POST /api/users

Used to create records.

It's important to note that anytime Masonite Entry catches a password input coming in from the API, it will automatically encrypt it using bcrypt. So anytime you create a new user and store a plain text password via the API, it will be encrypted with the same encryption method that Masonite uses before it is stored in the database.

PUT /api/users/1

Used to update information on a user.

DELETE /api/users/1

Used for deleting an individual user.

Just by that little bit of code opens up some really powerful features. Let's dive in a little deeper and start customizing our API.

Creating Users

Let's start by creating some users so we have some data to work with. If you already have data in your table then you can skip to the next section.

It's highly recommended you use an API development environment tool like Postman. Open up Postman and send a POST request to localhost:8000/api/users and pass the needed parameters in the Body of Postman and ensure that x-www-form-urlencoded is checked.

If you're using the default User model then that should only be the name, email and password.

Remember that anytime we pass a "password" input into the API, Masonite Entry will encrypt it for us before it reaches the database. If successful, we should see the record returned back to us.

Excluding Fields

Notice that our User model is exposing our password and remember_token. Our password should not be exposed for obvious reasons but our remember_token is our session cookie and absolutely should not be exposed; although this cookie is normally encrypted in the browser and only decrypted using your Masonite key so it's not too detrimental if it's leaked out. But let's exclude those fields:

from entry.api.Resource import Resource
from entry.api.JsonSerialize import JsonSerialize
from app.User import User
class UserResource(Resource, JsonSerialize):
model = User
exclude = ['password', 'remember_token']

Perfect. Now whenever we make an API call, our results won't show fields we don't want.

Changing The URL

The default URL is the name of our model class, both singular and plural. We might not want that. We could want to change the url because the model name is exposed if you find that is a security risk or because you need your API to be more friendly. We can change it simply by passing in a url attribute.

from entry.api.Resource import Resource
from entry.api.JsonSerialize import JsonSerialize
from app.User import User
class UserResource(Resource, JsonSerialize):
model = User
exclude = ['password', 'remember_token']
url = '/api/profile'

Now all requests can be made to /api/profile instead of /api/user. Make sure you specify it as a singular because Masonite Entry to make it plural by adding an "s" at the end.

Specifying CRUD

Not all resources may need all CRUD operations. We can specify what our resource needs to handle:

from entry.api.Resource import Resource
from entry.api.JsonSerialize import JsonSerialize
from app.User import User
class UserResource(Resource, JsonSerialize):
model = User
exclude = ['password', 'remember_token']
url = '/api/profile'
methods = ['create', 'read']

Now this resource will not be able to update or delete but only create and read.

Data Wrapping

By default, Masonite Entry will wrap all JSON responses in a data envelope. A typical API response might look like this:

[
data: [
{
"id": 1,
"name": "Joseph",
"email": "email1@gmail.com"
}
]
]

This is to prevent a well known JSON security risk. Because this feature was put in place to combat that security risk, it is not ideal to remove it but if you find that you need to then you can remove data wrapping easily:

from entry.api.Resource import Resource
from entry.api.JsonSerialize import JsonSerialize
from app.User import User
class UserResource(Resource, JsonSerialize):
model = User
data_wrap = False

Your JSON responses will now be unwrapped and will look like this:

[
{
"id": 1,
"name": "Joseph",
"email": "email@gmail.com",
}
]

Adding Prefixes

Many RESTful API systems have URI prefixes that are usually the version number of the API such as v1 or v3.3. You can do so by specifying a url_prefix attribute

from entry.api.Resource import Resource
from entry.api.JsonSerialize import JsonSerialize
from app.User import User
class UserResource(Resource, JsonSerialize):
model = User
url_prefix = 'v1'

Now this resource will have the /v1/api/users URI endpoint. This doesn't have to be a version number of course but this is what it might be most useful for.

Read Only Fields

Some fields should not be updated. You can simply specify which fields should be read only by passing in a read_only_fields attribute.

from entry.api.Resource import Resource
from entry.api.JsonSerialize import JsonSerialize
from app.User import User
class UserResource(Resource, JsonSerialize):
model = User
read_only_fields = ['email', 'is_employee']

Next Steps

This alone will be able to take you a long way into making your RESTful API. You can add additional functionality by adding something like token authentication.

Read the documentation on Token Authentication to learn more.