Demonstrating django-graphene when moving from Django Rest Framework
Switch branches/tags
Nothing to show
Clone or download

README.md

Graphy - a demo of Graphene-Django

Code and demonstation of the transition from Django REST Framework (DRF) to Graphene-Django.

Parts of the documentation of Graphene is currently a bit slim. The purpose of this repository is to show code examples compared against DRF, and help getting started with GraphQL on a Django backend, or transitioning from DRF.

Slides used for related Python meetup talk can be found here

Summary

  • Transition from DRF is surprisingly easy, and is done in quite few lines of code.
  • Documentation from Django help_text will be reused in schema generated from GraphQL.
  • You can reuse existing DRF serializers and validation
  • Addressing authentication and authorization is not a part of GraphQL spec and there's little help in Graphene or documentation about it. Own implementation can be found at graphy/utils/graphql.py
  • Graphene performs comparably to DRF even if you have "ideal" REST endpoints that avoid overfetching.

Examples

  • 3d55467 - Adding your first GraphQL endpoint and query.
  • c71967a - Adding your first GraphQL test.
  • e7a366c - Reusing DRF serializer to create your first mutation.
  • 3a72f29 - @auth_required wrapper added, returning a HTTP 401 or error object (Two different views).
  • ece2cf0 - Disallowing queries over a given depth.

Performance comparison

Since the nature of GraphQL and REST APIs are quite different, a performance comparision might not map to the differences in your real life situation.

While it makes perfect sense to prefetch (select_related with Django ORM) to a REST endpoint that needs the prefetched data, the same can not be said for GraphQL endpoints, since a signle GraphQL Query (or "endpoint" if you talk REST) can be used for different cases.

For an ideal implmentation (performance wise), where a Graphene Query and a DRF endpoint is implemented to serve a single, given data request, it looks like DRF is performing better: 1-5% when the select_related is not used, and 10-50% when select_related is used. The relative difference seems to to be larger the more instances are returned.

For real life implementations, overfetching is a problem that GraphQL avoids to a much larger degree than REST, and we've seen (large) performance gains at Otovo when switching from DRF to GraphQL. But be aware that GraphQL list queries where nested models are fetched can be slow when using Graphene naively. Consider adding own endpoints using select_related for such queris, or implementing assistance that modifies the database query based on the GraphQL Query.

Details of tests can be found in

  • ./graphy/location/views.py (DRF)
  • ./graphy/location/gql_actions.py (Graphene)
  • ./graphy/location/tests/test_gql_vs_drf_performance.py (GraphQL Query / REST call)

Numbers below are an average of 500 requests done when using curl against a non-debug server running postgres.

WITHOUT select_related

Type Avg time Returned objects
Shallow GraphQL query 19.2 ms 1
DRF 27.9 ms 1
Deep GraphQL query 28.1 ms 1
Shallow GraphQL query 29.8 ms 100
DRF 422.1 ms 100
Deep GraphQL query 441.6 ms 100

WITH select_related

Type Avg time Returned objects
Shallow GraphQL query 22.8 ms 1
DRF 24.5 ms 1
Deep GraphQL query 26.2 ms 1
Shallow GraphQL query 49.9 ms 100
DRF 50.0 ms 100
Deep GraphQL query 75.54 ms 100

Setup

Install python

This repo should work out of the box with python 3.7, and probably most other python 3 versions.

The commands below install 3.7 specifically on Mac / Linux

# See https://github.com/pyenv/pyenv-installer if you got Linux
brew install pyenv

# Installs 3.7.0, specified in .python-version
pyenv install

Install requirements

# Create and activate virtualenv
$(pyenv which python) -m venv .venv
source .venv/bin/activate

pip install -r requirements.txt

Load test data

python manage.py migrate
python manage.py loaddata db.json

This db dump includes a few instances of each model and a superuser with username: admin and password admin (...)

Run server

python manage.py migrate
python manage.py runserver

Admin panel

Admin panel runs at localhost:8000/admin. If you need to, you can create a super user with python manage.py createsuperuser

GraphiQL

Interactive GraphiQL runs at localhost:8000/graphql.

Tests

# Runs all tests
pytest

Related reading