GraphQL API in Django with CRUD operations
Do not miss this exclusive book on Binary Tree Problems. Get it now for free.
Hi readers, this article will cover the following topics:
- What is GraphQL API?
- Using GraphQL in Django
- CRUD Operations on for users in Django
NOTE: This article assumes that you are already familiar with Django framework.
What is GraphQL?
You might have heard of the phrase GraphQL API if you play with APIs, but what is it actually?
GraphQL was develeoped at facebook when they needed a data fetching api for their mobile app powerful enough to handle millions of traffic per second.
Professionally speaking, "GraphQL is an open-source query and data manipulation language for APIs".
It means it's simply a query or an API request(similar to REST-API request) but it's a smart query, because it contains additional information about the data requested from the API server, and thats what makes it efficient.
How to use GraphQl in your Django project
In order to integrate GraphQL API functionality to your Django app, you need to use a third party module which you can install by simply pip install graphene-django
. There are other clients as well which you can find here.
In this article, we are gonna build a full user-management GraphQL API so lets get started.
Installing Dependencies:
Assuming you already have your virtual environment and Django project setup, we can go ahead and install the dependencies:
To use GraphQL in Django we are gonna install following dependencies:
- A GraphQL django version which has some extended functionality inbuilt for Django
pip install graphene-django
- You can find its docs here
- A GUI interface through which we can interact with API
pip install django-graphiql
- You can find its docs here
After installing the dependencies, first thing we would do is install those apps in our Django project. This is as simple as we do with any other app in django, just add them in INSTALLED_APPS
list.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
'django_graphiql', # -> For GUI interface
'graphene_django', # -> For graphql api
]
Run the migrations after installing those apps: python manage.py migrate
and your django app is ready to be used GraphQL functionality.
Structure of GraphQL API in Django
There are a few things that might be new to you but you have to keep those in mind.
GraphQL uses queries and mutations for CRUD operations. Wait, what are they?
Query:
Just like a normal REST api query, its the request we send to fetch data from server, but this one is a smart query as it tells your server what and how much data it has to return for a request and where to look for it.
At the very basic level, a graphql query should look like this:
REQUEST:
-----------
query {
# Queries can have comments
user(id:"1"){ # Providing arguments
firstName
lastName
email
username
id
}
}
RESPONSE:
-----------
{
"data": {
"user": {
"firstName": "FooUser",
"lastName": "MyLastname",
"email": "someEmail@gmail.com",
"username": "admin",
"id": "1"
}
}
}
You see how we are fetching only the data we asked for instead of fetching all data.
Structure of Query
The query type contains a resolve method for each argument of data that can be requested. This resolve method describes the logic behind the fetching of data from the data base.
Remember the name resolve
is important and must be followed by the name of field you want to return the data for.
Example:
class QueryType(graphene.ObjectType):
name = "query" # Optional
description = "..." # Optional
pet = graphene.String()
def resolve_pet(root, args, info):
return "Dog"
schema = graphene.Schema(query = QueryType)
Mutations
GraphQL has similar analogy like REST that any query can be used to modify data(even GET), but conventionally this is not recommended.
It's useful to establish a convention that any query that modifies the data must be sent explicitly via a mutation.
Just like queries, mutation field returns an object type. This can be useful for fetching new state of an object after update.
Structure of Mutations
Each model type contains a mutate()
class method in which you implement the logic of modifying a django models object.
To make a mutation in model, we need to create a model type in which we will get the information or fields we need to modify, a field of object type, and a mutate()
method which will contain the logic of any operation we want to perform to modify the data.
class UpdateUser(graphene.Mutation):
...
@classmethod
def mutate(cls, root, info, update_data, id):
user = USER.objects.filter(id=id)
if user:
params = vars(update_data)
user.update(**{k: v for k, v in params.items() if params[k]})
return UpdateUser(user=user.first())
else:
print('error')
Schema: A schema will describe our data models and what data can be queried or requested. Each of these schema has a resolve
method in which you implement the logic of getting data from database.
A schema, you can say is an entry point in your API where every operations(CRUD) takes place.
Create a file name schema.py
in your app, this will be the entry point in your api. Lets say I made schema.py in my users app.
To keep it simple, Consider the following hello world example:
class QueryType(graphene.ObjectType):
name = "query" # Optional
description = "..." # Optional
name = graphene.String()
def resolve_hello(root, args, info):
return "SomeName"
schema = graphene.Schema(query = QueryType)
Now before you can use GUI interface to interact with your api, we need to define our schema and tell django_graphiql where is it. We do this by setting a variable in our project's settings.py
as follows:
In settings.py
GRAPHENE = {
'SCHEMA': 'users.schema.schema',
}
Here is the output:
Making Schema Using Django Model:
To use django models in our schema, we need to make a class for our model specifying fields and other necessary details.
Structure of schema:
import graphene
from graphene_django import DjangoObjectType
from .models import User
class UserType(graphene.ObjectType):
name = "query" # Optional
description = "..." # Optional
# Fields
first_name = graphene.String()
last_name = graphene.String()
email = graphene.String()
username = graphene.String()
id = graphene.String()
class Query(graphene.ObjectType):
name = "query" # Optional
description = "..." # Optional
person = graphene.Field(
UserType,
id=graphene.String()
)
@staticmethod
def resolve_person(root, info, **args):
id = args.get('id')
return userModel.objects.get(pk=id)
schema = graphene.Schema(query=Query)
CRUD Operations Using GraphQL in Django
Similar to REST, creating CURD in GraphQL is very easy.
The directory structure of my project is as follows:
├── gqlApi
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
└── users
├── admin.py
├── apps.py
├── __init__.py
├── models.py
├── schema.py # -> Schema file
├── tests.py
├── urls.py
└── views.py
In setting.py
GRAPHENE = {
'SCHEMA': 'users.schema.schema',
}
Create User
To create user, we need the details of the user that we want to create. This is passed using a POST request to mutations in your api.
To implement create operation, we need three classes to work with
- UserType (which contains the important fields of user table and model)
- CreateUser (which contains creation of user logic)
- Mutations
In our schema.py
file create a class to create new users with the fields required as follows:
from graphene_django import DjangoObjectType
import graphene
from django.contrib.auth import get_user_model
User = get_user_model()
class UserType(DjangoObjectType):
class Meta:
model = User
fields = '__all__'
class CreateUser(graphene.Mutation):
"""
This is the main class where user object is created.
This class must implement a mutate method.
"""
class Arguments:
username = graphene.String()
email = graphene.String()
first_name = graphene.String()
last_name = graphene.String()
password = graphene.String()
user = graphene.Field(UserType)
@classmethod
def mutate(cls, root, info, **user_data):
user = User(
first_name=user_data.get('first_name'),
username=user_data.get('username'),
email=user_data.get('email'),
)
user.set_password(user_data.password) # This will hash the password
user.save()
return CreateUser(user=user)
class Mutation(graphene.ObjectType):
"""
This class contains the fields of models that are supposed to be
mutated.
"""
create_user = CreateUser.Field()
schema = graphene.Schema(
mutation=Mutation # Adding mutations to our schema
)
In Action:
Update User
Similar to create user, we are gonna define our update logic in UpdateUser
class's mutate()
method.
The logic of update is simple in mutate()
method, iterate over thee given dict of parameters and include only those which are not None.
Note: .update()
method will only work if you get the user object by .filter()
which returns a query. Other than that, you need to explicitly pass keyworded arguments and then save using save()
method.
In our schema.py
file:
...
class UpdateUser(graphene.Mutation):
class Arguments:
id = graphene.ID()
username = graphene.String()
email = graphene.String()
first_name = graphene.String()
last_name = graphene.String()
user = graphene.Field(UserType)
@classmethod
def mutate(cls, root, info, id, **update_data):
user = User.objects.filter(id=id)
if user:
params = update_data
user.update(**{k: v for k, v in params.items() if params[k]})
return UpdateUser(user=user.first())
else:
print('User with given ID does not exist.')
class Mutation(graphene.ObjectType):
"""
This class contains the fields of models that are supposed to be
mutated.
"""
create_user = CreateUser.Field()
update_user = UpdateUser.Field()
In Action:
Read User Data
Reading data in GraphQL is not a part of mutations as it does not effect anny data so it will be simply a query. You can ask for specific details from your api server.
To implement read operation in graphql, simply create a field of User type. Add any extra key worded arguments you would like to receive in resolve method to get user from data base.
For example in code below, id
and username
will be passed as key worded arguments in resolve
method and based on them, you can fetch the user model from database.
Now the main work begins in resolve
method, here you got the information how to get data and what you need to return. You can simply use django api for that as shown below:
...
class QueryType(graphene.ObjectType):
"""
This is what read query looks like:
query {
user(id or username like-> username:"boopDog") {
firstName
lastName
... -> fetch fields
}
}
"""
user = graphene.Field(
UserType,
id=graphene.String(),
username=graphene.String()
)
@staticmethod
def resolve_user(*args, **kwargs):
return User.objects.filter(**kwargs).first()
schema = graphene.Schema(
query=QueryType,
mutation=Mutation
)
In Action:
Delete User:
Delete operation should be implemented using mutations
as it modifies the data in database.
There is nothing much to implement in delete operation, similar to read, pass the information about the user you want to delete and simply delete using .delete()
...
class DeleteUser(graphene.Mutation):
class Arguments:
id = graphene.ID()
user = graphene.Field(UserType)
@classmethod
def mutate(cls, root, info, id):
user = USER.objects.get(id=id)
user.delete()
return DeleteUser(user)
In Action:
Who Uses GraphQL
- GitHub
- Coursera
This concludes our discussion on GraphQL API and how to use it in Django app. Hope you find this article at OpenGenus useful.
Sign up for FREE 3 months of Amazon Music. YOU MUST NOT MISS.