Create a Modern Application with Django and Vue #2
- Create a Modern Application with Django and Vue #1
- Create a Modern Application with Django and Vue #2 ← You are here
- Create a Modern Application with Django and Vue #3
In part two, we are going to talk about how to connect the backend and the frontend. Currently, the industry standard is to use something called REST API, which stands for representational state transfer application programming interface. API refers to the connection between two software applications, and REST refers to a specific architecture that this type of connection follows.
A REST API request usually consists of an endpoint, which points to the server, an HTTP method, a header and a body. The header provides meta information such as caching, user authentication and AB testing, and the body contains data that the client wants to send to the server.
However, REST API has one small flaw, it is impossible to design APIs that only fetch the exact data that the client requires, so it is very common for the REST API to overfetch or underfetch. GraphQL was created to solve this problem. It uses schemas to make sure that with each request, it only fetches data that is required, we’ll see how this works later.
Before proceeding to the rest of this article, make sure you are familiar with Django and Vue.js. If not, please go through the following tutorials first:
➡️ Get the source code for FREE!
Setting up GraphQL with Django #
Let’s start by setting up GraphQL in the backend. You need to install a new package called graphene-django
. Run the following command:
|
|
Next, go to settings.py
and find the INSTALLED_APPS
variable. You must add graphene-django
inside so that Django is able to find this module.
|
|
Configuring graphene-django
#
There are still a few things you need to do before you can use GraphQL. First, you need to setup a URL pattern to serve the GraphQL APIs. Go to urls.py
and add the following code:
|
|
Next, create the schemas and tell Django where to find them in the settings.py
. GraphQL schemas define a pattern that allows Django to translate the database models into GraphQL and vice versa. Let’s take the Site
model as an example.
|
|
Create a schema.py
file inside the blog
directory.
|
|
As you can see, this file is divided into three parts. First, you must import the necessary packages and models.
Next, SiteType
class is declared, and this SiteType
is connected with the Site
model.
Lastly, there is a Query
class. This class is what allows you to retrieve information using the GraphQL API. To create or update information, you need to use a different class called Mutation
, which we’ll discuss in the next article.
Inside the Query
class, there is a resolve_site
function that returns the first record of the Site model. This method automatically binds with the site
variable due to its name. This part works exactly the same as the regular Django QuerySet.
Creating schemas #
Now you can do the same for all of the models. To make sure the schema file isn’t too big, I separated them into schema.py
, types.py
and queries.py
.
schema.py
|
|
types.py
|
|
queries.py
|
|
Finally, you need to tell Django where to find the schema file. Go to settings.py
and add the following code:
|
|
To verify that the schemas work, open your browser and go to http://127.0.0.1:8000/graphql . You should see the GraphiQL interface.
Notice how we are retrieving information in this example, it’s the GraphQL language, and it is how we are going to retrieve data in the frontend, which you’ll see later.
Setting up CORS #
Before you can move on to the frontend, there is still something you need to take care of. By default, data can only be transferred within the same application for security reasons, but in our case we need the data to flow between two applications. To tackle this problem, you must enable the CORS (cross origin resource sharing) functionality.
First, install the django-cors-headers
package. Inside the backend app, run the following command:
|
|
Add "corsheaders"
to the INSTALLED_APPS
variable.
|
|
Then add "corsheaders.middleware.CorsMiddleware"
to the MIDDLEWARE
variable:
|
|
And finally, add the following code to the settings.py
.
|
|
Setting up Apollo with Vue.js #
Now it’s time for us to move to the frontend. First, install the Apollo library. It allows you to use GraphQL in the Vue app. To do that, run the following command:
|
|
Under the src
directory, create a new file called apollo-config.js
and add the following code:
|
|
Then go to main.js
and import the apolloClient
:
|
|
Now we can use the GraphQL language we just saw to retrieve data from the backend. Let’s see an example. Go to App.vue
, and here we’ll retrieve the name of our website.
|
|
It is my personal preference to create a separate file for all the queries and then import it into the .vue
file.
src/queries.js
|
|
App.vue
|
|
The category page #
Now we have a left over problem from the previous article. When we invoke a router, how does the router know which page should be returned? For instance, when we click on a link Category One
, a list of posts that belong to category one should be returned, but how does the router know how to do that? Let’s see an example.
First, in the router/index.js
file where we defined all of our routes, we should set a segment of the URL pattern as a variable. In the following example, the word after /category/
will be assigned to the variable category
. This variable will be accessible in the CategoryView
component.
|
|
Next, in the AllCategories
view (the one that will show a list of all categories), we will pass some information to this category
variable.
|
|
In the Category
view, we can access this category
variable using this.$route
property.
|
|
And finally, the corresponding posts can be retrieved using the POSTS_BY_CATEGORY
query.
|
|
With this example, you should be able to create the tag and post page.
Creating and updating information with mutations #
From the previous section, we learned that we can use queries to retrieve information from the backend and send it to the frontend. However, in a modern web application, it is very common for you to send information from the frontend to the backend. To do that, we need to talk about a new concept called mutation.
Let’s go back to the backend and cd
into the blog
directory, and then create a file called mutations.py
. In this example, let’s investigate how you can pass data to the backend in order to create a new user.
|
|
On line 7, recall that the UserType
is tied with the User
model.
Line 9 to 12, to create a new user, you need to pass three arguments, username
, password
and email
.
Line 15 to 18, this should be very familiar to you, it is the same way you create a new item using the Django QuerySet.
Line 19, this line of code sets the password. For security reasons, you can not save the user’s original password in the database, and set_password()
method can make sure it is encrypted.
After that, you must make sure this mutation.py
file is included in the GraphQL schema. Go to schema.py
:
|
|
To make sure it works, open your browser and go to http://127.0.0.1:8000/graphql to access the GraphiQL interface.
|
|
I think you already know how to use this in the frontend. As an example, this is what I did.
|
|
src/mutations.js
|
|
User authentication with Django and Vue.js #
Now that you know how to send data to the backend, user authentication shouldn’t be too hard. You ask the user to input their username and password and send that information to the backend, and then in the backend, Django finds the user based on username, and it’ll try to match the password with the one stored in the database. If the match is successful, the user is logged in.
However, in practice, this plan has some problems. First, sending the user password back and forth isn’t exactly safe. You need some way to encrypt the data. The most commonly used method is JWT, which stands for JSON Web Token. It encrypts JSON information into a token. You can see an example here: https://jwt.io/ .
This token will be saved inside the browser’s local storage , and as long as there is a token present, the user will be considered logged in.
The second problem is caused by Vue’s component system. We know that each component is independent. If one component changes, it does not affect the others. However, in this case, we want all components to share the same state. If the user is logged in, we want all components to recognize the user’s state as logged in.
You need a centralized place to store this information (that the user is logged in), and all components should be able to read data from it. To do that, you’ll need to use Pinia , which is Vue’s new official store library created based on Vuex.
JWT in the Backend #
First, let’s integrate JWT with the Django backend. To do that, you need to install another package called django-graphql-jwt
.
|
|
Then go to settings.py
and add a middleware as well as authentication backend. The configuration will overwrite Django’s default setting, allowing it to use JWT instead.
|
|
To use this package, go to mutations.py
and add the following code:
|
|
We can test it in the GraphiQL interface.
Wrong Password
User Authenticated
As you can see, the input arguments are username and password, and if the user is authenticated, an encrypted token will be returned. Later, you can save this token in the browser’s local storage.
If you want, you can also customize the behaviour of ObtainJSONWebToken
. Go back to mutations.py
:
|
|
Notice that the ObtainJSONWebToken
extends to the default JSONWebTokenMutation
, and then in the Mutation
class, you can use ObtainJSONWebToken
instead.
Now you can make GraphQL return more information about the user.
Pinia in the Frontend #
Now it’s time for us to solve the second problem in the frontend. Let’s start by installing Pinia.
|
|
Then, go to main.js
and make sure that your app is using Pinia.
|
|
Go back to the src
directory and create a folder called stores
. This is where we’ll put all of our stores. For now, you only need a user
store, so create a user.js
file:
|
|
Notice that this store consists of mainly three sections, state
, getters
and actions
. If you already know how to create a Vue application, this should be fairly easy to understand.
state
is like the data()
method in a Vue component, it is where you declare variables, except these variables will be accessible to all components. In our example, Vue will first try to get the token from the local storage, if the token does not exist, the variable will be assigned the value null
.
getters
are the equivalent of the computed
variables. It performs simple actions, usually just returning the value of a state. Again, it is accessible to all components and pages.
And finally actions
are like the methods
in a Vue component. They usually perform some action using the states. In this case, you are saving/removing the user’s token and information.
One more thing you need to note is that you cannot save objects inside the local storage, only strings. That is why you have to use stringify()
and parse()
to turn the data into a string and then back into an object.
Next, you need to use this store when log the user in. I created a SignIn.vue
file like this:
|
|
Line 2, imported the user store you just created.
Line 9-12, call the user store in the setup
hook, this makes Pinia easier to work with without any additional map functions.
Line 32-33, invoke the setToken()
and setUser()
actions we just created, this will save the information inside the local storage.
Now, this is how you can log the user in, but what if the user is already signed in? Let’s take a look at an example:
|
|
Line 18-19, try to get the token and user info from the store.
Line 31-33, if the token exists, then the user is considered as authenticated.
Line 38-41, this method will log the user out when invoked.
If you think my articles are helpful, please consider making a donation to me. Your support is greatly appreciated.