############ Introduction ############ .. include:: warning.rst **pg-jsonapi** is an asynchronous Python library for building `JSON API v1.0 `_ compliant calls using a very simple declarative syntax. Only ``PostgreSQL`` is supported. ``PostgreSQL`` integration is powered by the `asyncpgsa `_ library. `SQLAlchemy `_ is required for describing database objects. Under the hood, the `marshmallow `_ library is used for object serialization. No previous knowledge of ``marshmallow`` is needed. The user defines models that map to ``SQLAlchemy`` tables. Each model represents a single JSONAPI resource. Each resource has a type. A set of fields can be defined for each resource. A field can be a simple attribute mapping directly to a database column, or derived from multiple columns. The user may also define aggregate fields (ex. counts, max values, etc.). Relationship fields can be used to define relationships between models. The library supports the fetching of resource data, inclusion of related resources, sparse fieldsets, sorting, pagination, and filtering. Quick Start *********** As an example, we create a resource model and use it to implement two basic API calls. First we use SQLAlchemy to describe the database tables:: import datetime as dt import sqlalchemy as sa metadata = sa.MetaData() PASSWORD_HASH_LENGTH = 128 users_t = sa.Table( 'users', metadata, sa.Column('id', sa.Integer, primary_key=True), sa.Column('email', sa.Text, unique=True, nullable=False), sa.Column('created_on', sa.DateTime, nullable=False, default=dt.datetime.utcnow), sa.Column('password', sa.String(PASSWORD_HASH_LENGTH), nullable=False)) user_names_t = sa.Table( 'user_names', metadata, sa.Column('user_id', sa.Integer, sa.ForeignKey('users.id'), primary_key=True, autoincrement=False), sa.Column('title', sa.Text), sa.Column('first', sa.Text, nullable=False), sa.Column('middle', sa.Text), sa.Column('last', sa.Text, nullable=False), sa.Column('suffix', sa.Text), sa.Column('nickname', sa.Text)) Then we define the model:: from jsonapi.model import Model from jsonapi.fields import Derived class UserModel(Model): from_ = users_t, user_names_t fields = ('email', 'first', 'last', 'created_on' Derived('name', lambda rec: rec.first + ' ' + rec.last)) .. note:: The ``id`` and ``type`` fields are predefined. Attempting to do define them explicitly will raise an exception. The primary key of the mapped table is automatically assigned to the ``id`` field, regardless of what the database column is called. The ``type`` field is determined by the value of the :attr:`Model.type_` attribute (see :doc:`model` for more details). .. note:: Composite primary keys are not allowed in the mapped tables. .. note:: You can define fields for a subset of the available database columns. In the example above, we chose not to expose the ``password`` column, for example. Now we are ready to implement the API calls. We use the ``Quart`` web framework for demonstration purposes:: import asyncio import uvloop from quart import Quart, jsonify, request from asyncpgsa import pg from jsonapi.model import MIME_TYPE from jsonapi.tests.model import UserModel asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) app = Quart('jsonapi-test') app.config['JSONIFY_MIMETYPE'] = MIME_TYPE @app.before_first_request async def init(): await pg.init(database='jsonapi', user='jsonapi', password='jsonapi', min_size=5, max_size=10) @app.route('/users/') async def users(): return jsonify(await UserModel().get_collection(request.args)) @app.route('/users/') async def user(user_id): return jsonify(await UserModel().get_object(request.args, user_id)) if __name__ == "__main__": app.run(host="localhost", port=8080, loop=asyncio.get_event_loop()) Example 1. Fetching a Single Object =================================== :: GET http://localhost/users/1 ?fields[user]=email,name :: HTTP/1.1 200 Content-Type: application/vnd.api+json { "data": { "attributes": { "email": "dianagraham@fisher.com", "name": "Robert Camacho" }, "id": "1", "type": "user" } } Example 2. Fetching a Collection of Objects =========================================== :: GET http://localhost/users/ ?fields[user]=created-on,name,email &sort=-created-on &page[size]=10 :: HTTP/1.1 200 Content-Type: application/vnd.api+json { "data": [ { "attributes": { "createdOn": "2019-10-03T16:27:01Z", "email": "dana58@wall.org", "name": "Tristan Nguyen" }, "id": "888", "type": "user" }, { "attributes": { "createdOn": "2019-10-03T11:18:34Z", "email": "gilbertjacob@yahoo.com", "name": "Christian Bennett" }, "id": "270", "type": "user" }, ... ], "meta": { "total": 1000 } } Next Steps ========== In the following sections we will guide you through the different features available.