Skip to content

FastAPI-HyperModel

Simple hypermedia for FastAPI

Package version


Documentation: https://jtc42.github.io/fastapi-hypermodel/

Source Code: https://github.com/jtc42/fastapi-hypermodel


FastAPI-HyperModel is a FastAPI + Pydantic extension for simplifying hypermedia-driven API development.

Hypermedia consist of enriching API responses by providing links to other URIs within the services to fetch related resources or perform certain actions. There are several levels according to the Hypermedia Maturity Model Levels. Using Hypermedia makes APIs reach Level 3 of the Richardson Maturity Model (RMM), which involves leveraging Hypertext As The Engine Of Application State (HATEOAS), that is, Hypermedia.

Below are two examples of how implementing hypermedia changes the responses in different formats. The first example is for singular elements whereas the second is for collections.

Single Item Example

1
2
3
4
5
{
    "id_": "item01",
    "name": "Foo",
    "price": 10.2,
}
1
2
3
4
5
6
7
8
{
    "id_": "item01",
    "name": "Foo",
    "price": 10.2,

    "href": "/items/item01",
    "update": "/items/item01"
}
{
    "id_": "item01",
    "name": "Foo",
    "price": 10.2,

    "_links": {
        "self": {"href": "/items/item01"},
        "update": {"href": "/items/item01"},
        "curies": [
            {
                "href": "https://schema.org/{rel}",
                "templated": true,
                "name": "sc"
            }
        ],
    },
}
{
    "properties": {
        "id_": "item01",
        "name": "Foo",
        "price": 10.2
    },
    "links": [
        {
            "rel": ["self"],
            "href": "/items/item01"
        }
    ],
    "actions": [
        {
            "name": "update",
            "method": "PUT",
            "href": "/items/item01",
            "type": "application/x-www-form-urlencoded",
            "fields": [
                {
                    "name": "name",
                    "type": "text",
                    "value": "Foo"
                },
                {
                    "name": "description",
                    "type": "text",
                    "value": "None"
                },
                {
                    "name": "price",
                    "type": "number",
                    "value": "10.2"
                }
            ]
        }
    ]
}

Collection of Items Example

{
    "items": [
        {
            "id_": "item01",
            "name": "Foo",
            "description": null,
            "price": 50.2,
        },
        {
            "id_": "item02",
            "name": "Bar",
            "description": "The Bar fighters",
            "price": 62.0,
        },
        {
            "id_": "item03",
            "name": "Baz",
            "description": "There goes my baz",
            "price": 50.2,
        },
        {
            "id_": "item04",
            "name": "Doe",
            "description": "There goes my Doe",
            "price": 5.0,
        }
    ],
}
{
    "items": [
        {
            "id_": "item01",
            "name": "Foo",
            "description": null,
            "price": 50.2,

            "href": "/items/item01",
            "update": "/items/item01"
        },
        {
            "id_": "item02",
            "name": "Bar",
            "description": "The Bar fighters",
            "price": 62.0,

            "href": "/items/item02",
            "update": "/items/item02"
        },
        {
            "id_": "item03",
            "name": "Baz",
            "description": "There goes my baz",
            "price": 50.2,

            "href": "/items/item03",
            "update": "/items/item03"
        },
        {
            "id_": "item04",
            "name": "Doe",
            "description": "There goes my Doe",
            "price": 5.0,

            "href": "/items/item04",
            "update": "/items/item04"
        }
    ],

    "href": "/items",
    "find": "/items/{id_}",
    "update": "/items/{id_}"
}
{
    "_embedded": {
        "sc:items": [
            {
                "id_": "item01",
                "name": "Foo",
                "description": null,
                "price": 10.2,

                "_links": {
                    "self": {
                        "href": "/items/item01"
                    },
                    "update": {
                        "href": "/items/item01"
                    },
                    "curies": [
                        {
                            "href": "https://schema.org/{rel}",
                            "templated": true,
                            "name": "sc"
                        }
                    ]
                }
            },
            {
                "id_": "item02",
                "name": "Bar",
                "description": "The Bar fighters",
                "price": 62.0,

                "_links": {
                    "self": {
                        "href": "/items/item02"
                    },
                    "update": {
                        "href": "/items/item02"
                    },
                    "curies": [
                        {
                            "href": "https://schema.org/{rel}",
                            "templated": true,
                            "name": "sc"
                        }
                    ]
                }
            },
            {
                "id_": "item03",
                "name": "Baz",
                "description": "There goes my baz",
                "price": 50.2,

                "_links": {
                    "self": {
                        "href": "/items/item03"
                    },
                    "update": {
                        "href": "/items/item03"
                    },
                    "curies": [
                        {
                            "href": "https://schema.org/{rel}",
                            "templated": true,
                            "name": "sc"
                        }
                    ]
                }
            },
            {
                "id_": "item04",
                "name": "Doe",
                "description": "There goes my Doe",
                "price": 5.0,

                "_links": {
                    "self": {
                        "href": "/items/item04"
                    },
                    "update": {
                        "href": "/items/item04"
                    },
                    "curies": [
                        {
                            "href": "https://schema.org/{rel}",
                            "templated": true,
                            "name": "sc"
                        }
                    ]
                }
            }
        ]
    },

    "_links": {
        "self": {
            "href": "/items"
        },
        "find": {
            "href": "/items/{id_}",
            "templated": true
        },
        "update": {
            "href": "/items/{id_}",
            "templated": true
        },
        "curies": [
            {
                "href": "https://schema.org/{rel}",
                "templated": true,
                "name": "sc"
            }
        ]
    }
}
{
    "entities": [
        {
            "properties": {
                "id_": "item01",
                "name": "Foo",
                "description": null,
                "price": 10.2
            },
            "links": [
                {
                    "rel": ["self"],
                    "href": "/items/item01"
                }
            ],
            "actions": [
                {
                    "name": "update",
                    "method": "PUT",
                    "href": "/items/item01",
                    "type": "application/x-www-form-urlencoded",
                    "fields": [
                        {
                            "name": "name",
                            "type": "text",
                            "value": "Foo"
                        },
                        {
                            "name": "description",
                            "type": "text",
                            "value": "None"
                        },
                        {
                            "name": "price",
                            "type": "number",
                            "value": "10.2"
                        }
                    ]
                }
            ],
            "rel": ["items"]
        },
        {
            "properties": {
                "id_": "item02",
                "name": "Bar",
                "description": "The Bar fighters",
                "price": 62.0
            },
            "links": [
                {
                    "rel": ["self"],
                    "href": "/items/item02"
                }
            ],
            "actions": [
                {
                    "name": "update",
                    "method": "PUT",
                    "href": "/items/item02",
                    "type": "application/x-www-form-urlencoded",
                    "fields": [
                        {
                            "name": "name",
                            "type": "text",
                            "value": "Bar"
                        },
                        {
                            "name": "description",
                            "type": "text",
                            "value": "The Bar fighters"
                        },
                        {
                            "name": "price",
                            "type": "number",
                            "value": "62.0"
                        }
                    ]
                }
            ],
            "rel": ["items"]
        },
        {
            "properties": {
                "id_": "item03",
                "name": "Baz",
                "description": "There goes my baz",
                "price": 50.2
            },
            "links": [
                {
                    "rel": ["self"],
                    "href": "/items/item03"
                }
            ],
            "actions": [
                {
                    "name": "update",
                    "method": "PUT",
                    "href": "/items/item03",
                    "type": "application/x-www-form-urlencoded",
                    "fields": [
                        {
                            "name": "name",
                            "type": "text",
                            "value": "Baz"
                        },
                        {
                            "name": "description",
                            "type": "text",
                            "value": "There goes my baz"
                        },
                        {
                            "name": "price",
                            "type": "number",
                            "value": "50.2"
                        }
                    ]
                }
            ],
            "rel": ["items"]
        },
        {
            "properties": {
                "id_": "item04",
                "name": "Doe",
                "description": "There goes my Doe",
                "price": 5.0
            },
            "links": [
                {
                    "rel": ["self"],
                    "href": "/items/item04"
                }
            ],
            "actions": [
                {
                    "name": "update",
                    "method": "PUT",
                    "href": "/items/item04",
                    "type": "application/x-www-form-urlencoded",
                    "fields": [
                        {
                            "name": "name",
                            "type": "text",
                            "value": "Doe"
                        },
                        {
                            "name": "description",
                            "type": "text",
                            "value": "There goes my Doe"
                        },
                        {
                            "name": "price",
                            "type": "number",
                            "value": "5.0"
                        }
                    ]
                }
            ],
            "rel": ["items"]
        }
    ],
    "links": [
        {
            "rel": ["self"],
            "href": "/items"
        }
    ],
    "actions": [
        {
            "name": "find",
            "method": "GET",
            "href": "/items/{id_}",
            "templated": true
        },
        {
            "name": "update",
            "method": "PUT",
            "href": "/items/{id_}",
            "type": "application/x-www-form-urlencoded",
            "fields": [
                {
                    "name": "name",
                    "type": "text",
                    "value": "None"
                },
                {
                    "name": "description",
                    "type": "text",
                    "value": "None"
                },
                {
                    "name": "price",
                    "type": "number",
                    "value": "None"
                }
            ],
            "templated": true
        }
    ]
}

Installation

pip install fastapi-hypermodel

Limitations

Currently, query parameters will not resolve correctly. When generating a resource URL, ensure all parameters passed are path parameters, not query parameters.

This is an upstream issue, being tracked here.