REST API for user application

Rational

At B2CK we maintain a Flask web shop based on flask-tryton. We have achieved good web vitals score (~95%) for desktop but we are still low on mobile (for LCP, FCP and TTFB). This is mainly due to slow time to first bit because we need to render the full page before flask send the first bit. The reason for that is that we use Tryton model instances in the template so stream rendering is not possible as the database connection will be closed. (An option would be to collect all the data before the rendering but it is complex and not very flexible).
There are also others inconveniences with flask-tryton for example:

  • The Tryton models can not be cached with their value (in memcached for example).
  • It is difficult to add cache on template blocks because Tryton models have hidden keys in the context like the language or the company.
  • To scale we need to add more flask instances which all keep a database pool which increase the number of connection to the database (even worse with cache managed by bus).

So having a stream rendering could improve the web vitals because the header could be sent quickly which can contain preload and other optimizations.

So to improve the situation, we think that we need to have a stateless communication between the web server (Flask or other) (which can optionally be cached) and Tryton.
We think that a REST API would provide the most flexibility and performance with a simple implementation that can follow the modularity of Tryton (compared to GraphQL).

Proposal

The proposal is to implement a REST API using user application authentication.
Using user application provides access control and log. So a dedicated user can be used per consumer.

API

  • GET https://tryton.example.com/api/rest/database/party.party
    To get all the parties as a json list of party values.
    Parameters:
    • d: a json domain like d=[['name', '=', 'John']]
    • s: the limit
    • p: the offset
    • o: a json order like o=[['name', 'ASC']]
  • POST https://tryton.example.com/api/rest/database/party.party
    To create a party and return the URL of the created party values.
  • GET https://tryton.example.com/api/rest/database/party.party/id
    Return the party values for given id.
  • GET https://tryton.example.com/api/rest/database/party.party/id/addresses
    Return the relations field for the given party id.
    Such sub-collections are advised in the header of the main collection.
  • PUT https://tryton.example.com/api/rest/database/party.party/id
    Update the party for given id with the json value. And return the new party values.
  • DELETE https://tryton.example.com/api/rest/database/party.party/id
    Delete the party for given id.
  • POST https://tryton.example.com/api/rest/database/party.party/id/button
    Call the button for the given id.

The context can be set using HTTP header X-Tryton-Context as a json dictionary.
The language is set using the HTTP header Accept-Language.

The JSON values of a record are build using a method ModelStorage.__json__(self, usages=None). The usages is a list of string which represent for which purpose the API is used by the consumer (ex: sale and webshop). These usage are set in the HTTP header X-Tryton-Usage as a comma separated list.
By default the returned values are {'id': self.id, 'rec_name': self.rec_name}.

Library

A Python library is started as a consumer of this API. It uses the ActiveRecord pattern to access and browse the resources (using CRUD methods).
It is based on python-requests using a connection pooling.
It keeps a global context that is set as header of the requests.
The library must be independent of the Tryton series.

Implementation

6 Likes

I initially liked the proposal but I’m wondering now, which differences or features would a REST API introduce versus the current JSON-RPC?

The main difference would be the knowledge of the procedures to call versus the intuitive (and probably well documented) REST API. But this is something that you either way can already do in the client-side of the application you’re building.

Am I wrong or missing some points?

The JSON-RPC is currently session based which is a problem to use for automate communication.
But also the content of the response is strictly defined by the request. For example the read call must define each fields to read and can only read fields.
But the idea behind the REST API is to be flexible and extensive. But also to use introspection on the answer to detect feature for example. This way I think it will be possible to create for example generic flask module for web shop that adapt depending on the modules activated (and so on the available content in the responses).

A last point is also that JSON-RPC is mainly design for the clients. So it works with list of records, with model definition etc.
When the REST API will be more directed to single and simple record actions and with caching possibilities.

4 Likes

Will the REST API include support for OpenAPI specifications? if so, will the schema endpoint dynamically display the routes / specs for only the active modules?

Why not but it is not a main priority.

A post was split to a new topic: What’s the recommended way for someone to expose a REST-like api for tryton now?

I decided to encode in base64 the JSON parameters to follow Shorten clients URL (#13052) · Issues · Tryton / Tryton · GitLab

I think it will be good to implement a default usage which renders the value of all (without expanding relations) to be used as simple API for tryton that can replace client api.

1 Like