All Projects → thomaxxl → Safrs

thomaxxl / Safrs

Licence: gpl-3.0
SqlAlchemy Flask-Restful Swagger Json:API OpenAPI

Programming Languages

python
139335 projects - #7 most used programming language
python3
1442 projects

Projects that are alternatives of or similar to Safrs

Apispec
A pluggable API specification generator. Currently supports the OpenAPI Specification (f.k.a. the Swagger specification)..
Stars: ✭ 831 (+225.88%)
Mutual labels:  rest-api, swagger, openapi, openapi3, flask
Full Stack Fastapi Couchbase
Full stack, modern web application generator. Using FastAPI, Couchbase as database, Docker, automatic HTTPS and more.
Stars: ✭ 243 (-4.71%)
Mutual labels:  swagger, openapi, openapi3, json
Kaizen Openapi Editor
Eclipse Editor for the Swagger-OpenAPI Description Language
Stars: ✭ 97 (-61.96%)
Mutual labels:  swagger, openapi, openapi3, json
Openapi Directory
🌐 Wikipedia for Web APIs. Directory of REST API definitions in OpenAPI 2.0/3.x format
Stars: ✭ 2,635 (+933.33%)
Mutual labels:  rest-api, swagger, openapi, openapi3
Spectral
A flexible JSON/YAML linter for creating automated style guides, with baked in support for OpenAPI v2 & v3.
Stars: ✭ 876 (+243.53%)
Mutual labels:  swagger, openapi, openapi3, json
Swurg
Parse OpenAPI documents into Burp Suite for automating OpenAPI-based APIs security assessments (approved by PortSwigger for inclusion in their official BApp Store).
Stars: ✭ 94 (-63.14%)
Mutual labels:  swagger, openapi, openapi3, json
Swagger Parser
Swagger Spec to Java POJOs
Stars: ✭ 468 (+83.53%)
Mutual labels:  rest-api, swagger, openapi, openapi3
Fastapi
FastAPI framework, high performance, easy to learn, fast to code, ready for production
Stars: ✭ 39,588 (+15424.71%)
Mutual labels:  swagger, openapi, openapi3, json
Swagger Core
Examples and server integrations for generating the Swagger API Specification, which enables easy access to your REST API
Stars: ✭ 6,898 (+2605.1%)
Mutual labels:  rest-api, swagger, openapi, openapi3
Flasgger
Easy OpenAPI specs and Swagger UI for your Flask API
Stars: ✭ 2,825 (+1007.84%)
Mutual labels:  rest-api, swagger, openapi, flask
Full Stack Fastapi Postgresql
Full stack, modern web application generator. Using FastAPI, PostgreSQL as database, Docker, automatic HTTPS and more.
Stars: ✭ 7,635 (+2894.12%)
Mutual labels:  swagger, openapi, openapi3, json
Flask Restplus Server Example
Real-life RESTful server example on Flask-RESTplus
Stars: ✭ 1,240 (+386.27%)
Mutual labels:  rest-api, swagger, openapi, flask
Swagger Ui
Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.
Stars: ✭ 21,279 (+8244.71%)
Mutual labels:  rest-api, swagger, openapi, openapi3
Oapi Codegen
Generate Go client and server boilerplate from OpenAPI 3 specifications
Stars: ✭ 806 (+216.08%)
Mutual labels:  rest-api, swagger, openapi, openapi3
Springdoc Openapi
Library for OpenAPI 3 with spring-boot
Stars: ✭ 1,113 (+336.47%)
Mutual labels:  rest-api, swagger, openapi, openapi3
Mockoon
Mockoon is the easiest and quickest way to run mock APIs locally. No remote deployment, no account required, open source.
Stars: ✭ 3,448 (+1252.16%)
Mutual labels:  rest-api, swagger, openapi, openapi3
Proteus
Lean, mean, and incredibly fast JVM framework for web and microservice development.
Stars: ✭ 178 (-30.2%)
Mutual labels:  rest-api, swagger, openapi
Swagger meqa
Auto generate and run tests using swagger/OpenAPI spec, no coding needed
Stars: ✭ 151 (-40.78%)
Mutual labels:  rest-api, swagger, openapi
shipengine-openapi
The official OpenAPI 3.0 definitions for ShipEngine™
Stars: ✭ 13 (-94.9%)
Mutual labels:  swagger, openapi, openapi3
openapi-schema-validator
OpenAPI schema validator for Python
Stars: ✭ 35 (-86.27%)
Mutual labels:  swagger, openapi, openapi3

Latest Version Supported Python versions License: GPL v3 Python application Codacy Badge Downloads

SAFRS: Python OpenAPI & JSON:API Framework

demo

Overview

SAFRS is an acronym for SqlAlchemy Flask-Restful Swagger. The purpose of this framework is to help python developers create a self-documenting JSON API for sqlalchemy database objects and relationships. These objects can be serialized to JSON and can be created, retrieved, updated and deleted through the JSON API. Optionally, custom resource object methods can be exposed and invoked using JSON. Class and method descriptions and examples can be provided in yaml syntax in the code comments. The description is parsed and shown in the swagger web interface.

The result is an easy-to-use swagger/OpenAPI and JSON:API compliant API implementation.

A LIVE DEMO is available, where much of the basic functionality is implemented with a simple example.

UPDATE! documentation can be found in the wiki.

Installation

SAFRS can be installed as a pip package or by downloading the latest version from github, for example:

git clone https://github.com/thomaxxl/safrs
cd safrs
pip install .

Once the dependencies are installed, the examples can be started with

python3 examples/demo_relationship.py "your-interface-ip"

JSON:API Interface

Exposed resource objects can be queried using the JSON:API format. The API supports following HTTP operations:

  • GET : Retrieve an object or a list of objects
  • PATCH : Update an object.
  • DELETE: Delete an object.
  • POST : Create an object.

Please check the JSON:API spec for more implementation details. You can also try out the interface in the live demo.

Resource Objects

Database objects are implemented as subclasses of the SAFRSBase and SQLAlchemy model classes. The SQLAlchemy columns are serialized to JSON when the corresponding REST API is invoked.

Following example app illustrates how the API is built and documented:

class User(SAFRSBase, db.Model):
    """
        description: User description
    """

    __tablename__ = "Users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    email = db.Column(db.String)

The User class is implemented as a subclass of

  • db.Model: SQLAlchemy base
  • SAFRSBase: Implements JSON serialization for the object and generates (swagger) API documentation

This User object is then exposed through the web interface using the Api object

api.expose_object(User)

The User object REST methods are available on /User, the swagger schema is available on /api/swagger.json and the UI is available on /api/: User Swagger

Relationships

Database object such as the User class from the demo.py example can be extended to include relationships with other objects. The demo_relationship.py contains following extension of the User class where a relationship with the Book class is implemented:

class User(SAFRSBase, db.Model):
    '''
        description: User description
    '''
    __tablename__ = 'Users'
    id = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, default='')
    email = db.Column(db.String, default='')
    books = db.relationship('Book', back_populates="user")
...

A many-to-one database association is declared by the back_populates relationship argument. The Book class is simply another subclass of SAFRSBase and db.Model, similar to the previous User class:

class Book(SAFRSBase, db.Model):
    '''
        description: Book description
    '''
    __tablename__ = 'Books'
    id = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, default='')
    user_id = db.Column(db.String, db.ForeignKey('Users.id'))
    user = db.relationship('User', back_populates='books')

The User.book relationship can be queried in the API through the following endpoints: Relations Swagger

  • POST adds an item to the relationship
  • DELETE removes an item from the relationship
  • GET retrieves a list of item ids

The relationship API endpoints work similarly for one-to-many relationships.

Relationship members can also be included in the response when querying an instance, by specifying the relationship names as a comma separated list in the include query argument.

relationship include swagger

For example, to retrieve all items in the books_read relationship from the People endpoint, you may add the include=books_read url parameter

http://thomaxxl.pythonanywhere.com/api/People/?include=books_read

To retrieve nested relationship items, you can specify the nested relationship name after the '.', to retrieve the authors of the books_read instances for instance, you can use

http://thomaxxl.pythonanywhere.com/api/People/?include=books_read.author

Methods

Custom Methods

Safrs allows the user to implement custom methods on the exposed objects. This methods can be invoked through the json API by sending an HTTP POST request to the method endpoint The following example implements a "send_mail" method fro example:

class User(SAFRSBase, db.Model):
    '''
        description: User description
    '''
    __tablename__ = 'Users'
    id = Column(String, primary_key=True)
    name = Column(String, default='')
    email = Column(String, default='')

    # Following method is exposed through the REST API 
    # This means it can be invoked with a HTTP POST
    @jsonapi_rpc(http_methods=['POST','GET'])
    def send_mail(self, email):
        '''
            description : Send an email
            args:
                email:
                    type : string 
                    example : test email
        '''
        content = 'Mail to {} : {}\n'.format(self.name, email)
        return { 'result' : 'sent {}'.format(content)}

This method shows up in the swagger interface:

Method Swagger

The send_mail method is documented with the jsonapi_rpc decorator. This decorator generates a schema based on the function documentation. This documentation contains yaml specification of the API which is used by the swagger UI.

api_methods.py contains a couple of methods that can be used in your models.

The yaml specification has to be in the first part of the function and class comments. These parts are delimited by four dashes ("----") . The rest of the comment may contain additional documentation.

Class Methods

Two class-level methods have been defined to facilitate object retrieval:

  • lookup : retrieve a list of objects that match the argument list. For example, following HTTP POST request to a container will retrieve a list of itemswhere the name is "thomas"
{
  "method": "lookup",
  "args": {
    "name": "thomas"
  }
}
  • get_list : retrieve a list of the items with the specified ID's

Application Initialization

The API can be initialized like this:

api = SAFRSAPI(app, host=HOST, port=PORT, prefix=API_PREFIX)

Then you can expose objects with

api.expose_object(User)    

An example that uses the flask app factory pattern is implement in examples/mini_app.py

app.config.update(DEBUG=True)

Endpoint Naming

As can be seen in the swagger UI:

  • the endpoint collection path names are the SQLAlchemy __tablename__ properties (e.g. /Users )
  • the parameter names are derived from the SAFRSBase class names (e.g. {UserId} )
  • the the relationship names are the SAFRSBase class relationship names (e.g /books ) The URL path format is configurable

Configuration

Some configuration parameters can be set in config.py:

  • USE_API_METHODS: set this to false in case you want to disable the jsonapi_rpc functionality
  • INSTANCE_URL_FMT: This parameter declares the instance url path format
  • RELATIONSHIP_URL_FMT: This parameter declares the relationship endpoint path format

Exposing Existing Databases

Safrs allows you to Expose existing databases as jsona:api services with the expose_existing.py script, for example:

python3 expose_existing.py mysql+pymysql://root:[email protected]/sakila --host localhost

This script uses sqlacodegen to generate a source file containing the SQLAlchemy and SAFRSBase database models and starts the API webservice.

More details here

More Examples and Use Cases

The examples folder contains more example scripts:

  • Using a sha hash as primary key (id)
  • CORS usage
  • Flask-Admin integration example, eg.: demo

A docker image can be found here: https://github.com/thomaxxl/safrs-example

Advanced Usage

Filtering

The swagger shows the jsonapi filters that can be used in the url query arguments. Items with an exact match of the specified attribute value can be fetched by specifying the corresponding key-value query parameter. For example, suppose the User class, exposed at /Users has a name attribute, to retrieve all instances with the name "John", you can use a GET request to /Users?filter[name]=John.

It is also possible to use more generic filters by specifiying a JSON string, for example filter=[{"name":"timestamp","op":"gt","val":"2020-08-01"},{"name":"timestamp","op":"lt","val":"2020-08-02"}].

More info can be found in the wiki.

Custom Serialization

Serialization and deserialization are implemented by the SAFRSBase to_dict and __init__ : you can extend these methods as usual. For example, if you would like to add some attributes to the json payload of the User object, you can override the to_dict method:

class User(SAFRSBase, db.Model):
    '''
        description: User description
    '''
    __tablename__ = 'Users'
    id = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, default='')
    email = db.Column(db.String, default='')
    books = db.relationship('Book', back_populates="user")

    def to_dict(self):
        result = SAFRSBase.to_dict(self)
        result['custom_field'] = 'custom'
        return result

This will add the custom_field attribute to the result attributes:

"attributes": {
    "custom_field": "custom",
    "email": "reader_email0",
    "name": "Reader 0"
}

Excluding Attributes and Relationships

It is possible to specify attributes and relationships that should not be serialized by specifying the respective exclude_attrs and èxclude_rels` class attributes in your SAFRSBase instances. Examples can be found here and here

Limiting HTTP Methods

It is possible to limit the HTTP methods that are allowed by overriding the http_methods class attribute. An example can be found here

HTTP Decorators

The decorators class attribute list can be used to add custom decorators to the HTTP endpoints. An example of this functionality is implemented in the authentication examples.

API Methods

Some additional API RPC methods are implemented in api_methods.py, e.g. mysql regex search.

Custom swagger

The swagger schema can be merged with a modified schema dictionary by supplying the to-be-merged dictionary as the custom_swagger argument to SAFRSAPI, e.g.

custom_swagger = {"info": {"title" : "New Title" }} # Customized swagger title will be merged
api = SAFRSAPI(app, host=swagger_host, port=PORT, prefix=OAS_PREFIX, api_spec_url=OAS_PREFIX+'/swagger',
               custom_swagger=custom_swagger, schemes=['http', 'https'], description=description)

Classes Without SQLAlchemy Models

You can implement a serializable class without a model but this requires some extra work because safrs needs to know which attributes and relationships to serialize. An example is implemented here

More Customization

The documentation is being moved to the wiki

Limitations & TODOs

This code was developed for a specific use-case and may not be flexible enough for everyone's needs. A lot of the functionality is available but not documented for the sake of brevity. Performance is reasonable for regular databases, but once you start exposing really big tables you may run into problems, for example: the count() for mysql innodb is slow on large(1M rows) tables, a workaround can be implemented by querying the sys tables or using werkzeug caching. Feel free to open an issue or drop me an email if you run into problems or something isn't clear!

References

Thanks

I developed this code when I worked at Excellium Services. They allowed me to open source it when I stopped working there.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].