Building Models in Flask and GraphQL
Tags: tutorial, webdev, graphql, python
Published: 2022-03-09 00:13:02

Welcome to Part 3 of this mulit-part series. In this part of the series, we’re going to be looking at how to write our Models for our database, as well as our schemas and resolver functions for GraphQL. 

In the first two parts of the series, we discussed how to set up our flask backend api, how to connect it to a postgres database, and how to structure our files for an evolving application using an application factory. 

> As a reminder, we will need to have the following packages installed for this portion of the tutorial:
        `pip install flask flask_sqlalchemy flask-graphql graphene`

I’ve covered flask and flask_sqlalchemy in part one. We will be using flask-graphql and graphene in this part of the tutorial. I was using `Flask-GraphQL==2.0.1` and `graphene==2.1.9` at the time this article was written. 

Let’s jump right in!

### OUR FIRST MODEL

_Findr / backend / models.py:_

In our models.py file, let’s start with the following lines of code:
```python
from backend import db
 
 
class User(db.Model):
   __tablename__ = 'users'
   id = db.Column(db.Integer, primary_key=True)
   email = db.Column(
       db.String(64), unique=True, index=True)
 
   def __repr__(self):
       return f"<User {self.email} >"
```
Let’s break apart what’s happening here. 

First we have to import our _SQLAlchemy_ that we initiated as _db_ in our application factory (See previous article). Then we create a User Class that extends SQLAlchemy’s Model class. We created a name for our table using the double underscore method for tablename. We created a unique ID for each user that we created by adding `primary_key` to that Column. We created an email address column as well, and said that it needs to be unique, as two users can’t have the same email address. Then finally, we used the double underscore _repr_ method so that when we try to print our user in the terminal, it’ll display something legible. Also, as you may have noticed, we are using the _db.Column, db.Integer, and db.String_ methods from SQLAlchemy. There are a few others that we will use throughout this, but also feel free to check the [documentation](https://flask-sqlalchemy.palletsprojects.com/en/2.x/) . It’s always a good idea to look through the documentation of a technology you are learning! 

So, now we have built our first model! Our database needs to be updated and let it know that our new model exists so that we can start adding users to our database. Open up your terminal and make sure you are in the correct directory _~/Documents/Development/Findr_ . Also, we want to make sure that our _virtual environment is active!_ We can access it by typing `source flaskenv/bin/activate` in our terminal. One we are ready, we can start making our _migrations_ for our database models. 

### MAKING DATABASE MIGRATIONS

We are going to need another library to help us out here. Let’s install _Flask-Migrate._
`python3 -m pip install flask-migrate`

Also, in our _app.py_ file in the root directory, we want to make sure we add the following lines of code if we don’t have them already:
```python
from flask_migrate import Migrate

# …
migrate = Migrate(app,db)
```
Now let’s start making our migrations. In our terminal, we will initialize our database for migrations:
`flask db init`

This command creates a _migrations_ directory, where all our migration scripts will be kept. In order to make our first migrations script we will run the following command:
`flask db migrate -m “inital migration” `

If we take a look at our files, we should see a _migrations_ directory was added and inside of that directory is a subdirectory called _versions_. This is where each of the scripts are stored as we make migrations. These are important because they allow us to **upgrade** or **downgrade** our migrations – meaning we can move forward with a migration or we can backtrack if something went wrong or we don’t want to make that change.

Let’s open up the migrations directory and look inside the versions subdirectory. There should be just a single file for now, with a name in a hexadecimal code format – a bunch of letters and numbers. Open that file and take a look. You will see two functions: `def upgrade( )` and `def downgrade( )` which are used to move forward with the migration or resort back to the state it was in before we ran the `flask db migrate` command. Since we just made one our first migration nothing should be wrong, so let’s go ahead and upgrade with the following command:
`flask db upgrade`

Anytime we want to add more migrations, we should always follow this procedure:
Make the necessary changes in the database model.
Generate a migration with the `flask db migrate` command.
Review the generated migration script and correct it if it has any inaccuracies.
Apply the changes to the database with the `flask db upgrade` command.


### ADDING OUR FIRST USER

It’s time to actually get some data in that database now! 

Let’s go to our terminal and open up the _flask shell_ for our application by entering the following: 
`flask shell`

Once we are inside the flask shell, we will want to enter the following:
`from app import db, User`
`user_brandon = User(email=’myemail@gmail.com’)`
`db.session.add(user_brandon)`
`db.session.commit( )`

So, we imported our database and our User Model from our application factory, created a user with an email address, added that user to our session, then committed it to the database. Pretty straight forward! We won’t have to do this ourselves for every user that is created, eventually we will create a function that will add the new users to the database for us through GraphQL. 


### ADDITIONAL MODELS

Before we get started on the GraphQL portion of this tutorial, let’s add just a bit more complexity to our models so we can really get a good understanding of how GraphQL works. 

Let’s make our models.py file look like the following:

```python
from backend import db
 
class User(db.Model):
   __tablename__ = 'users'
   id = db.Column(db.Integer, primary_key=True)
   email = db.Column(
       db.String(64), unique=True, index=True)
 
   #THIS IS NEW BELOW
   profile_id = db.Column(
       db.Integer, db.ForeignKey('profiles.id'))
   profile = db.relationship(
       "Profile", backref='user')
 
   def __repr__(self):
       return f"<User {self.email} >"
 
 
class Profile(db.Model):
   __tablename__ = 'profiles'
   id = db.Column(db.Integer, primary_key=True)
   first_name = db.Column(db.String(20))
   last_name = db.Column(db.String(20))
   skills = db.relationship("Skill")
 
   def __repr__(self):
       return f"<Profile {self.first_name} {self.last_name}: #{self.id} >"
 
 
class Skill(db.Model):
   __tablename__ = 'skills'
   id = db.Column(db.Integer, primary_key=True)
   name = db.Column(db.String(20), unique=True,
                    index=True, nullable=False)
   profile_id = db.Column(
       db.Integer, db.ForeignKey('profiles.id'))
 
   def __repr__(self):
       return f"<Skill {self.name} >"
```

Let’s break this apart. We added two new models: _Profile_ and _Skill_.  So every profile, will have a user that is attached it – that is where the extra two lines in the _User_ model come from:
```python
profile_id = db.Column(db.Integer, db.ForeignKey('profiles.id'))
profile = db.relationship("Profile", backref='user')
```

We are creating a _Many-to-One_ relationship in our database. There are many users, each of which has a profile. We create a _ForeignKey_ that tells us the exact ID of the profile that user has. Also, we let the database know that there is a relationship that we can access now by calling `User.profile` to get information about that profile. 

For more information on database relationships, check out the SQLAlchemy [documentation](https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html) about Basic Relationship Patterns. 




### SETTING UP GRAPHQL

If you’re interested in some more information about why to use GraphQL instead of a _REST API_, check out this [article](https://www.apollographql.com/blog/graphql/basics/why-use-graphql/). 

Time to set up our application to handle our GraphQL!

First we need to go to our `backend / __init__.py file` where we created our application factory, and add the following lines of code:

```python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
 
#THIS LINE IS NEW
from flask_graphql import GraphQLView
 
 
db = SQLAlchemy()
 
def create_app(config_name):
   …
 
   from .schema import schema
 
   app.add_url_rule(
       '/graphql',
       view_func=GraphQLView.as_view(
           'graphql',
           schema=schema,
           graphiql=True  # for having the GraphiQL interface
       )
   )
 
   …
 
   return app
```


We imported our schema from our schema.py file, but we haven’t created that yet so we will need to do that next! The rest of the code allows us to use GraphQL as a view function so the frontend can make the call to the backend and talk to GraphQL directly. We also set `graphiql=True` so we can use the grapgiQL interface that will let us see and practice using our queries – more on this later. 

Next, let’s open up our _backend/schema.py_ file. This is where our GraphQL schema will live. Schemas are an essential part of a GraphQL server implementation. Our schema is going to be made up of two components: _the queries and the mutations_. **Queries** are what we use to call the data that we are looking for. **Mutations** are used to create new data or edit existing data. For now, we’ll just focus on the queries portion. 

Let’s write the following code:

_Findr / backend / schema.py_

```python
import graphene
 
from .graphql.query import Query
 
schema = graphene.Schema(query=Query)
```


Again, you’ll notice that we made a call to yet another file, namely the _query.py_ file. We use this separate schema file as a way to stay organized. Eventually we will also add our mutations to this file as well, so it is important to have all of our schema information in one location. This helps the app scale well for future changes. 

### GRAPHQL QUERIES

Let’s open up our _query.py_ file and add the following lines of code:

_Findr / backend / graphql / query.py_ 

```python
import graphene
from graphene import relay
from graphene_sqlalchemy import SQLAlchemyConnectionField
 
from ..models import Profile as ProfileModel, \
   User as UserModel, \
   Role as RoleModel
 
from ..graphql.objects import UserObject as User, \
   ProfileObject as Profile \
 
 
class Query(graphene.ObjectType):
   node = relay.Node.Field()
 
   users = graphene.List(
       lambda: User, email=graphene.String(), user_id=graphene.Int())
 
 
   def resolve_users(self, info, email=None):
       query = User.get_query(info)
 
       if email:
           query = query.filter(UserModel.email == email)
       return query.all()
 
   all_users = SQLAlchemyConnectionField(User.connection)
```


We are making some good progress here, we wrote our first **resolver function**! A _resolver_ is a function that resolves a value for a type or field in a schema. Resolvers can return objects or scalars like Strings, Numbers, Booleans, etc. If an Object is returned, execution continues to the next child field. If a scalar is returned (typically at a leaf node), execution completes. If null is returned, execution stops there.

Our _users_ resolver function allows us to query all of the Users from our database, then filter by email if we choose to enter an email address in our GraphQL query (we’ll see this shortly as well). You can notice that one of the parameters of the `resolve_users` function is `email=None`. This is our default value which means that if we don’t input an email address in our query, then we will just return all users in the database. It is easy to see how we can add more filters once we start to add attributes to our User models, Profile models, etc. 

I would also like to point out the `all_users` variable and the `users` variable. `all_users` basically does the same thing as our users resolver function, however we can only search for all of the users, we cannot filter. Our `users` variable tells GraphQL that when we do a query on users, we will return the information in a list, with the ability to use the email parameter and/or the user_id parameter. 

A query of:
```
{
 users {
  email
  userId
  }
}
```

Will return something like:

```
{
  "data": {
    "users": [
      {
        "email": "bj.redmond19@gmail.com",
        "userId": 3
      },
      {
        "email": "bredmond1019@gmail.com",
        "userId": 2
      }
    ]
  }
}
```
Let’s finish off this by creating a _resolver function_ for our profile object as well:
```python
class Query(graphene.ObjectType):
   ...
   profiles = graphene.List(
       lambda: Profile, id=graphene.Int()
   )
 
   def resolve_profiles(self, info, id=None):
       query = Profile.get_query(info)
 
       if id:
           query = query.filter(
               ProfileModel.id == id)
       return query.all()
    …
```

We applied the same principles here as for the users. 

We’re almost ready to test our first query. We just have one more piece to the puzzle – our **GraphQL Objects**. If you noticed in the code above, we imported things from two different files. One was our _models.py_ file, which we’ve built and talked about already. The other was a _objects.py_ file that we still need to create. 

### GRAPHQL OBJECTS

Let’s open up our _objects.py_ file. 

_Findr / backend / graphql / object.py_

```python
import graphene
from graphene import relay
from graphene_sqlalchemy import SQLAlchemyObjectType
 
from ..models import User as UserModel
 
class UserObject(SQLAlchemyObjectType):
   user_id = graphene.Int(source='id')
 
   class Meta:
       model = UserModel
       interfaces = (relay.Node, )
```


Before we add the other objects to this file, let’s discuss what we have so far. We imported our models from our _models.py_ file. Then we are creating our `class UserObject`, which we can see extends the _SQLAlchemyObjectType_ – a class provided by [graphene-sqlalchemy](https://github.com/graphql-python/graphene-sqlalchemy) to integrate SQLAlchemy and GraphQL. The `user_id` attribute tells graphene to use the id associated with the model in the database, rather than the unique id graphql provides. You don’t need to use this attribute if you don’t want to, I find it useful for my applications. Finally, we are classing the _inner class_ [Meta](https://docs.graphene-python.org/en/latest/types/objecttypes/#objecttype-configuration-meta-class) that allows us to set different options. We are telling graphene_sqlalchemy to use the _UserModel_ and to use the [Relay Node](https://dev.to/augustocalaca/graphql-understanding-node-interface-33e). 

Now that we have a basic understanding, let’s add some more code to this file: 

```python
class ProfileObject(SQLAlchemyObjectType):
   class Meta:
       model = ProfileModel
       interface = (relay.Node, )
 
   skills = graphene.List(lambda: SkillObject, name=graphene.String(
   ))
 
   def resolve_skills(self, info, name=None):
       query = SkillObject.get_query(info=info)
       query = query.filter(
           SkillModel.profile_id == self.id)
       if name:
           query = query.filter(SkillModel.name == name)
 
       return query.all()
 
 
class SkillObject(SQLAlchemyObjectType):
   class Meta:
       model = SkillModel
       interface = (relay.Node, )
 
 
class SkillInput(graphene.InputObjectType):
   name = graphene.String()
   preferred_skill = graphene.Boolean()
```


Similarly to our UserObject we created above, we have our _ProfileObject_. However, this time we added a resolver function `resolve_skills` inside of our _ProfileObject_. Here we are using the exact same logic as before with our `resolve_users` function we created in our _query.py_ file. We put this resolver function inside of the _ProfileObject_ so that we know the skills query will take place under the ProfileObject rather than in its own query. Let’s take a look at what the GraphQL query would look like. 

```
{
  profiles {
    firstname
    skills {
      name
    }
  }
}
```

We can see that the skills query takes place directly inside the profiles query. Notice that _profiles_ is at the top of the chain of queries, just like _users_ was. That’s because the profiles resolver function was created in the queries class at the top level of our code. Hopefully now you can see how to implement hierarchical queries. 

This query would return something like:
```
{
  "data": {
    "profiles": [
      {
        "firstName": "Brandon",
        "skills": [
          {
            "name": "GraphQL"
          },
          {
            "name": "React"
          },
          {
            "name": "Flask"
          }

        ]
      }
    ]
  }
}
```

A few more points to dissect from our objects.py file is that we added a _Skill_ class and a _SkillInput_ class. The Skill class is straight forward and follows from our User class created earlier. The SkillInput class is going to be useful in our mutations that will follow later. 


### TESTING OUR GRAPHQL 

Alright! Everything is in place and we are ready to run our flask server and make our first query!

**NOTE** When I first installed graphql and graphene, I wasn’t able to view the following link you’re about to try. That was because I had originally installed graphene==3.0.0 and I had to make sure that I was using an earlier version. As noted in the beginning of this article, I was using `Flask-GraphQL==2.0.1` and `graphene==2.1.9` at the time this article was written. I would make sure you have those versions running or you may have to do some troubleshooting if you don’t want to revert back to the older versions. Once you do get it running, continue to the following paragraph. 

Go ahead and go to your terminal, make sure you are in the home directory, and run `flask run`
The usual response should appear and we can open up our browser and head to `http://localhost:5000/graphql`. Here we should see an empty GraphQL query page where we can test our queries and mutations, also just play around with it and learn!


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ic2x9y1rukm0vbvy1zq.png)

So far, all we’ve created was one _User_ and added it to our database (unless you’ve added more on your own). We can run our first query:
```
{
 users {
  email
  userId
  }
}
```
And we should see that single user be returned on the right hand side along with the email and the userId. 

### GRAPHQL MUTATIONS
Home stretch! Mutations are the last component we’re going to talk about in this article. After this you will have a good understanding of the fundamentals of how to use GraphQL! 

If we want to add more users, profiles, skills, etc, then we are going to need to utilize GraphQL’s mutations. Let’s jump right in by opening up our _mutations.py_ file.

_Findr / backend / graphql / mutations.py_

```python
import graphene
 
from backend import db
from ..graphql.objects import UserObject as User, ProfileObject as Profile, SkillInput
from ..models import User as UserModel, Profile as ProfileModel, Skill as SkillModel
 
 
 
class UserMutation(graphene.Mutation):
   class Arguments:
       email = graphene.String(required=True)
 
   user = graphene.Field(lambda: User)
 
   def mutate(self, info, email):
       user = UserModel(email=email)
 
       db.session.add(user)
       db.session.commit()
 
       return UserMutation(user=user)
 
 
class ProfileMutation(graphene.Mutation):
   class Arguments:
       first_name = graphene.String(required=True)
       last_name = graphene.String(required=True)
       user_id = graphene.Int(required=True)
       skills = graphene.List(SkillInput)
 
   profile = graphene.Field(lambda: Profile)
 
   def mutate(self, info, first_name, last_name, user_id, skills):
       user = UserModel.query.get(user_id)
 
       profile = ProfileModel(first_name=first_name, last_name=last_name)
 
       skill_list = [SkillModel(name=input_skill.name) for input_skill in skills]
 
       profile.skills.extend(skill_list)
 
       db.session.add(profile)
 
       user.profile = profile
       db.session.commit()
 
       return ProfileMutation(profile=profile)
 
 
class Mutation(graphene.ObjectType):
   mutate_user = UserMutation.Field()
   mutate_profile = ProfileMutation.Field()
```
 

This looks like a lot but it’s really not that bad. First, we import our objects and models as we’ve done before. Then, we create two classes: _UserMutation_ and _ProfileMutation_. Within each class there is an inner class _Arguments_ which are just the parameters for the mutation that we will input through GraphQL (I’ll demonstrate in just a bit). We tell graphene the name of the mutation field (_user, profile_) we are mutating by assigning those variables the `graphene.Field( )` values. Finally, we create a function called `mutate` that takes each of the attributes listed in the _Arguments_ inner class as a parameter. We take those inputs and use them to update our models in our database with the new information and commit them to the database. 

We create a Mutation Class at the end of this file that we will need to add to our schema. Let’s open up the _schema.py_ file again. 

_Findr / backend / schema.py_

```python
import graphene
 
 
from .graphql.query import Query
from .graphql.mutations import Mutation
 
 
schema = graphene.Schema(query=Query, mutation=Mutation)
```

Import the mutations that we’ve created and then add them to the end of the schema after our Query class we created earlier. 

Let’s make a mutation to test that it worked. Go back to `http://localhost:5000/graphql` after making sure that your flask server is still running, otherwise run it again. 
```
mutation {
  mutateUser(email: "myemail@gmail.com") {
    user {
      email
      
    }
  }
}
```

Should return:
```
{
  "data": {
    "mutateUser": {
      "user": {
        "email": "myemail@gmail.com"
      }
    }
  }
}
```

One thing to note about mutations is that it also returns a query of the information you created for that user. This can be useful in your application so you don’t have to make multiple queries for a new user, etc. 

Run a query for users and you should see the new user added to the database! 

Let’s create a new profile as well:

```
mutation {
  mutateProfile(
    firstName:"Brandon",
    lastName:"Smith",
    userId:3,
    skills: [{name:"Python"}, {name:"JavaScript"}]
)
  {
    profile {
      firstName
      lastName
      skills {
        name
      }
    }
  }
}
```

Here we can see where we used the _SkillInput_ class from before because we added two skills in the form of a list. Check out the _objects.py_ file from earlier. Also, it’s important to match the _userId_ with the correct user. Typically in your application, you will have that information already, or you will need to perform a query first to get the information. 

There we have it! 

To wrap up what we’ve done in this article, we’ve:
- Created Models for our database
- Created our Migrations folder to update our models
- Added users to our database from the flask shell
- Connected Flask to GraphQL
- Created GraphQL Objects, Queries, and Mutations
- Ran our own queries and mutations in graphiQL

I hope this article was helpful. Feel free to leave comments or questions! 

The next part of the series will be on creating a _React App_ utilizing _Babbel_ and _Webpack5_ instead of just using _create react app_. See you there!