Building a GraphQL API with FastAPI and Strawberry
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:
- 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.
- 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.
- 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.
- 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.
- 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: Thestrawberry.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.