SkillAgentSearch skills...

Turbulette

😓 Turbulette - A batteries-included framework to build high performance, fully async GraphQL APIs

Install / Use

/learn @turbulette/Turbulette

README

Turbulette

<p align="center"> <a class="badge" href="https://github.com/turbulette/turbulette/actions?query=workflow%3ATest"> <img src="https://github.com/turbulette/turbulette/workflows/Test/badge.svg" alt="test"/> </a> <a class="badge" href="https://www.codacy.com/gh/turbulette/turbulette/dashboard?utm_source=github.com&utm_medium=referral&utm_content=turbulette/turbulette&utm_campaign=Badge_Coverage"> <img src="https://app.codacy.com/project/badge/Coverage/e244bb031e044079af419dabd40bb7fc" alt="codacy-coverage"/> </a> <a class="badge" href="https://www.codacy.com/gh/turbulette/turbulette/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=turbulette/turbulette&amp;utm_campaign=Badge_Grade"> <img src="https://app.codacy.com/project/badge/Grade/e244bb031e044079af419dabd40bb7fc" alt="codacy-grade"/> </a> <a class="badge" href="https://pypi.org/project/turbulette/"> <img src="https://img.shields.io/pypi/v/turbulette" alt="pypi"/> </a> <a class="badge" href="https://img.shields.io/pypi/pyversions/turbulette"> <img src="https://img.shields.io/pypi/pyversions/turbulette" alt="py-version"/> </a> <a class="badge" href="https://github.com/turbulette/turbulette/blob/main/LICENSE"> <img src="https://img.shields.io/pypi/l/Turbulette" alt="license"/> </a> <a class="badge" href="http://mypy-lang.org/"> <img src="https://img.shields.io/badge/mypy-checked-blue" alt="mypy"/> </a> <a class="badge" href="https://github.com/psf/black"> <img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="black"/> </a> <a class="badge" href="https://github.com/PyCQA/bandit"> <img src="https://img.shields.io/badge/security-bandit-yellow.svg" alt="bandit"/> </a> <a class="badge" href="https://pre-commit.com/"> <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white" alt="pre-commit"/> </a> <a class="badge" href="https://gitter.im/turbulette/turbulette"> <img src="https://badges.gitter.im/turbulette/turbulette.svg" alt="gitter"/> </a> <a class="badge" href="https://app.netlify.com/sites/turbulette/deploys"> <img src="https://api.netlify.com/api/v1/badges/3d71e7d8-f219-41c3-9dce-1dc0c5b92251/deploy-status" alt="netlify"/> </a> </p> <p align="center">Turbulette packages all you need to build great GraphQL APIs :</p> <p align="center"><strong><em>ASGI framework, GraphQL library, ORM and data validation</em></strong></p>

Documentation : https://turbulette.netlify.app


Features :

  • Split your API in small, independent applications
  • Generate Pydantic models from GraphQL types
  • JWT authentication with refresh and fresh tokens
  • Declarative, powerful and extendable policy-based access control (PBAC)
  • Extendable auth user model with role management
  • Async caching (provided by async-caches)
  • Built-in CLI to manage project, apps, and DB migrations
  • Built-in pytest plugin to quickly test your resolvers
  • Settings management at project and app-level (thanks to simple-settings)
  • CSRF middleware
  • 100% test coverage
  • 100% typed, your IDE will thank you ;)
  • Handcrafted with ā¤ļø, from šŸ‡«šŸ‡·

Requirements

Python 3.6+

šŸ‘ Turbulette makes use of great tools/frameworks and wouldn't exist without them :

  • Ariadne - Schema-first GraphQL library
  • Starlette - The little ASGI framework that shines
  • GINO - Lightweight, async ORM
  • Pydantic - Powerful data validation with type annotations
  • Alembic - Lightweight database migration tool
  • simple-settings - A generic settings system inspired by Django's one
  • async-caches - Async caching library
  • Click - A "Command Line Interface Creation Kit"

Installation

pip install turbulette

You will also need an ASGI server, such as uvicorn :

pip install uvicorn

šŸš€ Quick Start

Here is a short example that demonstrates a minimal project setup.

We will see how to scaffold a simple Turbulette project, create a Turbulette application, and write some GraphQL schema/resolver. It's advisable to start the project in a virtualenv to isolate your dependencies. Here we will be using poetry :

poetry init

Then, install Turbulette from PyPI :

poetry add turbulette

For the rest of the tutorial, we will assume that commands will be executed under the virtualenv. To spawn a shell inside the virtualenv, run :

poetry shell

1: Create a project

First, create a directory that will contain the whole project.

Now, inside this folder, create your Turbulette project using the turb CLI :

turb project eshop

You should get with something like this :

.
└── šŸ“ eshop
    ā”œā”€ā”€ šŸ“ alembic
    │   ā”œā”€ā”€ šŸ“„ env.py
    │   └── šŸ“„ script.py.mako
    ā”œā”€ā”€ šŸ“„ .env
    ā”œā”€ā”€ šŸ“„ alembic.ini
    ā”œā”€ā”€ šŸ“„ app.py
    └── šŸ“„ settings.py

Let's break down the structure :

  • šŸ“ eshop : Here is the so-called Turbulette project folder, it will contain applications and project-level configuration files
  • šŸ“ alembic : Contains the Alembic scripts used when generating/applying DB migrations
    • šŸ“„ env.py
    • šŸ“„ script.py.mako
  • šŸ“„ .env : The actual project settings live here
  • šŸ“„ app.py : Your API entrypoint, it contains the ASGI app
  • šŸ“„ settings.py : Will load settings from .env file

Why have both .env and settings.py?

You don't have to. You can also put all your settings in settings.py. But Turbulette encourage you to follow the twelve-factor methodology, that recommend to separate settings from code because config varies substantially across deploys, code does not. This way, you can untrack .env from version control and only keep tracking settings.py, which will load settings from .env using Starlette's Config object.

2: Create the first app

Now it's time to create a Turbulette application!

Run this command under the project directory (eshop) :

turb app --name account

You need to run turb app under the project dir because the CLI needs to access the almebic.ini file to create the initial database migration.

You should see your new app under the project folder :

.
└── šŸ“ eshop
    ...
    |
    └── šŸ“ account
        ā”œā”€ā”€ šŸ“ graphql
        ā”œā”€ā”€ šŸ“ migrations
        │   └── šŸ“„ 20200926_1508_auto_ef7704f9741f_initial.py
        ā”œā”€ā”€ šŸ“ resolvers
        └── šŸ“„ models.py

Details :

  • šŸ“ graphql : All the GraphQL schema will live here
  • šŸ“ migrations : Will contain database migrations generated by Alembic
  • šŸ“ resolvers : Python package where you will write resolvers binded to the schema
  • šŸ“„ models.py : Will hold GINO models for this app

What is this "initial" python file under šŸ“ migrations?

We won't cover database connection in this quickstart, but note that it's the initial database migration for the account app that creates its dedicated Alembic branch, needed to generate/apply per-app migrations.

Before writing some code, the only thing to do is make Turbulette aware of our lovely account app.

To do this, open šŸ“„ eshop/settings.py and add "eshop.account" to INSTALLED_APPS, so the application is registered and can be picked up by Turbulette at startup :

# List installed Turbulette apps that defines some GraphQL schema
INSTALLED_APPS = ["eshop.account"]

3: GraphQL schema

Now that we have our project scaffold, we can start writing actual schema/code.

Create a schema.gql file in the šŸ“ graphql folder and add this base schema :

extend type Mutation {
    registerCard(input: CreditCard!): SuccessOut!
}

input CreditCard {
    number: String!
    expiration: Date!
    name: String!
}

type SuccessOut {
    success: Boolean
    errors: [String]
}

Note that we extend the type Mutation because Turbulette already defines it. The same goes for Query type

Notice that with use the Date scalar, it's one of the custom scalars provided by Turbulette. It parses string in the ISO8601 date format YYY-MM-DD.

4: Add pydantic model

We want to validate our CreditCard input to ensure the user has entered a valid card number and date. Fortunately, Turbulette integrates with Pydantic, a data validation library that uses python type annotations, and offers a convenient way to generate a Pydantic model from a schema type.

Create a new šŸ“„ pyd_models.py under šŸ“ account :

from turbulette.validation import GraphQLModel
from pydantic import PaymentCardNumber


class CreditCard(GraphQLModel):
    class GraphQL:
        gql_type = "CreditCard"
        fields = {"number": PaymentCardNumber}

What's happening here?

The inherited GraphQLModel class is a pydantic model that knows about the GraphQL schema and can produce pydantic fields from a given GraphQL type. We specify the GraphQL type with the gql_type attribute; it's the only one required.

But we also add a fields attribute to override the type of number field because it is string typed in our schema. If we don't add this, Turbulette will assume that number is a string and will annotate the number field as str. fields is a mapping between GraphQL field names and the type that will override the schema's one.

Let's add another validation check: the expiration date. We want to ensure the user has entered a valid date (i.e., at least greater than now) :

from datetime import datetime
from pydantic import PaymentCardNumber
from turbulette.validation i
View on GitHub
GitHub Stars64
CategoryDevelopment
Updated1y ago
Forks5

Languages

Python

Security Score

85/100

Audited on Dec 6, 2024

No findings