Python FastAPI for GraphQL ft. Strawberry
Building a GraphQL API with FastAPI and Strawberry: A Comprehensive Guide
Table of contents
This tutorial will show how GraphQL can be implemented using FastAPI. In this article, we will be utilizing GraphQL, where clients can specify exactly what data they need. The server will only return that data, reducing the amount of data transferred over the network and improving the overall performance of the application. This article is not an Introduction to GraphQL, some basics of this framework are prerequisites and can be accessed here
System Architecture
When a client sends a GraphQL query to the server, the server processes the query by parsing it, validating it against the schema, and executing the appropriate resolvers to fetch the requested data from the database. The server then returns the requested data to the client in the form of a JSON response.
With this architecture, we can build a flexible and scalable API that can handle a wide range of queries and mutations with minimal overhead. By using FastAPI and Strawberry, we can take advantage of the performance and scalability benefits of modern Python web development while still providing a simple and intuitive API that is easy to use and maintain. Learn more here and here
Lets get Coding...
Libraries & Config
Step 1 Configuring venv & Libraries
Configure venv using
python -m venv venv
Activate venv using
source <path>/venv/bin/activate
Install Necessary Libraries
pip install typing fastapi uvicorn 'strawberry-graphql[debug-server]'
Strawberry
Strawberry is a Python library that provides a type-based approach to building GraphQL APIs. It simplifies the process of defining types, resolvers, and mutations by leveraging Python's type annotations. With Strawberry, developers can define GraphQL types using Python classes and use type hints to define the shape of the data returned by the API.
Strawberry also provides a powerful decorator-based syntax for defining resolvers, allowing developers to easily specify the data that should be returned for each GraphQL field. Additionally, Strawberry offers automatic schema generation and validation, simplifying the process of defining a GraphQL schema.
Learn more about Strawberry here . Some alternatives to strawberry are discussed here
Step 2 Importing Libraries
import typing
import strawberry
from fastapi import FastAPI
from strawberry.asgi import GraphQL
once the import is successful without any issues, we can proceed. U=You may have to reinstall starlette , I am using the following version as on May 2023 -
sse-starlette 1.3.3 , starlette 0.16.0
Building
Step 1 Defining Data Structure(s)
In GraphQL, data structures are types that specify the properties and shape of data objects that can be manipulated and queried by the API. These structures are defined in the GraphQL schema language, which enables developers to determine the types of data that can be queried and the fields available for each type. GraphQL includes several built-in data structures, such as scalar types like String, Boolean, and Int, as well as more complex types like List, Object, and Enum. Additionally, developers can create custom data structures using the schema language to create data models that are tailored to their application's needs. The ability to define data structures in the GraphQL schema language provides a clear, consistent, and understandable structure for developers to work with and allows clients to access data in a more efficient manner. Learn more here
# Data Structure
@strawberry.type
class User:
name: str
age: int
addresses: list["Address"]
@strawberry.type
class Address:
person: User
street: str
city: str
state: str
zip_code: str
In this code, two data structures are defined using the @strawberry.type
decorator: User
and Address
. User
includes properties for name
, age
, and a list of Address
objects. Address
includes properties for person
(which is a User
object), street
, city
, state
, and zip_code
. These data structures define the shape and properties of the objects that can be queried and manipulated by the GraphQL API, and are used to create a clear and consistent structure for developers to work with.
Step 2 Defining Get (Resolver) Function
# Note that these parameters, can also be fetched from a DB
def get_user(self) -> typing.List[User]:
address = Address(person=None, street="123 Main St", city="Anytown", state="CA", zip_code="12345")
user = User(name="John", age=30, addresses=[address])
address.person = user
return [user]
The get_user
function defines a resolver that returns a list of User
objects. It creates an Address
object with a street address, city, state, and zip code. Then it creates a User
object with a name, age, and a list containing the Address
object. It sets the person
property of the Address
object to the User
object, creating a bi-directional relationship between the two. Finally, it returns a list containing the User
object.
For a SQL connection, this code looks something like this
import asyncpg
async def get_user():
conn = await asyncpg.connect(user='your_username', password='your_password',
database='your_database', host='your_host')
rows = await conn.fetch('SELECT name, age FROM users')
users = []
for row in rows:
name, age = row
addresses = await get_user_addresses(conn, name)
user = User(name=name, age=age, addresses=addresses)
users.append(user)
await conn.close()
return users
async def get_user_addresses(conn, name):
rows = await conn.fetch('SELECT street, city, state, zip_code FROM addresses WHERE name = $1', name)
addresses = []
for row in rows:
street, city, state, zip_code = row
address = Address(person=None, street=street, city=city, state=state, zip_code=zip_code)
addresses.append(address)
return addresses
This part although touched, is out of scope for this article. Learn more about GraphQL and Databases here
Step 3 Query Function
@strawberry.type
class Query:
users: typing.List[User] = strawberry.field(resolver=get_user)
schema = strawberry.Schema(query=Query)
@strawberry.type
is a decorator provided by the Strawberry library that is used to define GraphQL types. In this code, it is used to define two types: User
and Address
. class Query
is used to define the queries that can be executed against the GraphQL API. In this case, the users
field is defined as a list of User
objects and the resolver function for this field is set to get_user
. The schema
the variable is created using strawberry.Schema
, which takes the root query type as an argument. In this case, the root query type is Query
.
Step 4 Create App and Route
# Create a FastAPI app
app = FastAPI() #--> CORS and other functionality can be added here for deployment.
# Mount the GraphQL app on a route
app.add_route("/graphql", GraphQL(schema))
# Add a WebSocket route for the GraphQL endpoint
app.add_websocket_route("/graphql", GraphQL(schema))
app.add_route("/graphql", GraphQL(schema))
mounts the GraphQL app on a route called /graphql
using the add_route()
method of the FastAPI app. The GraphQL
object takes the GraphQL schema object (schema
) as an argument and handles the GraphQL requests.
app.add_websocket_route("/graphql", GraphQL(schema))
adds a WebSocket route to the application, which is used for subscriptions in GraphQL. The GraphQL
object is used again here to handle the WebSocket connections.
These routes allow clients to make GraphQL queries and subscriptions to the API by sending HTTP or WebSocket requests to the appropriate endpoints.
Testing
Step 1 Run Code
To test in local run command
uvicorn <code file name>:app --reload
The following Screen appears:
Step 2 Add Query
{
users{
name
addresses{
state
street
}
}
}
This Query suggest that
For each user, the query is requesting the name
field and the addresses
field.
For each address, the query is requesting the state
and street
fields.
Step 3 GraphQL Explorer
Press the Explorer button on left-hand side to expose different data structures.
You can also use Postman for Testing.
Conclusion
In conclusion, FastAPI, GraphQL, and Strawberry are powerful tools that developers can use to build high-performance APIs with a strong type of system. FastAPI provides a simple and intuitive interface for building web applications with fast response times. GraphQL allows for efficient data fetching and supports powerful queries, while Strawberry simplifies the process of defining types and resolvers.
Together, these technologies allow developers to create APIs that are easy to maintain, flexible, and scalable. By leveraging the strengths of each of these tools, developers can build robust and performant applications that meet the needs of their users.
What's Next
If you are interested in using GraphQL with databases like MongoDB or SQL, you can explore the official documentation of MongoDB or PsyCopyg2 libraries to learn more about integrating them with GraphQL APIs.
For deployment, the article mentioned using Azure Web App, but there are many other options available, such as Heroku, AWS Elastic Beanstalk, and Google App Engine. You can choose the one that suits your needs and budget.
Overall, this article has provided an introduction to building GraphQL APIs using FastAPI and Strawberry. With the knowledge gained from this article, readers can explore more advanced features of GraphQL and build robust and scalable APIs.