 Today I want to show something that I have discovered maybe three or four weeks ago, which is basically GraphQL. I knew that it exists for many months already. I tried it many months ago and I failed because I was too stupid to use Relay and then I ranted on Twitter about it, why is it so difficult and then everybody said, you know, why don't you just try Apollo with GraphQL and then a few weeks later, you know, I had some time and I tried again and suddenly it all came together and worked pretty nicely. And after learning about it two weeks ago, we are already having some components on our production website running in production. So you know, I work for this online art gallery called the Artling and this luxury marketplace called Laksblot and basically just two weeks ago, I learned about GraphQL and I'm like, okay, let's build, make an offer model here and build this thing in React.js and use GraphQL for the endpoint and Apollo on the front end. And this worked so well that we basically decided to migrate our entire API for Laksblot away from Django REST framework, so it's a classic REST API and towards GraphQL, let's see if I can. So this is basically our current API and this is only half of it. I was pretty bad at making sure that all my endpoints are actually documented. So you would have to click into it. Sometimes these even crash if you're not logged in or they load very long because they are loading 10,000 products and it crashes the browser and then, you know, this is how you could see what kind of fields you can post and how the result looks like. So my current workflow is essentially, I want to write some front end components and I'm asking myself, hmm, which endpoint do we have that gives me that data, right? And then I go on this to this website here and I try to find that endpoint. Sometimes it's not on here, so I have to open up in Django my URL.py file and try to find the route in my URL file and then go to the API view and then see what the view actually does. So it's a really painful process, which is partly my own fault because I'm not documenting my API correctly. But it also kind of shows that even though Django REST framework is a fantastic framework, it's not easy enough, right? You as a developer, you can still fail and GraphQL kind of, I think, solves that problem. So I will show two parts today and I will show a lot of code. So if you want to try this at home, you can essentially go to this repository on my GitHub account and simply follow the same, like basically these are my slides here. You just follow this thing and you copy and paste all the code and hopefully it works. I mean, I will show that it works right now. So anyone should be encouraged to try this as well. Or you know, you can just clone the repo. The repo already has the final state. So when you clone this and you run the install for Python and Yarn install for the front end part, you should have the final version up and running. And you can just start playing around with it and manipulate it, okay? All right, so for those who have not used GraphQL before, I get a very quick intro. So it's a query language for your API. So what does that mean? It means that we are writing queries like this. We are not calling API endpoints anymore. There's only one API endpoint and that is your GraphQL API endpoint. Like with normal REST APIs, we have dozens, it's not hundreds of API endpoints, right? With GraphQL, there's only one. And we send a post request to that endpoint. And the data of the request is simple text queries like this, right? And the data that we get back looks exactly like the query that we have sent. So I mean, if you don't need to know anything about relational databases, about SQL or this kind of stuff, it's pretty easy to understand for pretty much anyone how to access the data, right? On the back end, you might have different data sources. You might have a MongoDB, you might have a PostgresDB. You might pull some data from the Twitter and Facebook API. And you all wrap it in your GraphQL schema. So your user, the developers that built the front end, they don't care where the data comes from. They just say, like, give me the user profile and the name, the first name, last name, email, and avatar picture. That's all I care about. Where it comes from, I don't care, right? Another good thing is that if you use something like Relay or Apollo, it groups all your queries. You might have dozens of components and they all have their own queries. It groups them all together into one request and sends only one request to the server. The server does all the database fetching and sends one response back to the client. So you save a lot of bandwidth, okay? So the main task when working with GraphQL is you have to describe how your database schema looks like, okay? So you will have, so this is the GraphQL schema. And by defining this schema, by saying in my application, there is something called character. There's something called planet and so on. When you define this schema, there's this editor here, Graphical, that has auto completion. So because the schema is pretty fine, the editor can patch the entire schema and know about it. And while you're typing, it auto completes. It knows which endpoints exist. It knows what is the return value of each endpoint. It knows what fields are available for the user profile object, for example. Maybe they show this here. You can even click here at docs. It shows all the endpoints that exist. If you click into an endpoint, it shows what is the return value and what fields are available on the return value. So API documentation, 100% on, always up to date. You cannot fail as a developer for getting to update your own documentation. And then you stop using it. And then you have to go back and read your ugly code, right? Okay, so that's basically the idea of GraphQL. All right, so let's start totally from scratch, right? You want to build a GraphQL server on the back end using Django. And you want ReactJF application on the front end using ReactJF and Apollo. So that's our goal for today. And I already prepared this because I don't want to download things. I don't know how fast the internet is here. So if you started a new Django project, you would create some folder on your machine. You would start a new virtual environment. Then you would run tipp install Django, high test Django, high test coverage and mixer. So I like to do stuff in a test-driven manner. Lots of people who already saw the other two talks that I gave about this asked me how do you test it. So I'm trying to show this today as well. And when you have Django installed, you can run on your terminal Django admin start project. And you give it a name. And I like to name it back end in this case. So when you run Django admin start project back end, it would then create this back end folder here and put a few piles inside. So it looks like this. And when you created a new Django project, you first want to migrate the database so that all the tables that are built into Django will be created. And then you want to create a super user so that you can lock into your own admin interface. You call it admin. And then you can run the development server. And if that worked, you should be able to go to localhost 8000 and you see this before Django page that every empty Django project shows. Okay? All right. So since we do stuff in a test-driven manner, I have a few patterns that I always use in all my projects. So we will need a test settings file. Basically, we are overriding some things from the Django settings. And we are saying we don't use whatever database your Django installation is using. We want to use an in-memory SQLite database because it's much faster. Then the test will be like 10 times faster than versus using your Postgres, your real Postgres database, right? Same here for the password hashes. In your tests, you will usually create lots and lots of users. And usually in the beginning of each test, you have to create a user so that you can attach it to your request and then call your views. And with the SHA password hashing, which is the one that you should use for your real installation, it's a bit slower than just using the md5 password hasher. And same here. If you have any models that have files, data like images or files, file fields, you can use in-memory storage so that these don't really get written to the disk. So you say IO operations, tests get faster, right? So this is a simple test setup that's useful for pretty much any Django project. So I like to use the built-in Django test runner. I like to use PyTest. And for this, we will need PyTest.ini files. We basically just tell PyTest where our test settings can be found, the file that I just created. And then I like to have 100% test coverage on all my projects. So we also create a coverage RC file. Actually, this file only is a list of files and folders that I don't care about, where I don't care about the coverage. You know, the managed py file, the wsgi.py file, they are pre-generated by Django. I don't put any code in those files, so I don't want them to reduce my coverage because I'm not going to test these files. The coverage in those files will always be 0%, and it reduces my overall coverage. And I want to have 100% coverage, so I add these exceptions here in the coverage RC file. This point, okay, so that's like, you just created a new Django project, you set up your testing framework, now you are ready to, you know, get some new work done. So the first thing that I use in Django, those people with this folder here, back-end, this is my entire Django. It's also called back-end. It's kind of my project folder. This folder usually only has project-wide settings. We have the test settings in there, and we have the main url file in there, so there's this file here, url file, which shows all the urls that are available in there. And all the real logic that deals with products, one application that deals with user-admin, start app, so let's say for today we built a little Twitter-like application where people can type messages on the home view you have the long list of messages, right? And you can click into the app. So we will need a table called message. So in Django, you basically, these are the fields, the columns of our database tables, okay? So each message belongs to a user, each message can have some text and a creation data, right? So pretty simple database with three columns. Database table with three columns. Usually I wouldn't do it this way. I would first write the test, but I think it's easy to understand what we are trying to test for. The test could look so forth, each file, for each file, like I have a model.py file here, I will also have a corresponding test underscore and then the file name. So later we will have a schema file and we will have test underscore schema. In most of my projects, I will have a utils file and I have test underscore utils. Every file that I make, I have a test file, right? And for every class and every function in all of my files, I'll write a test. So we have a message class here, so we will write a message test. So this stuff is basically boilerplate stuff that we need so that we can write into our database because PyTest by default does not allow you to write into the database. But when you import PyTest and then you make it, you call this PyTest.mark.jangoDB, then you can write into your database. Mixer is a very, very useful tool in the Django ecosystem that allows you to create test fixtures because usually when you write tests, you need to make sure that your database has certain objects. There needs to be something in your database and then you can run your test and see. For example, when you want to test if a view deletes an object, you need to create the object first and then you can test if the deletion works and then after what you check if the object is still in database or not. So I always use Mixer to create objects in my database and Mixer basically has a very simple API. You call Mixer.blend and then the name of the app and the name of the model that you want to create and Mixer will basically fill random values into all fields and this is the cool thing about Mixer. So it even understands if it's an email field it will put random emails into that field and if it's a name field like first name, last name it will actually put out of a dictionary of names random names into that field and a good thing is sometimes it even puts unicode characters like Chinese words into text fields which you never test and often you have unicode decode errors in your application and you will only realize after running your test suite for a few times you will realize that there are some bugs in your code because Mixer was putting unicode characters into your text field. So it's a really cool project. So I just try to instantiate a message object and I check if it has a primary key greater than zero that means it's in my database. If the object is not safe for the database the primary key would be none. So this could be a possible simple test called to just test a Django model. I mean there's nothing much to test anyway the model doesn't have any functions it's not doing much I just write this test essentially to get 100% code coverage. So we have our model and we also want to be able to use the Django admin to fill in some messages into our database so I register this model with the Django admin and now at this point I can create an initial migration so basically I have created a new database table right Django has this feature here make migrations that will create the insert table statements so when I deploy this application I also update my database automatically so at this point server is still running I should be able to lock into my admin and I can see my messages table and I can fill in new messages or we have two objects in our database So far this is like the super quick introduction into Django for those who have never used Django and those who have used Django before now comes the exciting part let's try to use Graphene Django so Graphene is a pretty cool Python app that allows you to write a GraphQL schema in Python by writing Python classes and Graphene Django uses Graphene to have some even more integrated stuff so that you can use it super easy with Django and it basically understands your models and creates the Graphene schema very similar for those who have used Django REST framework very similar to writing serializers for each model you usually write a serializer so that all the fields on your model can be returned to your product as a json so you would have to do pip install Graphene Django and every time we want to use a new app in our Django project we need to add that to our installs app setting and Graphene needs one more setting should have closed that file it needs to know where is your main schema file that's pretty similar to the urls.py file there is one main urls file that imports all the other ones from your applications and the same thing is true for Graphene we will have one main schema file which is in backend slash backend let me just create a file already another file looks a little bit like this so we say this is our main schema we have to read it from bottom to top this is our main schema variable this is the variable that this setting is referring to in backend dot schema so backend is the folder schema is the file and schema is the variable and the schema has this queries class right now we don't really have any real endpoint so you have to put something you cannot do this you cannot just have tasks and do nothing in this class so I put a dummy endpoint there essentially we have now one api endpoint that's called dummy and that always returns an empty string it doesn't do much so later we will not need this anymore for now I need this so that I can go on to the next file oh yeah we will have to replace we will have to add two more endpoints to our URLs so graphical is the editor that we will use during development probably in the real world if you deploy this you would only hook up this endpoint when debug is true for example you might not want that anyone can go to yourdomain.com slash graphical and then start exploring your api and gql is actually the same view but this is going to be used by our front end using a column yeah so this comes with jango graphene these two endpoints so at this point everything worked server is still running I should be able to go to this graphical endpoint yep and thanks to the code completion it already tells me that there is an endpoint called dummy so let's say I want a query dummy and it always returns null because we haven't really implemented we said the type of dummy is a string but we haven't really written a resolver function so it's not really returning any string it's currently just returning nothing so don't worry about this dummy endpoint we will have more meaningful endpoint later okay so I said that writing these schemas is very similar to writing serializers and jango rest framework and so basically what we have to do is in our simple app we will also have a schema file so and we have to support each model that we have so we have a message model we will create a message type so for each model we have to create a jango object type class and the syntax is always the same like we have class bit of the derived from jango object type then class meta and then you say which model is it and it's always interface graphene.node and then my app so this is in my simple app folder my app has its own sub query and here we say we want to have an endpoint that's called all messages and that returns a list of message type objects and then usually when you write these endpoints they always come in two pairs one is the name and the type and then there's a resolver function that has the same name like all messages but resolve underscore prepended to the name of the endpoint and essentially arcs is post parameter if your client wants to post some data to the endpoint this will be hidden here in arcs so in normal jango views this will be a request not post the data inside the post data context is in this case in graphene is the jango request I could actually call this request and then everybody who knows jango knows how to use this you have request.user is authenticated and all these things request.session and so on because the graphene docs don't assume that you are using jango and in their examples it's called context so I just kept using the same okay so now we created a new endpoint but this endpoint will at this point not be visible yet because in our main schema file we need to import it so we do import simple app so we use multi inheritance here simple app query now we don't need the dummy anymore because now we have an actual endpoint you know so we have our sub query here which is abstract type so this query alone cannot be used but when we combine it with graphene object type using multi class inheritance now we have a query that can be used for our graphene schema alright so basically in this file every time we add a new app to our project we will just keep importing the apps here and then we will keep adding those query classes to our main query class okay so at this point we should be able to see the endpoint all messages and it also knows that it returns lots of messages and messages have I feel called ID and they have the actual text called message I can run this query and I'm getting the two entries that are already in the database okay and by the way the docs are always up to date it tells me what queries are available or messages, it tells me the return type message and it tells me these three fields are available on my return type okay so the facility we tried the query okay let's create one more endpoint one endpoint that returns a single message and not all messages at the same time now I'm saying the message endpoint really all messages they don't have the dango primary key which is just one key they have this weird stuff here this is like some hash value if you unhash it you get a tuple with message type the name of our type and the primary key so it's like somehow encrypted but the information is still there and when you're back into a dango primary key or message type and primary key into one of these I know that the ID that I'm getting here will look like this so first I will use the from-world ID function to turn that I know how do we like list use now we want to know from bottom to top again similar to how we have lots of queries we will have lots of mutations so this mutation is going to be called createMessage and it's of type createMessageMutation so here's different we won't have to resolve our functions because mutations are a bit more complex than simple functions we will have mutation classes so how does the mutation class look like it has to derive from graphene mutation and it has to have this subclass input so obviously we want to create a message so the user has to type in the message so we need to get message as part of the post data and mutation can also have outputs so and this is tricky to wrap your mind around when you are used to REST APIs you know like if I call this endpoint and I do a post request this means the endpoint is supposed to write data to my database if my form data is wrong the endpoint will return 400 status codes and say this was a bad request there are some bad form values with GraphQL you don't have that anymore every request as a post request every response is a 200 response and either GraphQL was able to give you the data that you asked for or it wasn't able to give you the data that you asked for so it's up to you how in your front end how you figure out if there were form errors if the data that was requested if you are even allowed to see it so I came up with this for now when I have mutations I will always return a status which is like the usual HTTP status codes I will have form errors which is supposed to be adjacent string of my fields and the corresponding errors in the same way like the Django REST framework if you call your serializer and the serializer is not valid you can return the form errors of your serializer and on your front end you will have a dictionary with the field and a list of errors for that field so I will just make sure that I design my form errors in the same way and if the mutation was successful I'm going to return the newly created message so this is a graphene field of type message in this mutation you have the mutate function and here you can do things like checking if the user is even allowed to do it I told you ARCs is the post data Contacts is the HTTP request so we can check if Contacts.User is authenticated if the user is not authenticated and this is the funny thing we don't return HTTP values like in Django or in Django REST framework we return an instance of this mutation class where the corresponding return fields are set to some values so in this case when the user is not authenticated we will return the created message mutation and we will set the status to 403 which means forbidden so that means there will be no form errors, there will be no message returned and we won't have created anything in our database, we just return 403 on the front end it's up to you how to deal with that you will probably redirect to the login view or if the user is already logged in you will say that you are not allowed to do this action then we will probably do some kind of form validation we will check if the message is empty and if the message is empty we return an instance of the mutation and we will set status to 400 which means bad request and we will return a JSON dictionary where we say the message field had an error and the error is please enter a message if everything is fine we will use Django to create our message and then we return once again like always we return an instance of this mutation and the status will be 200, there will be no form errors and the message will be set to the newly created object so basically if you are used to just writing normal Django views this is your Django view this is what you used to do in your Django views in the post part or the resolver function is what you used to do in the dispatch function or in the get function of your Django view or if you are used to a Django REST framework this is what you used to do in your API views and your view sets and so on so this is where all your logic is and it's just normal Django code like you always use to write Django code nothing special alright so now we have this mutation let's have a look at test because it might not be super obvious how to test this oh yeah I need to create a new file speed up so just quickly people ask me how do I test this and it couldn't be any easier oh wait I forgot some imports so our mutation does three things are you logged in you provide a message and then it creates the message these are the three situations that can happen so you would want to write test for that so the first test would be you have correct data the message is there you create a request using Django's request factory you attach an anonymous user you can import anonymous user from from Django to that request and then you simply create an instance I mean it's just a class the message mutation is just a class you can create an instance of that class whatever you want so we have an instance here and the mutate function is just a function so you can just call that function on your instance and we know that the first thing is supposed to be a root I don't even know what root is so in my test I simply pass in none and it still works then the second parameter is supposed to be the post data the data that we've constructed here the third parameter is the context so we put in our request here and then we just check if the result that we get the result that we get is a dictionary which has these three fields and if the user is not logged in the field status is set to 403 so you can simply check for that in your test to check if result status is 403 so the next test will be now we don't have anonymous user we have a real user attached to our request but we don't provide any data anymore now the status should be 400 and we should see if message is in the form error suggestion string and finally we will have the same logged in user and we will call the mutate function passing in an actual message and now the status code should be 200 if everything is correct and we check if we got a message as a return value a message instance with a primary key so it means it has been saved to the database so this is how you could write a test for a mutate simply instantiate the class and call the function and the same is true for queries you can instantiate this query and you can call the result function and simply test what you want to test so testing couldn't be any easier the mutation in place now we need to hook it up in our main schema file just like we hooked up our queries at this point thank god it works now the editor knows that there is a mutation and it's called create message and you have to provide a message okay let's try an empty message and we get status 400 form errors, please enter a message if we provide an actual message we know that, we expect that we will get back the message object and the editor can go deeper and deeper it knows that message is of type message type which has ID and message so I might just want to query that from my return value and I'm getting status 200 no form errors and a new ID and a new message to our database when you look back at Django admin there should be three messages now okay so final thing and then I think we can have a short break authentication everybody asked me, cool, that's so cool but how do I do authentication so there is something coming up Django GraphOps I haven't had the time to really look into the code of this repository and it seems to be a little bit stale I haven't done any work recently and they say it's based on Django REST auth so I'm like, why don't I just use Django REST auth then so I kind of decided against this there's also Python JWT which is like the Python implementation for doing JSON web token you could probably write your own mutation that accepts username and password and then looks it up in your database if it's correct, then generates a token like that and returns the token but you know, I'm not really a cryptography expert and so on I would probably write something that can be exploited so I didn't have the balls to do that there is Django REST framework JWT which I know has a lot of watchers and stars recent development five days ago Django REST framework has a huge community most of the people are probably using Django REST framework JWT as well in their project so I felt more secure using this this is not using GraphQL but I thought, okay then I have an API that has the GraphQL endpoint and three more endpoints for token management who cares four endpoints in total it's still super easy to understand API versus my current API with 300 endpoints so I thought it's a good trade off using something that's very well tested but isn't using GraphQL but it works so you would run pit install in REST framework Django REST framework JWT and then add these to and then Django REST framework needs a bunch of settings and we will just add for local development we need to make sure that because our backend and off-run it has different IPs so it's like different origins and usually the Google Chrome browser would prevent you from sending requests between the two and then we need to add the course headers middleware to our middleware classes but and thanks to Django REST framework JWT we have a few new URLs that we can hook up in our main URLs file which is essentially these three and these are classic REST endpoints that most people will have in their classic REST APIs so these three APIs will help us to authenticate and get a token okay, when we did this we should be able to use Curl our user is called adminpassword test1234 the endpoint is called ap-token-off so hopefully if this worked we should be able to send a post request with our username and password and we will get back a json web token that is now valid for five minutes alright so this is the first part should we have a short break there's still a lot of food there we get some drinks and we do a 5-10 minute break you can ask me some questions over there and then we just return here and get to the more difficult part okay, so thanks so far