HTTP APIs in Django with GraphQL

18 minutes
5 months, 3 weeks ago
<h2><b>Introduction</b></h2><p>This tutorial will introduce you to GraphQL with Django and Graphene. You will know how to create a Django project to build an API server based on GraphQL and not REST.&nbsp;</p><p><b style="color: inherit; font-family: inherit; font-size: 24px;">What is an API? Why GraphQL and not REST?</b></p><p>An <b>API</b> (Application Programming Interface) provides a way (or an interface) for clients to fetch data from servers. This establishes a relationship between a client and a server. <b>REST</b> stands for Representational State Transfer and it's an architectural pattern for designing client/server distributed systems. In the case of REST, the established interface is inflexible and strongly coupled to the server implementation so if the server's implementation changes the depending clients, most often than not, will break.</p><p><b>GraphQL</b> is a modern API standard for building Web APIs, invented and used internally by Facebook for its native mobile applications then later open sourced. GraphQL provides a better, powerful and flexible alternative to REST.&nbsp;<br></p><p>But why is GraphQL better than REST?</p><h3><b>1)&nbsp;Data fetching</b></h3><p>The most significant improvement that GraphQL introduced is data fetching. In a typical REST API, to fetch or retrieve data from a server, you might end up making requests to multiple endpoints. But using GraphQL, you only have one endpoint with which data is accessed on a server. Just make a single request and get the object along with the related fields.</p><p><b style="color: inherit; font-family: inherit; font-size: 24px;">2) Network Requests</b></p><p>In the GraphQL, you only make a single request to the server. This reduces network requests by allowing us fetch or retrieve all the data we need in a single query.</p><h3><b>3) Over/Under Fetching</b></h3><p>It is easy to fetch more than the data you need with REST, because each endpoint in a REST API has a fixed data structure which it is meant to return whenever it is hit. So, most times we just make do with the data we need and end up ignoring the rest. Also, REST makes it easy to under fetch data hence making you perform additional requests to other endpoints in order to fetch associated data.</p><p>With GraphQL that is not the case. Because GraphQL is a declarative data fetching specification and a query language, we only fetch what we need from the server by constructing our query to only include what we need.</p><h3>4) Error Handling</h3><p>Error handling in REST is pretty straightforward, we simply check the HTTP headers to get the status of a response. Depending on the HTTP status code ( 404, 503, 500 etc) we get, we can easily tell what the error is and how to go about resolving it. GraphQL on the other hand, when operated over HTTP, we will always get a 200 OK response status. When an error occurs while processing GraphQL queries, the complete error message which is more informative is sent to the client with the response.&nbsp;</p><h2><b>BUILD YOUR OWN GRAPHQL-DJANGO APPLICATION</b></h2><p><b>Assuming you have the basic knowledge of how Django works, let's first setup the project:</b></p><p><b>-&gt; A Django project called "<i>fashion</i>".</b></p><p><b>-&gt; An app called "<i>clothes</i>" inside it.</b></p><p><b>On terminal:</b></p><pre># Create the project directory<br>mkdir fashion<br>cd fashion<br># Create a virtualenv<br>virtualenv env<br>source env/bin/activate # On Linux/ Mac<br># Install Django and Graphene with Django support<br>pip install django<br>pip install graphene_django<br># Set up a new project with a single application<br>django-admin.py startproject fashion . <br>cd fashion<br>django-admin.py startapp clothes</pre><p>For first database synchronisation, run:</p><pre>python manage.py migrate</pre><p>Let's define new models:</p><pre># fashion/clothes/models.py<br>from django.db import models<br>class ClotheType(models.Model):<br> name = models.CharField(max_length=100)<br> def __str__(self):<br> return self.name<br>class Detail(models.Model):<br> name = models.CharField(max_length=100)<br> description = models.TextField()<br> type = models.ForeignKey(<br> ClotheType, related_name='type', on_delete=models.CASCADE)<br> def __str__(self):<br> return self.name</pre><p>Add clothes in INSTALLED_APPS:</p><pre>INSTALLED_APPS = [<br> ...<br> # Install the clothes app<br> 'fashion.clothes',<br>]</pre><p>Don’t forget to create &amp; run migrations after every change in models.py</p><pre>python manage.py makemigrations<br>python manage.py migrate</pre><p>Now to add some data to your model, use Django admin interface . You’ll need to run the development server and create a login for yourself (<b>./manage.py createsuperuser</b>).</p><p>Register models with admin panel:</p><pre># fashion/clothes/admin.py<br>from django.contrib import admin<br>from fashion.clothes.models import ClotheType, Detail<br>admin.site.register(ClotheType)<br>admin.site.register(Detail)</pre><p>Now you can login to your admin panel and create data for yourself by running:</p><pre>python manage.py runserver</pre><h2>GraphQL : Schema and Object Types</h2><p>Now that we are done with setting up Django project and adding data to models, we need queries to fetch that data.</p><p>What we need is:</p><p>- Schema with defined object types</p><p>- A view to take queries as input and return the result</p><p>The graph has a root type through which all access begins (Query class).We can achieve this by creating a type, subclassing DjangoObjectType. After that, we will list those types as fields in the Query class.</p><p>Create fashion/clothes/schema.py and type the following:</p><pre># fashion/clothes/schema.py<br>import graphene<br>from graphene_django.types import DjangoObjectType<br>from fashion.clothes.models import ClotheType, Detail<br>class ClothingType(DjangoObjectType):<br> class Meta:<br> model = ClotheType<br>class DetailType(DjangoObjectType):<br> class Meta:<br> model = Detail<br>class Query(object):<br> all_types = graphene.List(ClothingType)<br> all_details = graphene.List(DetailType)<br> def resolve_all_types(self, info, **kwargs):<br> return ClotheType.objects.all()<br> def resolve_all_details(self, info, **kwargs):<br> return Detail.objects.select_related('type').all()</pre><p>Now create the parent project-level <i><b>fashion/schema.py&nbsp;</b></i>:</p><pre>import graphene<br>import fashion.clothes.schema<br>class Query(fashion.clothes.schema.Query, graphene.ObjectType):<br> pass<br>schema = graphene.Schema(query=Query)</pre><p>Now after doing this, install your app and GraphiQL in your Django project. <i><b>GraphiQL</b></i> is a web-based integrated development environment to assist in the writing and executing of GraphQL queries. It will provide you with a simple and easy way of testing the 'fashion' project.</p><p>Add graphene_django to INSTALLED_APPS in fashion/settings.py:</p><pre>INSTALLED_APPS = [<br> ...<br> 'graphene_django',<br>]</pre><p>And then add the SCHEMA to the GRAPHENE config in fashion/settings.py:</p><pre>GRAPHENE = {<br> 'SCHEMA': 'fashion.schema.schema'<br>}</pre><p>Unlike a RESTful API, there is only a single URL from which GraphQL is accessed. Requests to this URL are handled by Graphene’s GraphQLView view.</p><pre>from django.conf.urls import url, include<br>from django.contrib import admin<br>from graphene_django.views import GraphQLView<br>urlpatterns = [<br> url(r'^admin/', admin.site.urls),<br> url(r'^graphql', csrf_exempt(GraphQLView.as_view(graphiql=True))),<br>]</pre><p>We’re now done to test the API we built using GraphQL. Go to your GraphQL server.</p><pre>$ python ./manage.py runserver<br>Performing system checks...<br>Django version 1.9, using settings 'fashion.settings'<br>Starting development server at http://127.0.0.1:8000/<br>Quit the server with CONTROL-C.</pre><p>Go to http://127.0.0.1:8000/graphql and type your query as follows.</p><pre>query {<br> allDetails {<br> id<br> name<br> }<br>}</pre><p>You will see a response like this assuming you loaded some data into the models.</p><pre>{<br> "data": {<br> "allDetails": [<br> {<br> "id": "1",<br> "name": "shirts"<br> },<br> {<br> "id": "2",<br> "name": "jeans"<br> },<br> {<br> "id": "3",<br> "name": "dresses"<br> },<br> {<br> "id": "4",<br> "name": "suits"<br> }<br> ]<br> }<br>}</pre><p>You can do the same with allTypes.</p><p>You can also get the relations as we have used foreign key in Detail Model. For example, we may want to list all types and in each type, all details that are in that type.</p><pre>query {<br> allTypes {<br> id<br> name<br> details {<br> id<br> name<br> }<br> }<br>}<br></pre><p>This will give you :</p><pre> "data": {<br> "allTypes": [<br> {<br> "id": "1",<br> "name": "mens_collection",<br> "details": [<br> {<br> "id": "1",<br> "name": "shirts"<br> },<br> {<br> "id": "2",<br> "name": "jeans"<br> }<br> ]<br> },<br> {<br> "id": "2",<br> "name": "women_wear",<br> "details": [<br> {<br> "id": "3",<br> "name": "dresses"<br> },<br> {<br> "id": "4",<br> "name": "suits"<br> }<br> ]<br> }<br> ]<br> }<br>}</pre><h3>Conclusion</h3><p>GraphQL is a very powerful technology for building Web APIs and using it you can easily add GraphiQL support to your django project.</p>

Comments

You must login to comment