Python FastAPI for GraphQL ft. Strawberry

Python FastAPI for GraphQL ft. Strawberry

Building a GraphQL API with FastAPI and Strawberry: A Comprehensive Guide

ยท

7 min read

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

GraphQL Server Connected Database

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.

Did you find this article valuable?

Support Everything Python ๐Ÿ๐Ÿš€ by becoming a sponsor. Any amount is appreciated!

ย