Building a GraphQL API with FastAPI and Strawberry

Shikha Pandey
7 min readJul 16, 2023

--

In today’s digital landscape, the need for efficient and flexible APIs is more prevalent than ever. As applications grow in complexity and data requirements, traditional REST APIs can become cumbersome to work with. This is where GraphQL, a query language and runtime, comes into play. GraphQL offers a more efficient and intuitive way to retrieve and manipulate data.

In this blog, we will explore how to build a GraphQL API using FastAPI and Strawberry. We will walk through the process of creating a simple CRUD (Create, Read, Update, Delete) API for managing user data stored in a MongoDB database. We will also discuss the advantages of GraphQL over traditional REST APIs and delve into the features provided by FastAPI and Strawberry.

The Need for GraphQL

Traditional REST APIs have been widely used for client-server communication. However, as applications grow larger and more complex, REST APIs can become difficult to maintain and inefficient in terms of data retrieval. This is primarily because REST APIs follow a fixed structure where each endpoint returns a predetermined data structure. As a result, clients often end up over-fetching or under-fetching data, leading to performance issues and increased network traffic.

GraphQL was developed by Facebook to address these limitations. It allows clients to request and receive precisely the data they need in a single request. With GraphQL, clients can define the structure of the response they expect, enabling efficient data retrieval and reducing the number of network round trips. This flexibility and efficiency make GraphQL an attractive choice for modern API development.

Advantages of GraphQL over REST APIs

GraphQL offers several advantages over traditional REST APIs:

  1. Efficient Data Retrieval: GraphQL enables clients to retrieve only the data they need, eliminating over-fetching and under-fetching. This reduces unnecessary network traffic and improves performance.
  2. Flexible Data Queries: Clients can specify the exact data requirements in their queries, allowing them to retrieve related data in a single request. This eliminates the need for multiple round trips to the server.
  3. Strongly Typed Schema: GraphQL APIs have a well-defined schema that describes the available types, fields, and relationships. This schema acts as a contract between the server and clients, providing clear documentation and improving collaboration.
  4. Versionless API: With GraphQL, API changes can be introduced without breaking existing clients. Clients can request specific fields and gracefully handle schema changes, ensuring backward compatibility.
  5. Rapid Development: GraphQL APIs are quick to develop, as clients and servers can work independently and evolve at their own pace. Clients have more control over the data they receive, reducing the need for backend changes.

Introducing FastAPI and Strawberry

FastAPI is a modern, high-performance web framework for building APIs with Python. It leverages Python type hints for automatic data validation, serialization, and documentation generation. FastAPI provides an easy-to-use interface and impressive performance capabilities, making it an excellent choice for building GraphQL APIs.

Strawberry, on the other hand, is a GraphQL library for Python that follows a code-first approach. It allows developers to define types, queries, mutations, and resolvers using Python classes and decorators. Strawberry brings the power of GraphQL to Python and seamlessly integrates with FastAPI.

Setting Up the Project

To get started, make sure you have the following prerequisites installed:

  • Python 3.7+
  • FastAPI (pip install fastapi)
  • Strawberry (pip install strawberry)
  • PyMongo (pip install pymongo)
  • Uvicorn (pip install uvicorn)

Create a new directory for your project and navigate into it in your terminal. Inside the project directory, create a new Python file called main.py and open it in your favorite code editor.

Importing Required Dependencies

In main.py, we will begin by importing the necessary dependencies for our application:

from fastapi import FastAPI
from typing import List
from pymongo import MongoClient
import strawberry
from strawberry.asgi import GraphQL

Here, we import FastAPI from the fastapi module, List from the typing module, MongoClient from the pymongo module, and strawberry along with its GraphQL implementation from the strawberry module.

Creating the FastAPI Application

Next, we will create an instance of the FastAPI class to initialize our application:

app = FastAPI()

Connecting to MongoDB

We will now establish a connection to our MongoDB database using the MongoClient class:

client = MongoClient("mongodb://localhost:27017/")
db = client["your-database-name"]
collection = db["users"]

Here, we create a MongoClient instance pointing to localhost on the default MongoDB port 27017. We then access the database and the users collection within that database.

Defining the User Model

Now, let’s define our User model using Strawberry's @strawberry.type decorator:

@strawberry.type
class User:
id: int
name: str

This model represents a user and has two fields: id (integer) and name (string).

Defining the Root Resolver

  • Root Resolver: The root resolver in GraphQL serves as the entry point for executing queries or mutations. It determines how to fetch or manipulate the requested data and acts as the bridge between the client’s requests and the underlying data sources.
  • @strawberry.type Decorator: The @strawberry.type decorator is used in Strawberry to define GraphQL types. It automatically generates the corresponding GraphQL type based on the class fields, allowing for automatic schema generation and ensuring type safety in the code.

Define the root resolver for our GraphQL API using Strawberry’s @strawberry.type decorator:

@strawberry.type
class Query:
@strawberry.field
def users(self) -> List[User]:
users = []
for user_data in collection.find():
user = User(id=user_data["id"], name=user_data["name"])
users.append(user)
return users

@strawberry.field
def user(self, id: int) -> User:
user_data = collection.find_one({"id": id})
if user_data:
return User(id=user_data["id"], name=user_data["name"])
else:
return None

Here, we define the Query class using the @strawberry.type decorator. It has two resolver functions: users and user.

The users resolver fetches all the users from the MongoDB collection and returns a list of User objects.

The user resolver fetches a specific user by their id from the MongoDB collection and returns a single User object. If the user is not found, it returns None.

Defining Mutations

  • Mutation: In GraphQL, a mutation is a type of operation that allows modification or creation of data on the server. It is used to perform write operations, such as creating, updating, or deleting data.
  • strawberry.mutation Decorator: The strawberry.mutation decorator in Strawberry is used to define a GraphQL mutation. It marks a method or function as a mutation resolver, specifying that it can modify data on the server and providing the necessary input arguments and return type for the mutation.

Now, let’s define the mutations for creating, updating, and deleting users:

@strawberry.type
class Mutation:
@strawberry.mutation
def create_user(self, name: str) -> User:
user = {"id": get_next_id(), "name": name}
collection.insert_one(user)
return User(id=user["id"], name=user["name"])

@strawberry.mutation
def update_user(self, id: int, name: str) -> User:
collection.update_one({"id": id}, {"$set": {"name": name}})
updated_user = collection.find_one({"id": id})
return User(id=updated_user["id"], name=updated_user["name"])

@strawberry.mutation
def delete_user(self, id: int) -> User:
deleted_user = collection.find_one_and_delete({"id": id})
return User(id=deleted_user["id"], name=deleted_user["name"])

We define the Mutation class using the @strawberry.type decorator. It contains three mutation functions: create_user, update_user, and delete_user.

The create_user mutation creates a new user with the provided name, assigns it a unique id obtained from the get_next_id() function, and inserts it into the MongoDB collection. It returns the created user as a User object.

The update_user mutation updates the user with the provided id in the MongoDB collection with the new name value. It then fetches the updated user from the collection and returns it as a User object.

The delete_user mutation deletes the user with the provided id from the MongoDB collection. It returns the deleted user as a User object.

Creating the Strawberry Schema

  • Strawberry Schema: The Strawberry schema is an object created using the strawberry.Schema class. It defines the structure and operations available in the GraphQL API by combining the query and mutation types, along with their respective resolvers. The schema acts as a contract between the server and clients, providing a clear definition of the available types, fields, and operations in the API.

Finally, we create the Strawberry schema using the strawberry.Schema class:

schema = strawberry.Schema(query=Query, mutation=Mutation)

Here, we provide the Query and Mutation classes as arguments to the strawberry.Schema class to create the schema for our API.

Mounting the GraphQL Endpoint in FastAPI

To expose our GraphQL API, we mount the GraphQL endpoint in FastAPI:

app.mount("/graphql", GraphQL(schema, debug=True))

Here, we mount the /graphql endpoint with the GraphQL class, passing our schema as an argument. We also enable debugging for our GraphQL API by setting debug=True.

Running the Application

To run the FastAPI server and test our GraphQL API, execute the following command in your terminal:

uvicorn main:app --reload

The --reload option enables automatic reloading of the server whenever changes are made to the code.

Testing the API

Now that our server is running, let’s test our API using some example GraphQL queries and mutations.

Create User

To create a new user, send a GraphQL mutation request:

mutation {
createUser(name: "Shikha Pandey") {
id
name
}
}

This mutation creates a new user with the name “Shikha Pandey” and returns the id and name of the created user.

Update User

To update a user, send a GraphQL mutation request:

mutation {
updateUser(id: 1, name: "Kritika Pandey") {
id
name
}
}

This mutation updates the user with id 1 and sets their name to "Kritika Pandey". It returns the updated user's id and name.

Delete User

To delete a user, send a GraphQL mutation request:

mutation {
deleteUser(id: 1) {
id
name
}
}

This mutation deletes the user with id 1 from the collection and returns the id and name of the deleted user.

Get All Users

To get a list of all users, send a GraphQL query request:

query {
users {
id
name
}
}

This query fetches all the users from the collection and returns their id and name as a list.

Get User by ID

To get a specific user by id, send a GraphQL query request:

query {
user(id: 1) {
id
name
}
}

This query fetches the user with id 1 from the collection and returns their id and name.

Conclusion

In this blog, we explored how to build a GraphQL API using FastAPI and Strawberry. We discussed the need for GraphQL and its advantages over traditional REST APIs. FastAPI provided a high-performance web framework, while Strawberry allowed us to define the schema, resolvers, and mutations using Python classes and decorators. By leveraging FastAPI and Strawberry, we were able to build a powerful and efficient GraphQL API.

You can find the complete code for this project in the GitHub repository: [here]

Feel free to explore and extend the code to add more functionality, validation, and authentication based on your requirements. FastAPI and Strawberry offer many features and integrations to help you build robust and scalable GraphQL APIs for your applications.

--

--