All Projects → sebastiandev → peach

sebastiandev / peach

Licence: MIT license
Lightweight framework built on top of flask, falcon with a touch of magic

Programming Languages

python
139335 projects - #7 most used programming language

Projects that are alternatives of or similar to peach

Falcon
A high-performance web server for Ruby, supporting HTTP/1, HTTP/2 and TLS.
Stars: ✭ 2,058 (+10731.58%)
Mutual labels:  falcon
Hug
Embrace the APIs of the future. Hug aims to make developing APIs as simple as possible, but no simpler.
Stars: ✭ 6,572 (+34489.47%)
Mutual labels:  falcon
Umpc Ubuntu
Scripts for UMPCs such as GPD Pocket, Pocket 2, MicroPC & TopJoy Falcon
Stars: ✭ 90 (+373.68%)
Mutual labels:  falcon
Responder
A familiar HTTP Service Framework for Python.
Stars: ✭ 3,569 (+18684.21%)
Mutual labels:  falcon
Falcon
Free, open-source SQL client for Windows and Mac 🦅
Stars: ✭ 4,848 (+25415.79%)
Mutual labels:  falcon
Fastdeploy
Deploy DL/ ML inference pipelines with minimal extra code.
Stars: ✭ 35 (+84.21%)
Mutual labels:  falcon
falcon-policy
Policy Middleware for Falcon APIs
Stars: ✭ 30 (+57.89%)
Mutual labels:  falcon
flask-restful-api-template
Template for flask-restful api project
Stars: ✭ 71 (+273.68%)
Mutual labels:  flask-restful
Falcon Plus
An open-source and enterprise-level monitoring system.
Stars: ✭ 6,770 (+35531.58%)
Mutual labels:  falcon
Graceful
Elegant Python REST toolkit built on top of falcon
Stars: ✭ 73 (+284.21%)
Mutual labels:  falcon
Falcon
Take Android screenshots with Falcons bright eye!
Stars: ✭ 362 (+1805.26%)
Mutual labels:  falcon
Swagger Py Codegen
a Python web framework generator supports Flask, Tornado, Falcon, Sanic
Stars: ✭ 508 (+2573.68%)
Mutual labels:  falcon
Ghidra falcon
Support of Nvidia Falcon processors for Ghidra (WIP)
Stars: ✭ 39 (+105.26%)
Mutual labels:  falcon
Dashboard
falcon-plus frontend
Stars: ✭ 344 (+1710.53%)
Mutual labels:  falcon
Spacy Services
💫 REST microservices for various spaCy-related tasks
Stars: ✭ 230 (+1110.53%)
Mutual labels:  falcon
openverse-api
The Openverse API allows programmatic access to search for CC-licensed and public domain digital media.
Stars: ✭ 41 (+115.79%)
Mutual labels:  falcon
Falcon Celery
Example of how to handle background processes with Falcon, Celery, and Docker
Stars: ✭ 29 (+52.63%)
Mutual labels:  falcon
pylodon
Flask-based ActivityPub server
Stars: ✭ 86 (+352.63%)
Mutual labels:  flask-restful
flask-rest-paginate
Pagination Extension for flask-restful
Stars: ✭ 18 (-5.26%)
Mutual labels:  flask-restful
Webargs
A friendly library for parsing HTTP request arguments, with built-in support for popular web frameworks, including Flask, Django, Bottle, Tornado, Pyramid, webapp2, Falcon, and aiohttp.
Stars: ✭ 1,145 (+5926.32%)
Mutual labels:  falcon

Peach

Peaches are sweet and awesome with a strong core, and so is Peach which is built on top of great frameworks with a touch of magic to make building apis less tedious, letting you focus on the business logic rather than on the database abstractions, endpoint routing and configurations, blueprint definitions or request parsing.

Many times you have an idea or prototype you want to code but you haven't decide yet which is the web framework that best suits your needs. Switching from one framework to another is time consuming because you have to adapt your logic with the new frameworks design, and one doesn't usually code thinking about switching frameworks.

Peach was designed in a way that it can sit on top of a framework without affecting the way you code your business logic, allowing to implement an api that would run on different frameworks, with almost zero code changes. This is possible because lightweight frameworks are mostly about dealing with requests and responses, so once you abstract that from your design, you are almost framework agnostic (not 100% but pretty close).

So far Peach works with:

Peach also comes with some nice out of the box features such as automagic wiring between resource, models, database and model filtering. If you like some of Django's magic, but love flask and its minimalistic philosophy, then you'll love Peach.

Peach runs on python3 so in order to install, do:

sudo pip3 install python-peach

Tired of reading docs? Okay, here's a full example of a very simple api from scratch

import os
from peach import Peach
from peach.handlers.flask import FlaskHandler
from peach.utils import module_dir


Peach.init(config=os.path.dirname(module_dir(__file__)), handler=FlaskHandler())  # Path to the config.py file directory


from peach.handlers.flask.resource import FlaskBaseResource
from peach.rest.seralizers import ModelSerializer
from peach.filters.mongo import NameFilter
from peach.models import BaseModel
from marshmallow import fields


class People(BaseModel):

    collection_name = 'people'

    def __init__(self, name=None, age=None, address=None, **kwargs):
        super().__init__(**{
            'name': name,
            'age': age,
            'address': address
        }, **kwargs)

    @classmethod
    def build(cls, doc):
        return People(**doc) if doc else None


class PeopleSerializer(ModelSerializer):

    model = People

    name = fields.String()
    age = fields.Int()
    address = fields.String()


class AddressFilter(BaseFilter):

    name = 'address'
    value_type = str
    allow_multiple = False

    @classmethod
    def condition(cls, address):
        return {
            'address': {
                "$regex": ".*?{}.*?".format(address),
                "$options": 'si'
            }
        }


class PeopleResource(FlaskBaseResource):

    model = People
    serializer = PeopleSerializer
    filters = [NameFilter, AddressFilter]
    

if __name__ == '__main__':
    Peach().run(port=3000, host='0.0.0.0')

There's only one more file needed, which is the config file.

If you use Flask, You may have read about the configuring options, there's usually a config file placed under the source folder. For more info read here

APIS = {
    'api': {
        'prefix': '/api',
        'name': 'People Api',
        'version': '0.0.1',
        'endpoints': [
            {
                'name': 'people',
                'class': 'myapp.app.PeopleResource',
                'urls': [
                    '/people',
                    '/people/<string:ids>'
                ]
            }
         ]
    }
}


DATABASE = {
    'proxy': 'peach.database.mongo_proxy.MongoDBProxy',
    'uri': 'mongodb://localhost:27017/',
    'name': 'PeopleDB'
}

That's all you need to get your api up and running! Sweet.

Have some time to read and want to learn more about it? Let's analyze each part of the example

Models

Just like in any other framework there are models, these are usually very dependent on the database engine mostly because of the CRUD operations performed on the database. Peach offers a base class for models that abstracts many of the internals of accesing the database, and it also offers a proxy, with whom the base model interacts with, allowing to interact in the same way with different database engines. So far there's only one implementation of a proxy, and it is for MongoDB

class People(BaseModel):

    collection_name = 'people'

    def __init__(self, name=None, age=None, addres=None, **kwargs):
        super().__init__(**{
            'name': name,
            'age': age,
            'address': address
        }, **kwargs)

    @classmethod
    def build(cls, doc):
        return People(**doc) if doc else None

Here we have defined the People model, since we are working with MongoDB we need to specify a collection name. Then the basic operations are already implemented on the MongoDBProxy. If any other method is needed, like a more complex query using aggregation, it can be added on your model.

Serializers

For every model, there's usually a serializer that handles the representation of the model as a json document. The base serialization class is based on marshmallow, and it only requires you to specify the model is serializing and the fields you want to use from the model

class PeopleSerializer(ModelSerializer):

    model = People

    name = fields.String()
    age = fields.Int()
    address = fields.String()

Filters

Filters are as the name suggests a way of filtering models but in a more decoupled way, so one filter might be re used on different models that share the same attribute, like date, name, or any other you may come up with. They are designed around the idea of applying a condition over a data collection, so, in order to create a filter you need to give it a name, to be identifiable, define the type of the attribute to filter and specify if the filter allows multiple values or not.

class AddressFilter(BaseFilter):

    name = 'addres'
    value_type = str
    allow_multiple = False

    @classmethod
    def condition(cls, address):
        return {
            'account': {
                "$regex": ".*?{}.*?".format(address),
                "$options": 'si'
            }
        }

The BaseFilter class has 3 methods, one builds the condition, another applies the condition on the model and the third counts the amount of docs that satisfiy the condition. Similar to models, filters depend on the database engine, thus the condition will be the method you will always need to override to build the condition in the database sintax. There are some generic filters for mongo db already implemented.

Resources

Sometimes called views in a more simplified version, represent the actual thing (usually a model) you want to operate on. In our example we are dealing with people. Every resource of course has an endpoint and handles certain common operations like paging, sorting and filtering.

from peach.web.flask.resource import FlaskBaseResource


class PeopleResource(FlaskBaseResource):

    model = People
    serializer = PeopleSerializer
    filters = [NameFilter, AddressFilter]

or if you prefer Falcon

from peach.handlers.falcon.resource import FalconBaseResource


class PeopleResource(FalconBaseResource):

    model = People
    serializer = PeopleSerializer
    filters = [NameFilter, AddressFilter]

The base class for resources BaseResource handles all the wiring for the most basic operations. All you need to do in the most simple case is define the model, the serializer to be used and the available filters.

In order to filter a resource, you need to add the filter the query string like this:

curl -GET localhost:3000/api/people?filter[name]=foo
curl -GET localhost:3000/api/people?filter[name]=foo,mary

The standard sintax is filter[FILTER NAME]=FILTER VALUE

If your query/filter returns many results, you can ask for a diffrent page or define the page size like this:

curl -GET localhost:3000/api/people?filter[name]=foo&page[number]=2  # to get the secondn page
curl -GET localhost:3000/api/people?page[size]=20&page[number]=4  # to use a page size of 20 and get the 4th page

If you need your results to be sorted by any of the model attributes, you can do it by using the sort keyword.

curl -GET localhost:3000/api/people?filter[name]=foo&sort=name
curl -GET localhost:3000/api/people?page[size]=20&sort=<name,>age

You can specify the ordering by prepending < (Descending) or > (Ascending) the attribute name. You can combine sorting with different attributes by specifying more attributes as a comma separated list.

Running your app

In order to run your app you need to create the application object. Before doing anything, and after importing peach, you MUST define the path for you config file. This is usually placed under the source directory. If you have a script that populates your database or does anything that deals with models, you MUST initialize peach with the instance_path before doing anything or importing any model.

Initialization

For the configuration you can use the flask standard way of defining instance's path, or you can pass the absolute path to your config file or you can pass a dictionary. Did I say that this is the first thing you MUST do after import peach? Yeah...

from peach import Peach
from peach.handlers.flask import FlaskHandler
from peach.utils import module_dir

# The usual flask way
Peach.init(config=module_dir(__file__), handler=FlaskHandler())  # here the path is taken from the current file

# Using falcon
from peach.handlers.falcon import FalconHandler
import config
Peach.init(config=config, handler=FalconHandler())  # here the path is taken from the current file


# Using a custom file (variable names should always be in CAPITALS)
Peach.init(config=path_to_a_file, handler=FalconHandler())  # This should be an absolute path to a file

# Using a dict (variable names should always be in CAPITALS)
Peach.init(config={}, handler=FalconHandler())  # A standard dict, maybe usefull for test cases


# Do whatever you need now ...

Peach().run(port=3000, host='0.0.0.0') 

Customizations

There are always corner cases or different needs that require changes. Peach offers a good balance between features, organization and soft rules to allow for further customizations without forcing the code changes to be overcomplicated or look like ad hoc hacks. Nevertheless, there are better places than others to make the needed changes or extensions and here you will get an idea of possible extensions and the best place to put them.

Pagination

Peach ships with built in pagination but you can provide you own logic as needed. When building the api, some objects are injected to the base resource like the databae proxy, the response factory and the pagination class. If you want to provide your own pagination, simply add one line in your api configuration specifying the module path for you pagination.

APIS = {
    'api': {
        'prefix': '/api',
        'name': 'People Api',
        'version': '0.0.1',
        'pagination': 'peach.rest.pagination.Pagination',
        'endpoints': [
            {
                'name': 'people',
                'class': 'myapp.app.PeopleResource',
                'urls': [
                    '/people',
                    '/people/<string:ids>'
                ]
            }
         ]
    }
}

Response formats

The response is a way of rendering the returned data from the api. There are many ways of formating a response. A popular one is JSONApi but it is also a very robust and copmlex format. The default response format used in Peach resembles a bit of JSONApi but just a bit, if you need to implement something like that or any other formating, the best place to put this is as a new response class. For this you need to define the response factory in the api configuration

APIS = {
    'api': {
        'prefix': '/api',
        'name': 'People Api',
        'version': '0.0.1',
        'response_factory': 'peach.rest.response.ResponseFactory',
        'endpoints': [
            {
                'name': 'people',
                'class': 'myapp.app.PeopleResource',
                'urls': [
                    '/people',
                    '/people/<string:ids>'
                ]
            }
         ]
    }
}

The response factory receives the data to render, any metadata needed to be displayed (in a form of a dict), a pagination object and a variable kwargs for any extra parameters. With this you should be able to render your custom response. Of course, following the JSONApi format, the very same resource seriralization requires a specific formating and to accomplish this you also need to define your model serialization accordingly (for this you'll probably need to dive deeper into marshmallow)

More dependencies for your resources

If need to inject more objects/modules to your resources, just extend the ApiFactory, extending for you selected framework, flaskand or falcon, and override the load_api_resource_params method to populate the Conf object with whatever you need. This method receives the app and the api configuration defined in the config.py module.

from peach.handlers.flask.api import FlaskApiFactory


class MyApiFactory(FlaskApiFactory):

    @classmethod
    def load_api_resource_params(cls, app_conf, api_conf):
        conf = super().load_conf(app_conf, api_conf)
        # add your stuff here
        
        return conf

And then pass it when initializing the handler fro you peach app

from peach import Peach
from peach.handlers.flask import FlaskHandler

Peach.init(config=config, handler=FlaskHandler(MyApiFactory()))
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].