Create a Fullstack app with Django, GraphQL and VueJS

On to Graphene!CREATING THE GRAPHENE (GRAPHQL) FRAMEWORKBefore we dive in the Graphene framework, I want to do a quick introduction on GraphQL.

GraphQL is an API query language developed at Facebook and was built to be an improvement on the REST architecture.

One of the biggest advantages of GraphQL over REST is the ability to make one request (or query) and return data from multiple schemas at once (similar to running a SQL join query) whereas one would have to make multiple requests in REST to achieve the same results.

REST vs GraphQLAnother advantage of GraphQL is that developers do not have to manage multiple endpoints.

With GraphQL, there is only one endpoint which is used to make and run all your queries.

An important note to remember is the terminology used in GraphQL.

With GraphQL, a “query” is the equivalent to a GET request while a “mutation” is equivalent to a POST/PUT request.

Graphene is a library built for developers to interact with GraphQL within python projects.

There are integrations built specifically for SQLAlchemy and Django (which we will be using for this project)13).

In order to use graphene with django, we need to install the graphene frameworkpipenv install graphene-djangoWe also need to install GraphiQL which is the GraphQL IDE located in the browser which we will need to create and run queriespipenv install django-graphiqlLet’s also install django filter, this will be explained in the futurepipenv install django-filterLastly, we need to update the settings.

py file in the project directory with our graphene installationINSTALLED_APPS = [ ##django apps ‘graphene_django’, ‘company’,]**Note that it is `graphene_django` in the settings file while it is `graphene-django` in the PipfileYou also need to add this snippet to the bottom of the settings.

py file.

This allows graphene to identify the schema file needed for queries and mutations.

GRAPHENE = { ‘SCHEMA’: ‘startupql.

schema.

schema’}**Make sure to change `startupql` to the name of your django project14).

Now to the fun part…creating our GraphQL schemas.

Create a schema.

py file within your app directory (i.

e.

company/schema.

py)import graphenefrom graphene_django import DjangoObjectTypefrom .

models import *from graphene_django.

filter import DjangoFilterConnectionFieldclass CityNode(DjangoObjectType): class Meta: model = City filter_fields = [‘city_name’] interfaces = (graphene.

relay.

Node,)class TitleNode(DjangoObjectType): class Meta: model = Title filter_fields = [‘title_name’] interfaces = (graphene.

relay.

Node,)class EmployeeNode(DjangoObjectType): class Meta: model = Employee filter_fields = [ ‘employee_name’, ‘employee_city__city_name’, ‘employee_title__title_name’ ] interfaces = (graphene.

relay.

Node,)class Query(object): city = graphene.

relay.

Node.

Field(CityNode) all_cities = DjangoFilterConnectionField(CityNode) title = graphene.

relay.

Node.

Field(TitleNode) all_titles = DjangoFilterConnectionField(TitleNode) employee = graphene.

relay.

Node.

Field(EmployeeNode) all_employees = DjangoFilterConnectionField(EmployeeNode)We created a new Object Type for all the models that we want to be able to query from the graphene framework.

At the bottom of the page, we also create a Query class which represents the root type through which access to all the nodes begin.

14).

We also need to create another schema file in the project level directory.

The schema file refers to the module setting that we specified above (‘startupql.

schema.

schema’).

Graphene needs this to be able to identify all the app level schemas within our django project.

import grapheneimport company.

schemaclass Query(company.

schema.

Query, graphene.

ObjectType): passschema = graphene.

Schema(query=Query)15).

Lastly, you need to add the url path for the GraphiQL IDE.

Navigate to the project level urls.

py file and add the following url pattern to itfrom django.

contrib import adminfrom django.

urls import pathfrom graphene_django.

views import GraphQLViewurlpatterns = [ path(‘admin/’, admin.

site.

urls), path(‘graphql/’, GraphQLView.

as_view(graphiql=True)), #add this]Let’s see if it worked.

Run the server again and navigate to http://127.

0.

0.

1:8000/graphql.

If everything worked right, you should see the screen below16).

Now we’re ready to run some quick queries!To return all employees that we manually entered earlierTo return all the city entriesSuccess!!17).

You will notice in the app level schema file, we have some filter fields for each graphene object.

Graphene uses django filter (which we installed earlier) to refine our queries and only return results which meet our criteriaTo return the City result for “Chicago”To return Employee results using filters from a foreign key, you will need to traverse to the related model using an underscore (_) between the field names.

E.

g.

employeeCity_CityName)MOVING TO ADVANCED REQUESTSNow that we’ve gotten our hands dirty with graphql and django, let’s switch gears to more advanced queries.

In this section, we will create new entries, update existing entries and delete entries.

Creating EntriesRemember that we currently only have 3 Title and 2 Employee entries in our system.

Let’s add some more entries to fill out the company database.

18).

Let’s start by adding some titles to the company organization.

We will navigate to your app level schema file and create a Mutation schema.

Remember I mentioned earlier that the create function in graphQL is called a mutation (equivalent of a POST request in REST)class CreateTitle(graphene.

relay.

ClientIDMutation): title = graphene.

Field(TitleNode) class Input: title_name = graphene.

String() def mutate_and_get_payload(root, info, **input): title = Title( title_name=input.

get(‘title_name’) ) title.

save() return CreateTitle(title=title)In the snippet above, we are initiating a title variable to represent the Title node which in turn virtually represents the Title model.

Within that variable, we create an Input class to describe all the node and model columns that we want to be able to create new values for.

We then create the ‘mutate_and_get_payload’ function where we get the ‘title_name’ column the model and save the entry to the model.

This is a simple example but we see a slightly more advanced example soon when we create a mutation class for the Employee model.

Within the same app level schema file, we also want to create a Mutation root class right below the Query class.

class Query(object): city = graphene.

relay.

Node.

Field(CityNode) all_cities = DjangoFilterConnectionField(CityNode) title = graphene.

relay.

Node.

Field(TitleNode) all_titles = DjangoFilterConnectionField(TitleNode) employee = graphene.

relay.

Node.

Field(EmployeeNode) all_employees = DjangoFilterConnectionField(EmployeeNode)class Mutation(graphene.

AbstractType): create_title = CreateTitle.

Field()Lastly, we want to go to the project level schema file and create another Mutation class which will combine all of our all level mutations and also add the mutation to the schema variableimport grapheneimport company.

schemaclass Query(company.

schema.

Query, graphene.

ObjectType): passclass Mutation(company.

schema.

Mutation, graphene.

ObjectType): passschema = graphene.

Schema(query=Query, mutation=Mutation)Now let’s run our server again and navigate to the graphql IDE and create a new “CIO” title entryLet’s confirm that it’s been created by running a query to return all titles19).

Let’s repeat the process for the Employee modelclass CreateEmployee(graphene.

relay.

ClientIDMutation): book = graphene.

Field(EmployeeNode) class Input: employee_name = graphene.

String() employee_city = graphene.

String() employee_title = graphene.

String() def mutate_and_get_payload(root, info, **input): employee = Employee( employee_name=input.

get(‘employee_name’), employee_city=City.

objects.

get( city_name=input.

get(‘employee_city’)), employee_title=Title.

objects.

get( title_name=input.

get(‘employee_title’)) ) employee.

save() return CreateEmployee(employee=employee)In this snippet, you will notice that in the Input class, we had to define all of the columns for the Employee model.

Additionally, within the ‘mutate_and_get_payload’ function, you will notice that we have to create a relative reference to the City and Title models given the foreign key relationship.

Remember to add the class to the Mutation root at the bottom of the app level schema fileclass Mutation(graphene.

AbstractType): create_title = CreateTitle.

Field() create_employee = CreateEmployee.

Field()There’s no need to update the project level schema file since the mutation only needs to be defined once.

Now we’ll create a new employee entry using the newly created title.

Success!!Updating EntriesLet’s update one of our employee entries.

In order to update our entries (i.

e.

PUT request), we will need to create the update class within the app level schema file.

One of the most important aspects of updating an entry is the use of the global ID.

You may have noticed from our original queries that graphene returns a different ID value (e.

g.

Q2l0eU5vZGU6NA==) than the primary key that we usually expect from the django backend.

This is a node ID assigned by the graphene server to use to identify this particular node.

In order to update any of our entries, we first need to get the unique node ID (plus any other model value) and then use it to update the node values.

The node ID is very important because it guarantees uniqueness when updating or deleting an entry.

Let’s start by creating our update class20).

First thing we need to do is import the global id module from graphene relay which we will use to “retrieve the node id”Add the snippet below to the top of the app level schema filefrom graphql_relay.

node.

node import from_global_id #for updatingThen add the update classclass UpdateEmployee(graphene.

relay.

ClientIDMutation): employee = graphene.

Field(EmployeeNode) class Input: id = graphene.

String() employee_name = graphene.

String() employee_city = graphene.

String() employee_title = graphene.

String() def mutate_and_get_payload(root, info, **input): employee = Employee.

objects.

get( pk=from_global_id(input.

get(‘id’))[1]) employee.

employee_name = input.

get(‘employee_name’) employee.

employee_city = City.

objects.

get( city_name=input.

get(‘employee_city’)) employee.

employee_title = Title.

objects.

get( title_name=input.

get(‘employee_title’)) employee.

save() return UpdateEmployee(employee=employee)Add the update class to the mutation classclass Mutation(graphene.

AbstractType): create_title = CreateTitle.

Field() create_employee = CreateEmployee.

Field() update_employee = UpdateEmployee.

Field()Let’s go back to the graphiql IDE and find the employee entry that we just created.

Let’s assume that John Phillips decided to change his first name to JasonYou will notice that even though we are updating only the employee name, we had to also provide all field values for the Employee model.

If we want to avoid that and update only the column that we want, then we will have to update the “mutate_and_get_upload” function and delete (or comment out) the model fields that we do not want to be overriddenclass UpdateEmployee(graphene.

relay.

ClientIDMutation): employee = graphene.

Field(EmployeeNode) class Input: id = graphene.

String() employee_name = graphene.

String() employee_city = graphene.

String() employee_title = graphene.

String() def mutate_and_get_payload(root, info, **input): employee = Employee.

objects.

get( pk=from_global_id(input.

get(‘id’))[1]) employee.

employee_name = input.

get(‘employee_name’) # employee.

employee_city = City.

objects.

get( # city_name=input.

get(‘employee_city’)) # employee.

employee_title = Title.

objects.

get( # title_name=input.

get(‘employee_title’)) employee.

save() return UpdateEmployee(employee=employee)Now we can go back to the IDE and update only the namemutation { updateEmployee(input: {id: “RW1wbG95ZWVOb2RlOjM=”, employeeName: “Jason Phillips”}) { employee { employeeName } }}Deleting EntriesNow we will focus on deleting previously created entries.

Very similar to updating entries, the node ID is very important when deleting an entry because of its uniqueness.

21).

Let’s start by creating a delete classclass DeleteEmployee(graphene.

relay.

ClientIDMutation): employee = graphene.

Field(EmployeeNode) class Input: id = graphene.

String() def mutate_and_get_payload(root, info, **input): employee = Employee.

objects.

get( pk=from_global_id(input.

get(‘id’))[1]) employee.

delete() return DeleteEmployee(employee=employee)Update the mutation class tooclass Mutation(graphene.

AbstractType): create_title = CreateTitle.

Field() create_employee = CreateEmployee.

Field() update_employee = UpdateEmployee.

Field() delete_employee = DeleteEmployee.

Field()Let’s go back to the graphiql IDE.

In this scenario, let’s assume that our newly updated Jason Phillips decided the company is not a good fit for him and decided to quit.

Now we need to delete him from the systemACCESSING THE GRAPHENE SERVER FROM OUTSIDE GRAPHIQLSo far, you will notice that we have been running all our graphql queries from the graphiql IDE which is tightly coupled with django.

There might be some situations where this may not be the optimal solution i.

e.

exposing your API to your customers.

In this example, I attempt to use insomia to make my requests but you can use any REST client like postman.

22).

In this example, I’ll attempt to run a query to return all the titles using the localhost (http://127.

0.

0.

1:8000/graphql/) as the endpointI got a 403 Forbidden error as well as a django error mentioning that we got a CSRF verification failureThere is a very easy solution for this.

23).

Navigate to our project level urls.

py file.

Add this snippet to the top of the filefrom django.

views.

decorators.

csrf import csrf_exemptNow wrap the GraphQL view within the url patterns with “csrf_exempt”urlpatterns = [ path(‘admin/’, admin.

site.

urls), path(‘graphql/’, csrf_exempt(GraphQLView.

as_view(graphiql=True))), ]Now go back to the REST client and run the query again.

It should work now!Thank you very much for taking the time to read through this.

If you want to see the repo for this project, you can click hereStay tuned for Part 2 which I’ll work on building a simple VueJS frontend application which will interact with the graphene framework and allow you to view and edit employees.

Till then!.

. More details

Leave a Reply