Quickstart
It’s time to write your first advanced REST API. This guide assumes you have a working understanding of FastAPI, and that you have already installed both FastAPI and FastAPI-JSONAPI. If not, then follow the steps in the Installation section.
In this section you will learn basic usage of FastAPI-JSONAPI around a small tutorial that uses the SQLAlchemy data layer. This tutorial shows you an example of a user and their computers.
Advanced example
An example of FastAPI-JSONAPI API looks like this:
"""Route creator"""
from typing import (
Any,
Dict,
List,
)
from fastapi import (
APIRouter,
FastAPI,
)
from examples.api_for_sqlalchemy.models import (
Child,
Computer,
Parent,
ParentToChildAssociation,
Post,
User,
UserBio,
)
from fastapi_jsonapi import RoutersJSONAPI
from fastapi_jsonapi.atomic import AtomicOperations
from .api.views_base import DetailViewBase, ListViewBase
from .models.schemas import (
ChildInSchema,
ChildPatchSchema,
ChildSchema,
ComputerInSchema,
ComputerPatchSchema,
ComputerSchema,
ParentInSchema,
ParentPatchSchema,
ParentSchema,
ParentToChildAssociationSchema,
PostInSchema,
PostPatchSchema,
PostSchema,
UserBioInSchema,
UserBioPatchSchema,
UserBioSchema,
UserInSchema,
UserPatchSchema,
UserSchema,
)
def add_routes(app: FastAPI) -> List[Dict[str, Any]]:
tags = [
{
"name": "User",
"description": "Users API",
},
{
"name": "Post",
"description": "Posts API",
},
]
router: APIRouter = APIRouter()
RoutersJSONAPI(
router=router,
path="/users",
tags=["User"],
class_detail=DetailViewBase,
class_list=ListViewBase,
model=User,
schema=UserSchema,
resource_type="user",
schema_in_patch=UserPatchSchema,
schema_in_post=UserInSchema,
)
RoutersJSONAPI(
router=router,
path="/posts",
tags=["Post"],
class_detail=DetailViewBase,
class_list=ListViewBase,
model=Post,
schema=PostSchema,
resource_type="post",
schema_in_patch=PostPatchSchema,
schema_in_post=PostInSchema,
)
RoutersJSONAPI(
router=router,
path="/user-bio",
tags=["Bio"],
class_detail=DetailViewBase,
class_list=ListViewBase,
model=UserBio,
schema=UserBioSchema,
resource_type="user_bio",
schema_in_patch=UserBioPatchSchema,
schema_in_post=UserBioInSchema,
)
RoutersJSONAPI(
router=router,
path="/parents",
tags=["Parent"],
class_detail=DetailViewBase,
class_list=ListViewBase,
model=Parent,
schema=ParentSchema,
resource_type="parent",
schema_in_patch=ParentPatchSchema,
schema_in_post=ParentInSchema,
)
RoutersJSONAPI(
router=router,
path="/children",
tags=["Child"],
class_detail=DetailViewBase,
class_list=ListViewBase,
model=Child,
schema=ChildSchema,
resource_type="child",
schema_in_patch=ChildPatchSchema,
schema_in_post=ChildInSchema,
)
RoutersJSONAPI(
router=router,
path="/parent-to-child-association",
tags=["Parent To Child Association"],
class_detail=DetailViewBase,
class_list=ListViewBase,
schema=ParentToChildAssociationSchema,
resource_type="parent-to-child-association",
model=ParentToChildAssociation,
)
RoutersJSONAPI(
router=router,
path="/computers",
tags=["Computer"],
class_detail=DetailViewBase,
class_list=ListViewBase,
model=Computer,
schema=ComputerSchema,
resource_type="computer",
schema_in_patch=ComputerPatchSchema,
schema_in_post=ComputerInSchema,
)
atomic = AtomicOperations()
app.include_router(router, prefix="")
app.include_router(atomic.router, prefix="")
return tags
This example provides the following API:
url |
method |
endpoint |
action |
---|---|---|---|
/users |
GET |
user_list |
Retrieve a collection of users |
/users |
POST |
user_list |
Create a user |
/users/<int:id> |
GET |
user_detail |
Retrieve details of a user |
/users/<int:id> |
PATCH |
user_detail |
Update a user |
/users/<int:id> |
DELETE |
user_detail |
Delete a user |
in developing
url |
method |
endpoint |
action |
---|---|---|---|
/users/<int:id>/group |
GET |
computer_list |
Retrieve a collection computers related to a user |
/users/<int:id>/group |
POST |
computer_list |
Create a computer related to a user |
/users/<int:id>/relationships/group |
GET |
user_computers |
Retrieve relationships between a user and computers |
/users/<int:id>/relationships/computers |
POST |
user_computers |
Create relationships between a user and computers |
/users/<int:id>/relationships/computers |
PATCH |
user_computers |
Update relationships between a user and computers |
/users/<int:id>/relationships/computers |
DELETE |
user_computers |
Delete relationships between a user and computers |
/computers |
GET |
computer_list |
Retrieve a collection of computers |
/computers |
POST |
computer_list |
Create a computer |
/computers/<int:id> |
GET |
computer_detail |
Retrieve details of a computer |
/computers/<int:id> |
PATCH |
computer_detail |
Update a computer |
/computers/<int:id> |
DELETE |
computer_detail |
Delete a computer |
/computers/<int:id>/owner |
GET |
user_detail |
Retrieve details of the owner of a computer |
/computers/<int:id>/owner |
PATCH |
user_detail |
Update the owner of a computer |
/computers/<int:id>/owner |
DELETE |
user_detail |
Delete the owner of a computer |
/computers/<int:id>/relationships/owner |
GET |
user_computers |
Retrieve relationships between a user and computers |
/computers/<int:id>/relationships/owner |
POST |
user_computers |
Create relationships between a user and computers |
/computers/<int:id>/relationships/owner |
PATCH |
user_computers |
Update relationships between a user and computers |
/computers/<int:id>/relationships/owner |
DELETE |
user_computers |
Delete relationships between a user and computers |
Save this file as api.py and run it using your Python interpreter. Note that we’ve enabled messages.
$ python api.py
* Running on http://127.0.0.1:8082/
* Restarting with reloader
Warning
Debug mode should never be used in a production environment!
Classical CRUD operations
Create object
Request:
POST /computers HTTP/1.1
Content-Type: application/vnd.api+json
{
"data": {
"type": "computer",
"attributes": {
"serial": "Amstrad"
}
}
}
Response:
HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
"data": {
"attributes": {
"serial": "Amstrad"
},
"id": "1",
"links": {
"self": "/computers/1"
},
"relationships": {
"owner": {
"links": {
"related": "/computers/1/owner",
"self": "/computers/1/relationships/owner"
}
}
},
"type": "computer"
},
"jsonapi": {
"version": "1.0"
},
"links": {
"self": "/computers/1"
}
}
List objects
Request:
GET /computers HTTP/1.1
Content-Type: application/vnd.api+json
Response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"attributes": {
"serial": "Amstrad"
},
"id": "1",
"links": {
"self": "/computers/1"
},
"relationships": {
"owner": {
"links": {
"related": "/computers/1/owner",
"self": "/computers/1/relationships/owner"
}
}
},
"type": "computer"
}
],
"jsonapi": {
"version": "1.0"
},
"links": {
"self": "http://localhost:5000/computers"
},
"meta": {
"count": 1
}
}
Update object
Request:
PATCH /computers/1 HTTP/1.1
Content-Type: application/vnd.api+json
{
"data": {
"type": "computer",
"id": "1",
"attributes": {
"serial": "New Amstrad"
}
}
}
Response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"attributes": {
"serial": "New Amstrad"
},
"id": "1",
"links": {
"self": "/computers/1"
},
"relationships": {
"owner": {
"links": {
"related": "/computers/1/owner",
"self": "/computers/1/relationships/owner"
}
}
},
"type": "computer"
},
"jsonapi": {
"version": "1.0"
},
"links": {
"self": "/computers/1"
}
}
Delete object
Request:
DELETE /computers/1 HTTP/1.1
Content-Type: application/vnd.api+json
Response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"meta": {
"message": "Object successfully deleted"
}
}
Relationships
Note
Now let’s use relationships tools. First, create 3 computers named “Halo”, “Nestor” and “Commodore”. We assume that Halo has id=2, Nestor id=3 and Commodore id=4.
Update object and his relationships
Now John sell his Halo (id=2) and buys a new computer named Nestor (id=3). So we want to link this new computer to John. John have also made a mistake in his email so let’s update these 2 things in the same time.
Request:
PATCH /users/1?include=computers HTTP/1.1
Content-Type: application/vnd.api+json
{
"data": {
"type": "user",
"id": "1",
"attributes": {
"email": "john@example.com"
},
"relationships": {
"computers": {
"data": [
{
"type": "computer",
"id": "3"
}
]
}
}
}
}
Response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"attributes": {
"display_name": "JOHN <john@example.com>",
"name": "John"
},
"id": "1",
"links": {
"self": "/users/1"
},
"relationships": {
"computers": {
"data": [
{
"id": "3",
"type": "computer"
}
],
"links": {
"related": "/users/1/computers",
"self": "/users/1/relationships/computers"
}
}
},
"type": "user"
},
"included": [
{
"attributes": {
"serial": "Nestor"
},
"id": "3",
"links": {
"self": "/computers/3"
},
"relationships": {
"owner": {
"links": {
"related": "/computers/3/owner",
"self": "/computers/3/relationships/owner"
}
}
},
"type": "computer"
}
],
"jsonapi": {
"version": "1.0"
},
"links": {
"self": "/users/1"
}
}
Create relationship
Now John buys a new computer named Commodore (id=4) so let’s link it to John.
Request:
POST /users/1/relationships/computers HTTP/1.1
Content-Type: application/vnd.api+json
{
"data": [
{
"type": "computer",
"id": "4"
}
]
}
Response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"meta": {
"message": "Relationship successfully created"
}
}
Check user’s computers without loading actual user
Request:
GET /users/1/computers HTTP/1.1
Content-Type: application/vnd.api+json
Response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"attributes": {
"serial": "Nestor"
},
"id": "3",
"links": {
"self": "/computers/3"
},
"relationships": {
"owner": {
"links": {
"related": "/computers/3/owner",
"self": "/computers/3/relationships/owner"
}
}
},
"type": "computer"
},
{
"attributes": {
"serial": "Commodore"
},
"id": "4",
"links": {
"self": "/computers/4"
},
"relationships": {
"owner": {
"links": {
"related": "/computers/4/owner",
"self": "/computers/4/relationships/owner"
}
}
},
"type": "computer"
}
],
"jsonapi": {
"version": "1.0"
},
"links": {
"first": "http://localhost:5000/computers",
"last": "http://localhost:5000/computers?page%5Bnumber%5D=2",
"next": "http://localhost:5000/computers?page%5Bnumber%5D=2",
"self": "http://localhost:5000/computers"
},
"meta": {
"count": 2
}
}
Delete relationship
Now John sells his old Nestor computer, so let’s unlink it from John.
Request:
DELETE /users/1/relationships/computers HTTP/1.1
Content-Type: application/vnd.api+json
{
"data": [
{
"type": "computer",
"id": "3"
}
]
}
Response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"jsonapi": {
"version": "1.0"
},
"meta": {
"message": "Relationship successfully updated"
}
}
If you want to see more examples visit JSON API 1.0 specification