# Getting Started with GraphQL in Django

[Django](https://www.djangoproject.com/) 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](https://graphql.org/), 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](https://docs.graphene-python.org/projects/django/en/latest/). You'll learn everything from basic setup to advanced features.

[ad-logs]

## Prerequisites

You need [Python 3.8](https://www.python.org/downloads/) 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:

```command
mkdir django-graphql-library && cd django-graphql-library
```

```command
python3 -m venv env
```
```command
source env/bin/activate 
```

Install the packages you need for GraphQL:

```command
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:

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

Update your `settings.py` to include the new apps:

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

Create your database models in `books/models.py`:

```python
[label 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:

```command
python manage.py makemigrations
```
```command
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):

```python
[label 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`:

```python
[label library_project/urls.py]
from django.contrib import admin
from django.urls import path
[highlight]
from graphene_django.views import GraphQLView
from django.views.decorators.csrf import csrf_exempt
[/highlight]


urlpatterns = [
    path('admin/', admin.site.urls),
[highlight]
    path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True))),
[/highlight]
]
```

Start your development server:

```command
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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/e0aef389-a88a-41ec-be72-16b7c793d000/md2x =3248x1996)


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:

```command
python manage.py createsuperuser
```

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

```python
[label 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:

```command
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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/f85164eb-d30d-4144-0b0f-811bb558a400/lg1x =3248x1996)

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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/ffedaf26-bcc3-4862-42b7-4bed16a2a100/orig =3248x1996)

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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/294e8308-3bf4-47f4-0502-75e84cef2900/orig =3248x1996)

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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/99e8ec6a-f71e-45a9-75f1-7ecf2d954b00/lg2x =3248x1996)

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:

```graphql
{
  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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/67735d52-ebe1-4bb9-ac6d-be59fe88d900/public =3248x1996)

Try querying all authors to see your author information:

```graphql
{
  allAuthors {
    id
    firstName
    lastName
    biography
  }
}
```

The GraphiQL interface will display your author data:

![Screenshot showing the all authors query results in GraphiQL](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/0eb8960a-c0e9-4927-17f1-65a2c9efe000/md1x =3248x1996)

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

```graphql
{
  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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/674e3dcd-fe01-406e-36d0-9a1294ab8f00/md2x =3248x1996)

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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/1f0d6ebc-d0ca-4795-c009-92cbebe24500/md1x =3248x1996)

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:

```python
[label library_project/schema.py]
import graphene
from graphene_django import DjangoObjectType
[highlight]
from graphene_django.filter import DjangoFilterConnectionField
from graphene import relay
[/highlight]
from books.models import Book, Author, Publisher

class BookType(DjangoObjectType):
    class Meta:
        model = Book
        fields = '__all__'
        [highlight]
        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,)
        [/highlight]

class AuthorType(DjangoObjectType):
    class Meta:
        model = Author
        fields = '__all__'
        [highlight]
        filter_fields = {
            'first_name': ['exact', 'icontains'],
            'last_name': ['exact', 'icontains'],
        }
        interfaces = (relay.Node,)
        [/highlight]

class PublisherType(DjangoObjectType):
    class Meta:
        model = Publisher
        fields = '__all__'
        [highlight]
        filter_fields = {
            'name': ['exact', 'icontains'],
            'established_year': ['exact', 'gte', 'lte'],
        }
        interfaces = (relay.Node,)
        [/highlight]

class Query(graphene.ObjectType):
    [highlight]
    # 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)
    [/highlight]

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:

```graphql
{
  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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/adf4368d-fe1a-466c-c3d4-48c16e1e9500/md1x =3248x1996)

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

```graphql
{
  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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/8115ffb9-ac4e-4752-46a1-3ee16c995a00/lg2x =3248x1996)

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:

```python
[label 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:

```python
[label 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
[highlight]
from books.mutations import CreateAuthor
[/highlight]

# ... your existing BookType, AuthorType, PublisherType classes ...

# ... your existing Query class ...

[highlight]
class Mutation(graphene.ObjectType):
    create_author = CreateAuthor.Field()

schema = graphene.Schema(query=Query, mutation=Mutation)
[/highlight]
```

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

```graphql
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](https://imagedelivery.net/xZXo0QFi-1_4Zimer-T0XQ/2396e710-54a2-4fa1-a5bf-f6fa0e5d2c00/lg2x =3248x1996)

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](https://docs.graphene-python.org/projects/django/en/latest/) and the [Django documentation](https://docs.djangoproject.com/).

Happy coding with Django and GraphQL!