Back to Scaling Python Applications guides

Getting Started with GraphQL in Django

Stanley Ulili
Updated on July 1, 2025

Django is one of Python's most popular web frameworks. It comes with everything you need to build web applications right out of the box.

When you combine Django with GraphQL, you get a powerful way to build APIs that give clients exactly the data they need. No more over-fetching or under-fetching data.

This guide will show you how to build GraphQL APIs using Django and Graphene-Django. You'll learn everything from basic setup to advanced features.

Prerequisites

You need Python 3.8 or newer installed on your computer. This tutorial assumes you are familiar with Django basics, Python classes, and database concepts.

Building your Django GraphQL foundation

Let's create a Django project so you can follow along and test everything yourself.

Start by creating a new project folder and setting up a virtual environment:

 
mkdir django-graphql-library && cd django-graphql-library
 
python3 -m venv env
 
source env/bin/activate 

Install the packages you need for GraphQL:

 
pip install django graphene-django django-filter

Here's what each package does:

  • django: The main web framework that handles routing, database operations, and app structure.
  • graphene-django: Adds GraphQL support to Django with helpful tools and decorators.
  • django-filter: Lets you add filtering and searching to your GraphQL queries.

Create your Django project and main app:

 
django-admin startproject library_project .
 
python manage.py startapp books

Update your settings.py to include the new apps:

settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
'graphene_django',
'books',
] ...
# GraphQL configuration
GRAPHENE = {
'SCHEMA': 'library_project.schema.schema'
}

Create your database models in books/models.py:

books/models.py
from django.db import models
from django.contrib.auth.models import User

class Publisher(models.Model):
    name = models.CharField(max_length=200)
    website = models.URLField(blank=True)
    established_year = models.IntegerField()

    def __str__(self):
        return self.name

class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    birth_date = models.DateField()
    biography = models.TextField(blank=True)

    def __str__(self):
        return f"{self.first_name} {self.last_name}"

class Book(models.Model):
    title = models.CharField(max_length=200)
    isbn = models.CharField(max_length=17, unique=True)
    publication_date = models.DateField()
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE, related_name='books')

    def __str__(self):
        return self.title

These models create a simple book library structure. The related_name attributes create reverse relationships that make it easy to access related data through GraphQL.

Run the migrations to create your database tables:

 
python manage.py makemigrations
 
python manage.py migrate

Create a basic GraphQL schema. Add a new file called schema.py inside your library_project directory (not in the root):

library_project/schema.py
import graphene
from graphene_django import DjangoObjectType
from books.models import Book, Author, Publisher

class BookType(DjangoObjectType):
    class Meta:
        model = Book
        fields = '__all__'

class AuthorType(DjangoObjectType):
    class Meta:
        model = Author
        fields = '__all__'

class PublisherType(DjangoObjectType):
    class Meta:
        model = Publisher
        fields = '__all__'

class Query(graphene.ObjectType):
    all_books = graphene.List(BookType)
    all_authors = graphene.List(AuthorType)
    all_publishers = graphene.List(PublisherType)

    def resolve_all_books(self, info):
        return Book.objects.all()

    def resolve_all_authors(self, info):
        return Author.objects.all()

    def resolve_all_publishers(self, info):
        return Publisher.objects.all()

schema = graphene.Schema(query=Query)

This schema shows how Graphene works with Django. The DjangoObjectType automatically creates GraphQL types from your Django models. The resolver methods use Django's ORM to get data from the database.

Add GraphQL routing to your main urls.py:

library_project/urls.py
from django.contrib import admin
from django.urls import path
from graphene_django.views import GraphQLView
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [ path('admin/', admin.site.urls),
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
]

Start your development server:

 
python manage.py runserver

Go to http://localhost:8000/graphql/ in your browser. You'll see the GraphiQL interface where you can test your GraphQL API:

Screenshot of the GraphQL interface in the browser

The interface features auto-completion and allows you to explore your schema.

Testing your basic GraphQL setup

Before adding more complex features, let's test what you've built so far. You need some sample data to work with, so let's add a few records through Django's admin interface.

First, create a superuser account:

 
python manage.py createsuperuser

Follow the prompts to create your admin user. Then register your models in books/admin.py:

books/admin.py
from django.contrib import admin
from .models import Publisher, Author, Book

@admin.register(Publisher)
class PublisherAdmin(admin.ModelAdmin):
    list_display = ['name', 'established_year']

@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
    list_display = ['first_name', 'last_name', 'birth_date']

@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'publisher', 'publication_date']
    list_filter = ['publisher', 'publication_date']

Start your server and navigate to the admin interface:

 
python manage.py runserver

Go to http://localhost:8000/admin/ and log in with your superuser credentials.

Let's start by adding a publisher. Click on "Publishers" under the "BOOKS" section, then click the "Add Publisher" button. Fill in the form with "Penguin Books" as the name, "https://www.penguin.com" as the website (this field is optional), and 1935 as the established year. Click "Save" when you're done.

Screenshot showing the Django admin add publisher form

Next, add an author. Go back to the main admin page and click on "Authors" under the "BOOKS" section. Click the "Add Author" button and fill in "George" as the first name, "Orwell" as the last name, and use the date picker to set the birth date to 1903-06-25 (you can also type it in YYYY-MM-DD format). You can add "English novelist and critic" in the biography field if you want, but it's optional. Save the author when you're finished.

Screenshot showing the Django admin add author form

Now you can create a book that connects your author and publisher. Go back to the main admin page and click on "Books". Click "Add Book" and fill in "1984" as the title, "978-0-452-28423-4" as the ISBN, 1949-06-08 as the publication date, 328 for pages, and 14.99 as the price. Use the dropdown menus to select "George Orwell" as the author and "Penguin Books" as the publisher. Save your book.

Screenshot showing the Django admin add book form

You should now see your book listed in the Books admin page with all the related information displayed:

Screenshot showing the Django admin books list with the new book

Now you have sample data to test your GraphQL queries. Head over to http://localhost:8000/graphql/ and try this query to get all your books:

 
{
  allBooks {
    id
    title
    isbn
    pages
    price
  }
}

You should see a response like this in the GraphiQL interface showing your book data:

Screenshot showing the all books query results in GraphiQL

Try querying all authors to see your author information:

 
{
  allAuthors {
    id
    firstName
    lastName
    biography
  }
}

The GraphiQL interface will display your author data:

Screenshot showing the all authors query results in GraphiQL

Here's where GraphQL really shines. You can fetch books along with their related author and publisher information in a single query:

 
{
  allBooks {
    title
    isbn
    author {
      firstName
      lastName
    }
    publisher {
      name
      establishedYear
    }
  }
}

This query demonstrates GraphQL's power to traverse relationships and get exactly the data you need:

Screenshot showing the books with relationships query results in GraphiQL

Notice how GraphQL returns your data in JSON format and lets you request exactly the fields you want. You can explore what other fields are available by using the "Docs" panel on the left side of the GraphiQL interface:

Screenshot showing the GraphiQL documentation panel

If you get empty results, double-check that you've added the data through the admin interface. The GraphiQL interface will help you debug any issues with helpful error messages.

Adding filters to your GraphQL queries

So far, you can query all books, authors, and publishers, but real applications need the ability to search and filter results. Let's add filtering capabilities using django-filter and Graphene's built-in filtering tools.

You'll use DjangoFilterConnectionField to allow clients to pass query parameters like title, publicationDate, or price directly into their GraphQL requests. This creates a much more flexible API that can handle complex search requirements.

Update your library_project/schema.py to include filtering capabilities:

library_project/schema.py
import graphene
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from graphene import relay
from books.models import Book, Author, Publisher class BookType(DjangoObjectType): class Meta: model = Book fields = '__all__'
filter_fields = {
'title': ['exact', 'icontains'],
'publication_date': ['exact', 'gte', 'lte'],
'price': ['exact', 'gte', 'lte'],
'author__first_name': ['exact', 'icontains'],
'author__last_name': ['exact', 'icontains'],
'publisher__name': ['exact', 'icontains'],
}
interfaces = (relay.Node,)
class AuthorType(DjangoObjectType): class Meta: model = Author fields = '__all__'
filter_fields = {
'first_name': ['exact', 'icontains'],
'last_name': ['exact', 'icontains'],
}
interfaces = (relay.Node,)
class PublisherType(DjangoObjectType): class Meta: model = Publisher fields = '__all__'
filter_fields = {
'name': ['exact', 'icontains'],
'established_year': ['exact', 'gte', 'lte'],
}
interfaces = (relay.Node,)
class Query(graphene.ObjectType):
# Relay-style filtered queries
book = relay.Node.Field(BookType)
all_books = DjangoFilterConnectionField(BookType)
author = relay.Node.Field(AuthorType)
all_authors = DjangoFilterConnectionField(AuthorType)
publisher = relay.Node.Field(PublisherType)
all_publishers = DjangoFilterConnectionField(PublisherType)
schema = graphene.Schema(query=Query)

The key changes here introduce relay-style connections through the Node interface and DjangoFilterConnectionField. The filter_fields dictionary specifies which fields can be filtered and what types of filtering operations are available. For example, icontains allows case-insensitive partial matching, while gte and lte enable range queries for dates and numbers.

Now test your filtering capabilities. Go to the GraphiQL interface at http://localhost:8000/graphql/ and try this query to find books with "1984" in the title:

 
{
  allBooks(title_Icontains: "1984") {
    edges {
      node {
        title
        isbn
        author {
          firstName
          lastName
        }
        publisher {
          name
        }
      }
    }
  }
}

You should see results that match your search criteria:

Screenshot showing filtered books query results in GraphiQL

You can also filter on related fields. Try this query to find all books by authors with "Orwell" in their last name:

 
{
  allBooks(author_LastName_Icontains: "Orwell") {
    edges {
      node {
        title
        author {
          firstName
          lastName
        }
        publicationDate
      }
    }
  }
}

Notice the author_LastName_Icontains field. It uses Django's double underscore syntax (author__last_name) under the hood, converted for GraphQL naming conventions:

Screenshot showing author-filtered books query results in GraphiQL

This filtering foundation sets you up perfectly for the next step: adding mutations to create, update, and delete data through your GraphQL API.

Adding mutations to your GraphQL API

Up to this point, your GraphQL API can read data from the database using queries—and even filter that data with flexible options. But most real-world apps also need to write data: things like adding new authors, updating book details, or deleting publishers.

That's where mutations come in. With Graphene, you can define mutations that directly connect to your Django models. In this section, you'll add your first mutation: one that creates new authors.

Inside your books app directory, create a new file named mutations.py. This keeps your mutations organized and separate from your main schema file, which is a good practice as your API grows.

Add the following code:

books/mutations.py
import graphene
from graphene_django.types import DjangoObjectType
from books.models import Book, Author, Publisher
from graphene import Mutation, Field, String, Int, Float, ID
from datetime import datetime

class AuthorType(DjangoObjectType):
    class Meta:
        model = Author
        fields = '__all__'

class CreateAuthor(Mutation):
    class Arguments:
        first_name = String(required=True)
        last_name = String(required=True)
        birth_date = String(required=True)
        biography = String()

    author = Field(AuthorType)

    def mutate(self, info, first_name, last_name, birth_date, biography=""):
        author = Author(
            first_name=first_name,
            last_name=last_name,
            birth_date=datetime.strptime(birth_date, "%Y-%m-%d").date(),
            biography=biography,
        )
        author.save()
        return CreateAuthor(author=author)

This defines a mutation called CreateAuthor. You can pass in author details as arguments, and it saves the new record to the database. Notice how we handle the date conversion from string to Python date object.

In library_project/schema.py, import your mutation and update the schema:

library_project/schema.py
import graphene
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from graphene import relay
from books.models import Book, Author, Publisher
from books.mutations import CreateAuthor
# ... your existing BookType, AuthorType, PublisherType classes ... # ... your existing Query class ...
class Mutation(graphene.ObjectType):
create_author = CreateAuthor.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)

Now you can create an author directly from GraphQL. Go to the GraphiQL interface and run this mutation:

 
mutation {
  createAuthor(
    firstName: "Aldous"
    lastName: "Huxley"
    birthDate: "1894-07-26"
    biography: "Author of Brave New World"
  ) {
    author {
      id
      firstName
      lastName
      biography
    }
  }
}

You should see a response showing your newly created author:

Screenshot showing the create author mutation results in GraphiQL

You've now added your first mutation and can create new records through GraphQL. This lays the groundwork for adding more mutations to handle books, publishers, updates, and deletions.

Final thoughts

You've built a complete GraphQL API with Django and Graphene that handles queries, filtering, and mutations. Your API can read data efficiently, search records with flexible filters, and create new authors through GraphQL.

This foundation enables you to easily add, update, and delete mutations, authentication, or real-time features as your application evolves.

For more information, check out the Graphene-Django documentation and the Django documentation.

Happy coding with Django and GraphQL!

Make your mark

Join the writer's program

Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.

Write for us
Writer of the month
Marin Bezhanov
Marin is a software engineer and architect with a broad range of experience working...
Build on top of Better Stack

Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.

community@betterstack.com

or submit a pull request and help us build better products for everyone.

See the full list of amazing projects on github