japyd 1.0.1
Automate JSON:API management in Flask. No manual mapping, just clean code.
License: MIT
Requires Python: >=3.11

# japyd

> *"JSON:API, Pydantically simple."*

Automate JSON:API relationships with Pydantic. No manual mapping, just clean code.

[![Tests](https://github.com/gdoumenc/japyd/actions/workflows/tests.yml/badge.svg)](https://github.com/gdoumenc/japyd/actions/workflows/tests.yml)
[![Codecov](https://codecov.io/gh/gdoumenc/japyd/branch/main/graph/badge.svg)](https://codecov.io/gh/gdoumenc/japyd)
[![PyPI](https://img.shields.io/pypi/v/japyd)](https://pypi.org/project/japyd/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python](https://img.shields.io/python/required-version-toml?tomlFilePath=https://raw.githubusercontent.com/gdoumenc/japyd/refs/heads/master/pyproject.toml)](https://pypi.org/project/japyd/)
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/gdoumenc/japyd/graphs/commit-activity)
[![Open Source](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/gdoumenc/japyd)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

## Japyd: Core Value Propositions

**Japyd** is a Python toolkit designed to simplify the interaction with JSON:API, a specification for building APIs in JSON.
It provides a set of utilities to facilitate the creation, manipulation, and validation of JSON:API resources and requests.

### Build JSON:API Relational Structures from Pydantic Models

**Native Pydantic Integration:** Automatically generates JSON:API-compliant resources and relationships from Pydantic models, eliminating boilerplate.
**Relationship Management:** Supports to-one, to-many, and included resources, fully compliant with the JSON:API specification.
**Smart Serialization:** Converts Pydantic objects into valid JSON:API documents, handling attributes, relationships, and metadata seamlessly.

### Parse JSON:API Query Parameters (Dotnet-style Syntax)

**Advanced Query Parsing:** Supports JSON:API.NET-style query parameters, including filtering, sorting, pagination, sparse fieldsets, and inclusion.
**Built-in Validation:** Ensures query parameters are syntactically correct and consistent.

- Filtering: filter[name]=Guillaume
- Sorting: sort=-created,title
- Pagination: page[number]=1&page[size]=10
- Sparse Fieldsets: fields[articles]=title,author
- Inclusion: include=author,comments

### Flask Extension for JSON:API Response Formatting

**Ready-to-use Flask Integration:** Provides a Flask extension to automatically encapsulate responses in JSON:API format, including:

- Resources (data, relationships, attributes)
- Pagination (links, page metadata)
- Errors (standardized JSON:API error objects)

**Simplified Endpoints**: Reduces manual response formatting, ensuring compliance with JSON:API standards.

## Relationship decomposition

To automate and standardize the definition of `relationships` in our JSON:API implementation, we leveraged Pydantic’s
data model.
This approach allowed us to dynamically infer relationships between resources without manually declaring them for each
object type.

The principle is as follows:

- A Pydantic model represents a resource (e.g., JSON:API Resource Object).
- If an attribute of this model is itself a Pydantic object (or a list of Pydantic objects), it is automatically
- interpreted as a `relationship` in the JSON:API response.
- If the attribute is a primitive type (string, integer, boolean, or event dict etc.), it is treated as a standard
  `attribute`.

### Usage

#### Serialization

Define your data models in Pydantic, let *japyd* automatically handle serialization—including relationships and included
resources—and expose a standard-compliant JSON:API with Flask in just a few lines of code.

```python
import typing as t

import pytest
from flask import Flask
from flask_pydantic import validate

from japyd import JsonApiBaseModel
from japyd import JsonApiQueryModel
from japyd import TopLevel


class Product(JsonApiBaseModel):
    jsonapi_type: t.ClassVar[str] = "product"

    id: str
    price: float


class Order(JsonApiBaseModel):
    jsonapi_type: t.ClassVar[str] = "order"

    id: str
    customer_id: str
    items: list[Product]  # This field will be 'relationship' in JSON:API
    status: str  # This field will be classical 'attribute'


app = Flask(__name__)


@app.route("/orders/<order_id>")
@validate(exclude_none=True)
def get_order(order_id, query: JsonApiQueryModel):
    order = Order(id=order_id, customer_id="123", items=[Product(id="1", price=100.0)], status="open")
    return query.one_or_none(order)


@pytest.fixture()
def client():
    return app.test_client()


def test_request(client):
    response = client.get("/orders/3?include=items")
    top = TopLevel.model_validate(response.json)
    assert top.data.id == "3"
    assert top.data.attributes['status'] == 'open'
    assert len(top.data.relationships['items'].data) == 1
    assert top.included[0].type == "product"
```

You can bypass this behavior by annotationg the field as follow:

```python
    items: Annotated[list[Product], 'as_attribute']  # This field will be now an 'attribute' in JSON:API
```

#### Filtering

The complete filtering syntax of [JsonApiDotNetCore](https://www.jsonapi.net/) is supported

## References

*japyd* (JsonApi PYDantic) is a coherent and powerful composition of :

1. Pydantic and its Flask extension [Flask-Pydantic](https://github.com/pallets-eco/flask-pydantic)
1. Filtering syntax defined in the dotnet implementation [JsonApiDotNetCore](https://www.jsonapi.net/).
1. Simple relationship extraction and other structure manipulations.

## 🚀 Looking for Contributors

We’re actively seeking developers, testers, and open-source enthusiasts to help us build and improve japyd.
Whether you’re passionate about data validation, API design, or just want to contribute to an innovative open-source
project, your help is welcome! Check out our contribution guidelines and open issues to get started. Let’s shape the
future of Python APIs together! 💻✨