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