API

The platform consists of both backend and frontend. Each of those two parts doing own work - backend is a server-side, data-storing and computing set of processes. Frontend is an any application, including web browser tab with loaded Wefram-based web code, or mobile apps for Android, Apple iOS and etc.

The frontend itself costs nothing without working backend and a some posibility to interact with it. The API is the way the frontend and backend, in general, interacting with each other.

Entity API reference

class wefram.api.EntityAPI(key: Optional[Any] = None)

The API-ready entity abstract class. Implements much of logics for the HTTP requests handling, endpointing to the EntityCRUD defined abstract methods.

app: str

The application for which is entity is applicable to. Filled automatically at the entity registration time.

paths: dict

The dict, containing registerd API paths for the every API method. For example:

{
    'create': '/api/<app>/<EntityName>',
    'read': '/api/<app>/<EntityName>/<key>',
    'list': '/api/<app>/<EntityName>',
    'update': '/api/<app>/<EntityName>/<key>',
    'update_batch': '/api/<app>/<EntityName>',
    'options': '/api/<app>/<EntityName>',
    'options_resolve': '/api/<app>/<EntityName>/resolve',
    'delete': '/api/<app>/<EntityName>/<key>',
    'delete_batch': '/api/<app>/<EntityName>',
}

So, by accessing the entity class’es path property using the corresponding method key, the programmer may get the absolute URL for accessing the corresponding API method from the frontend.

name: Optional[str] = None

The entity name. Filled automatically basing on the entity class name.

version: Optional[Union[int, str]] = None

Optional, the API version. Gives an ability to handle different versions of the entity by declaring different entity classes (for example, basing on the one general class and hanlding differences between versions in the inheited ones.

path_prefix: Optional[str] = None

Optional prefix used on the absolute routing path generation. This prefix, if specified, will be used in the routing path right after /api and before the rest of the path. For example:

prefix = 'myprefix'

The path will be: /api/myprefix/myapp/v1/MyEntity

path_suffix: Optional[str] = None

Optional suffix used on the absolute routing path generation. This suffix, if specified, will be used in the routing path at the end. For example:

suffix = 'mysuffix'

The path will be: /api/myapp/v1/MyEntity/mysuffix

requires: Optional[Union[str, List[str]]] = None

The general requirement for access rights. The programmer may list required permissions (scopes) here and them will be used by default for all API methods, except ones whose declares own requirements.

For example:

class MyEntity(EntityAPI):
    requires = ['mypermission']
allow_create: Union[bool, List[str]] = True

If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).

class MyEntity1(EntityAPI):
    # Permit the method, but require 'some_access' permission
    requires = ['some_access']
    allow_create = True  # may be omitted because by default is True

class MyEntity2(EntityAPI):
    # Permit the method & requires 'other_permission', and not requires
    # any other permissions to access.
    requires = ['some_access']
    allow_create = ['other_permission']
allow_read: Union[bool, List[str]] = True

If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).

class MyEntity1(EntityAPI):
    # Permit the method, but require 'some_access' permission
    requires = ['some_access']
    allow_read = True  # may be omitted because by default is True

class MyEntity2(EntityAPI):
    # Permit the method & requires 'other_permission', and not requires
    # any other permissions to access.
    requires = ['some_access']
    allow_read = ['other_permission']
allow_options: Union[bool, List[str]] = True

If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).

class MyEntity1(EntityAPI):
    # Permit the method, but require 'some_access' permission
    requires = ['some_access']
    allow_options = True  # may be omitted because by default is True

class MyEntity2(EntityAPI):
    # Permit the method & requires 'other_permission', and not requires
    # any other permissions to access.
    requires = ['some_access']
    allow_options = ['other_permission']
allow_update: Union[bool, List[str]] = True

If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).

class MyEntity1(EntityAPI):
    # Permit the method, but require 'some_access' permission
    requires = ['some_access']
    allow_update = True  # may be omitted because by default is True

class MyEntity2(EntityAPI):
    # Permit the method & requires 'other_permission', and not requires
    # any other permissions to access.
    requires = ['some_access']
    allow_update = ['other_permission']
allow_delete: Union[bool, List[str]] = True

If set to True (default) - the method is allowed and will be routed. If requires property has been specified - it will be applied to this method. If set to False - the method is prohibited and will not be routed. If set to list type - then this method is allowed, will be routed, but requirements for security access permissions (scopes) will be overrided with the given list of permissions (even if has been specified by requires property).

class MyEntity1(EntityAPI):
    # Permit the method, but require 'some_access' permission
    requires = ['some_access']
    allow_delete = True  # may be omitted because by default is True

class MyEntity2(EntityAPI):
    # Permit the method & requires 'other_permission', and not requires
    # any other permissions to access.
    requires = ['some_access']
    allow_delete = ['other_permission']
classmethod path_base() str

Internal method which returns a routing path base string basing (by default) on the entity class name. Useful for overriding how the entity sounds in the routing path (URL).

static prepare_response(result: Any, status_code: int = 200) starlette.responses.Response

Wrapper method which get the handler result and returns the HTTP response. This allows the handler methods (at CRUD model) to return Python-basic response like ‘True’ or dict or list, or even just a string, not dealing with HTTP response generation.

Parameters
  • result – The result of the API handler, which may be any kind of type.

  • status_code – The status code which about to use as HTTP status code in the response; by default is ‘200 OK’;

Returns

The formed and ready to send to the calling frontend client response.

async classmethod parse_request_payload(request: starlette.requests.Request) Optional[Dict[str, Any]]

Parses the payload of the request. If the payload is dict (most often case), then rename dict’s keys from localCamesCase (typical for the JSON, JS & TS) to the snake_case (instead, typical for the Python).

classmethod parse_request_arguments(request: starlette.requests.Request) Dict[str, str]

Parses the query arguments (if any) and returns them as dict. For the current moment - it is just a proxy function to the middleware prepared arguments’ dict.

async classmethod handle_create(request: starlette.requests.Request) starlette.responses.Response

Creates a single entity object, using JSON or FormData payload to set it up with the values. Returns no content response 201 if succeeded.

If argument :argument return_key: setted to ‘true’ - return the new entity object’s key (which requires FLUSH+COMMIT operation to be executed in the ORM prior to response been completed, usually).

async classmethod handle_read(request: starlette.requests.Request) starlette.responses.Response

The method both handling listing of entities, and returning a single entity data. If the :path_param key: is not None - the single entity object will be fetched and resulted as JSONed dict. If the :path_param key: is None - the list of entity objects will be fetched, corresponding with the given in args filters, and returned as JSONed list of objects’ dicts.

async classmethod handle_options(request: starlette.requests.Request) starlette.responses.Response

This method returns a dict, containing keys and corresponding object’s human-readable representations, like names.

async classmethod handle_update(request: starlette.requests.Request) starlette.responses.Response

This method both can update a single entity object, given using :path_param key: argument (in the URL), and a set of entity objects, given by keys[] as URL argument (used for batch update of several objects at a time).

async classmethod handle_delete(request: starlette.requests.Request) starlette.responses.Response

This method both can delete a single entity object, given using :path_param key: argument (in the URL), and a set of entity objects, given by keys[] as URL argument.

Model API reference

class wefram.api.ModelAPI(key: Optional[Any] = None)

The special case of the EntityAPI class, implementing the ORM model CRUD operations. This simplificates the API creation for ORM models, realizes corresponding, routed handlers for common operations.

Notice that there is a good idea to override some methods on this basic model if there is a need to make more complex behaviour on the one or the another operation.

For example, let’s assume that we have a some extra operations which must be done AFTER the object been successfully deleted. Let’s make a simple example on that:

class MyModel(ModelAPI):
    model = mymodels.MyModel

    async def delete(self, *keys: Union[str, int]) -> None:
        await super().delete(*keys)

        # There are no exceptions raised, so we know that deletion
        # was successful, and do some extra work here.
        print("Funny, its works!")
model: ClassVar[sqlalchemy.orm.decl_api.Base]

The corresponding ORM model class for which this API is declaring for.

set_name: Optional[str] = None

Optional name of the Model.Meta.attributes_sets entry, which will be used for read operations.

return_created_id: bool = False

If set to True - the create operation will return an object key to the frontend on object create done with HTTP result code 200; otherwise only Success code 204 (No response) will be returned, indicating the successful creation.

This might be useful especially when using auto-increment keys and there is a need to know the created object’s key after it creation.

Default is False.

default_deep: bool = False

The API has two behaviours on how to return objects’ structs - simple and deep. The difference is that in deep behaviour dependend objects (for example, joined using Many-to-One, Many-to-Many, One-to-Many, One-to-One relationships) will be fetched from the database too and their first-line representations will be JSONified and returned to the caller too. See dict() of the wefram.ds.model.Model class for more details.

This behaviour controlled by passing URL argument deep=true or deep=false (or even omitting this argument). So, when the argument is omitted, the default behaviour, which is controlled by this attribute, will be used.

Default is False.

classmethod path_base() str

Overrides the default entity-API path base to the model original name (because the class name rewrites by the ORM mechanics).

property key_clause: Optional[sqlalchemy.sql.elements.ClauseList]

Returns a ready to use in the query key clause for the corresponding, declared by model ORM model and this instance. Ready to be used in the WHERE clause by the ORM mechanics.

keys_clause(keys: List[Union[str, int]]) Optional[sqlalchemy.sql.elements.ClauseElement]

Returns a ready to use in the query key clause for the corresponding, declared by model ORM model and given list of SIMPLE keys. Ready to be used in the WHERE clause by the ORM mechanics.

Please note that this method does not work with complex keys!

Parameters

keys – A list of simple keys (str or int type).

Returns

A ready to use in the query SQLAlchemy clause, based on IN database operator.

async classmethod create(**with_values) Union[bool, object]

Creates a new ORM model instance, filling up it with the given in the payload values, saving it to the database.

Note that this method calls flush() function of the database session to ensure successful operation and handle some special case errors like integrity errors.

handle_read_filter(c: sqlalchemy.orm.attributes.QueryableAttribute, value: Any) Optional[sqlalchemy.sql.elements.ClauseElement]

Used by the class mechanics to handle passed in the URL argument filter and return an SQLAlchemy ready to use clause.

Parameters
  • c – The corresponding ORM Model attribute (column) for which to handle filter for;

  • value – The raw value which about to be handled.

Returns

SQLAlchemy ready to use clause

async filter_read(like: Optional[str] = None, ilike: Optional[str] = None, **filters: Any) sqlalchemy.sql.elements.ClauseList

Returns ready to use SQLAlchemy filter clause basing on the given filters for the read CRUD operation.

There are two basic filtering handling by this method:

  • value filtering - filter model objects by some value (some defined model column);

  • search - try to find objects by search operation (for more details see

    like() and ilike()). Search operation may be, according to corresponding methods of the Model class - like (strict, case sensetive search) and ilike (case insensetive search).

Filters expected to be specified using URL arguments. For example:

[GET] /api/myapp/MyModel?name=John&lastName=Final

or

[GET] /api/myapp/MyModel?ilike=eugene

Note that standard Wefram frontend functionality takes care on how to make the resulting URL with or without filters, by using corresponding frontend API methods (TypeScript written).

async item_as_json(instance: sqlalchemy.orm.decl_api.Base, deep: Optional[bool] = None) dict

Returns the given instance JSONified.

Parameters
  • instance (Model instance) – The ORM Model instance to JSONify to.

  • deep (bool) – Do the deep JSONify or not. See dict() of the wefram.ds.model.Model class for more details

Returns

The JSONify ready Python dict.

Return type

dict

async items_as_json(instances: List[sqlalchemy.orm.decl_api.Base], deep: Optional[bool] = None) List[dict]

Returns the given instances JSONified.

Parameters
  • instances (List of Model instances) – The ORM Model instances to JSONify to.

  • deep (bool) – Do the deep JSONify or not. See dict() of the wefram.ds.model.Model class for more details

Returns

The JSONify ready Python dict.

Return type

List of dicts

async read(*keys: Union[str, int], count: bool = False, offset: Optional[int] = None, limit: Optional[int] = None, order: Optional[Union[str, List[str]]] = None, deep: Optional[bool] = None, like: Optional[str] = None, ilike: Optional[str] = None, **filters: Any) Union[sqlalchemy.orm.decl_api.Base, dict, List[dict]]

The CRUD’s READ operation, used both for listing of existing entity objects like a list, and fetching a single object details.

async options(*keys: Union[str, int], like: Optional[str] = None, ilike: Optional[str] = None) dict

About to return dict containing keys and corresponding human readable representations. For example, user’s keys and corresponding names.

async update(*keys: Union[str, int], **values) None

The CRUD’s UPDATE operation, used to update the existing entity object (or objects, if the :param keys: can be handled by the real method).

async delete(*keys: Union[str, int]) None

The CRUD’s DELETE operation, used to delete according entity object(s).