GraphQL serves as a language for asking questions to APIs and as a tool for getting answers from existing data. It’s like a translator that helps your application talk to databases and other systems. When you use GraphQL, you’re like a detective asking for specific clues – you only get what you ask for.
Why Choose GraphQL?
GraphQL is pretty cool because it fixes some common problems we face with traditional ways of getting data (like REST APIs). Here’s why:
- Get Exactly What You Need: No more getting extra info you don’t want or missing out on stuff you do.
- Flexible Requests: You can ask for different things in one go, saving time and making things simpler.
- Easy Development: Imagine having just one stop for all your data needs – that’s GraphQL! It’s like a super organized library where everything has its place.
- Clear Rules: GraphQL sets clear rules between your app and the servers, making sure everyone understands each other perfectly.
- Better Performance: By sending only what’s necessary, GraphQL makes your app faster, especially on phones or slow connections.
Database Connectivity with GraphQL
GraphQL makes it easy to connect to databases and perform operations. Here, MySQL storage is used, and SQLAlchemy helps integrate seamlessly.
Enhancing GraphQL with Strawberry
Strawberry adds extra functionality to GraphQL functions, including queries, types, fields, and mutations. It even supports file uploads within queries.
Exploring GraphQL with FastAPI: A Practical Guide to begin with
To utilize GraphQL with FastAPI, the process involves several steps:
- Installation: Begin by installing the necessary packages using the provided command: pip install strawberry-GraphQL uvicorn fastapi mysql-connector-python SQLAlchemy.
- Database Setup: Begin by establishing a connection to the database to enable operations. In this example, MySQL is utilized for storage, and SQLAlchemy facilitates connectivity to the MySQL database as ORM. This setup typically involves creating a database.py file to manage the connection and a models.py file defining the table columns for the database.
database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DB_USER = graphql_query_user
DB_HOST = 'localhost'
DB_PORT = '3306'
DB_NAME = 'user_data' #Enter the database name
DB_PASSWORD = '*****'
# MySQL connection URL
SQLALCHEMY_DATABASE_URL =
f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
models.py
from database import Base
from sqlalchemy import Column, Integer, String
from database import engine
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
firstName = Column(String(255))
lastName = Column(String(255))
age = Column(Integer)
gender = Column(String(10))
email = Column(String(255))
phone = Column(String(20))
username = Column(String(255))
password = Column(String(255))
# Create the tables in the database
Base.metadata.create_all(bind=engine)
Defining Schemas: Once connected to the database, define the schema for the User in a schemas.py file. This schema outlines the structure of the User data that will be handled by GraphQL.
schemas.py
import strawberry
@strawberry.type
class UserType:
id: int
firstName: str
lastName: str
age: int
gender: str
email: str
phone: str
username: str
password: str
@strawberry.type
class DeleteUserResponse:
success: bool
message: str
CRUD Operations: Implement the CRUD (Create, Read, Update, Delete) operations in a crud.py file. This involves defining functions to handle operations such as fetching all users, retrieving a user by their ID, updating, deleting, and creating users. These operations are typically categorized under the Query and Mutation types in GraphQL.
crud.py
from models import User
import strawberry
from strawberry.extensions import FieldExtension
from typing import List, Any, Callable
from database import SessionLocal
from schemas import UserType, DeleteUserResponse
@strawberry.type
class Query:
@strawberry.field
async def users(limit: int = 10, offset: int = 0) -> List[UserType]:
db = SessionLocal()
users = db.query(User).offset(offset).limit(limit).all()
db.close()
return users
@strawberry.field
async def user_by_id(id: int) -> UserType:
db = SessionLocal()
user = db.query(User).filter(User.id == id).first()
db.close()
return user
- The Query class, adorned with @strawberry.type, encompasses two GraphQL query functions responsible for fetching user data from the database.
- The users function retrieves user details from the database, allowing optional parameters limit and offset to specify the number of users returned and the starting index respectively. Upon execution, it opens a session with the database, queries for users with the specified limit and offset, closes the session, and returns the fetched users as a list of UserType.
- The user_by_id function retrieves user details based on the provided id, opening a session with the database, querying for a user with the matching id, closing the session, and returning the user details corresponding to the provided id as a single UserType. These functions enable efficient data retrieval, catering to scenarios such as fetching all users or accessing details of a specific user by their ID.
@strawberry.type
class Mutation:
@strawberry.mutation
async def create_user(firstName: str, lastName: str, age: int, gender: str, email: str, phone: str, username: str, password: str) -> UserType:
db = SessionLocal()
new_user = User(firstName=firstName, lastName=lastName, age=age, gender=gender, email=email, phone=phone, username=username, password=password)
db.add(new_user)
db.commit()
db.refresh(new_user)
db.close()
return new_user
@strawberry.mutation
async def update_user(id: int, firstName: str, lastName: str, age: int, gender: str, email: str, phone: str, username: str, password: str) -> UserType:
db = SessionLocal()
user = db.query(User).filter(User.id == id).first()
if user:
user.firstName = firstName
user.lastName = lastName
user.age = age
user.gender = gender
user.email = email
user.phone = phone
user.username = username
user.password = password
db.commit()
db.refresh(user)
db.close()
return user
@strawberry.mutation
async def delete_user(id: int) -> DeleteUserResponse:
db = SessionLocal()
user = db.query(User).filter(User.id == id).first()
if user:
db.delete(user)
db.commit()
db.close()
return DeleteUserResponse(success=True, message=f"User with ID {id} deleted successfully.")
else:
db.close()
return DeleteUserResponse(success=False, message=f"User with ID {id} not found.")
The Mutation class, decorated with @strawberry.type, defines three GraphQL mutation functions to create, update, and delete user data in the database.
- The create_user mutation function asynchronously creates a new user with the provided parameters – first name, last name, age, gender, email, phone, username, and password. Upon execution, it establishes a connection to the database, adds the new user, commits the transaction, refreshes the database to reflect changes, and finally closes the session before returning the newly created user.
- The update_user mutation function updates an existing user’s details based on the provided id and other parameters. It begins by querying the database for the user with the matching id, and if found, updates the user’s attributes with the new values. It commits the changes, refreshes the database, closes the session, and returns the updated user. If no user with the provided id is found, it returns None.
- The delete_user mutation function deletes a user from the database based on the provided id. It starts by querying the database for the user with the specified id. If the user exists, it deletes the user, commits the transaction, and returns a success message along with a boolean indicating successful deletion. If no user is found with the provided id, it returns a failure message stating that the user was not found. Finally, it closes the database session before returning the response. These mutation functions provide essential functionalities for managing user data within the application.
Schema Definition: Define the schema using Strawberry, specifying the mutations and queries that correspond to the CRUD operations implemented in the crud.py file.
schema = strawberry.Schema(query=Query,mutation=Mutation)
6. FastAPI Endpoint Creation: Create a FastAPI endpoint at “/graphql” to handle GraphQL mutations. Use add_route to integrate the GraphQL functionality provided by Strawberry into your FastAPI application.
main.py
from fastapi import FastAPI
from strawberry.asgi import GraphQL
from crud import schema
app = FastAPI()
graphql_app = GraphQL(schema)
# Add GraphQL routes to FastAPI app
app.add_route("/graphql", graphql_app)
7. Running the Application: Execute the command uvicorn main:app –reload to run the application. After running, access it at http://localhost:8000/GraphQL. You can perform queries directly within the GraphQL body in Postman.
Query to Execute:
# Query to get user by id
query{
userById(id: 1){
firstName
lastName
email
password
}
}
# Query to get users details
query{
users{
id
firstName
lastName
email
}
}
# Query to create user
mutation {
createUser(
firstName: "John"
lastName: "Doe"
age: 30
gender: "Male"
email: "john.doe@example.com"
phone: "1234567890"
username: "johndoe"
password: "password123"
) {
id
firstName
lastName
age
gender
email
phone
username
}
}
# Query to delete user
mutation {
deleteUser(id: 23) {
success
message
}
}
# Query to update user details
mutation {
updateUser(
id: 21
firstName: "John"
lastName: "Doe"
age: 30
gender: "Male"
email: "john.doe@example.com"
phone: "1234567890"
username: "johndoe"
password: "password123"
) {
id
firstName
lastName
age
gender
email
phone
username
}
}
Here’s a screenshot from Postman showing the testing of fetching a user by their ID using GraphQL
File upload with GraphQL
- Install the required packages using this command – pip install strawberry-GraphQL uvicorn fastapi python-multipart
- Create a function that accepts a file as input and reads it. This function, named read_file, processes the uploaded file. Schema using Strawberry, specifying the mutation. Ensure to override the default scalar for file uploads to utilize the provided Upload scalar.
crud.py
from sqlalchemy.orm import Session
import strawberry
from typing import List, Union, Any, Callable
from strawberry.file_uploads import Upload
from starlette.datastructures import UploadFile
@strawberry.type
class Mutation:
@strawberry.mutation
async def read_file(self, file: Upload) -> str:
return (await file.read()).decode("utf-8")
schema = strawberry.Schema(query=Query,mutation=Mutation, scalar_overrides={UploadFile: Upload})
Create a FastAPI endpoint at “/GraphQL” to handle GraphQL mutations. Use add_route to integrate the GraphQL functionality provided by Strawberry into your FastAPI application.
main.py
from fastapi import FastAPI
from strawberry.asgi import GraphQL
from crud import schema
app = FastAPI()
GraphQL_app = GraphQL(schema)
# Add GraphQL routes to FastAPI app
app.add_route("/GraphQL", GraphQL_app)
- Execute the command uvicorn main:app –reload to run the application. After running, access it at http://localhost:8000/GraphQL.
You can also make API calls using Postman.
To upload a file using Postman, follow these steps:
- Set up the request as a POST request to the GraphQL endpoint.
- In the request body, select “form-data”.
- Add the following key-value pairs in the form-data:
1. Key: operations
Value:
{
“query”: “mutation($file: Upload!){ readFile(file: $file) }”,
“variables”: { “file”: null }
}
2. Key: map
Value:
{ “file”: [“variables.file”] }
3. Key: file
Value: Select the file you want to upload using the file selector.
This setup allows you to upload a file via the file parameter.
Here’s a screenshot from Postman showing the testing of reading file data by upload a file using GraphQL.
In conclusion, GraphQL makes CRUD operations easier by putting them all in one place through queries and mutations. This makes it easier for developers to build APIs and for clients to get exactly the data they need, making apps faster. But GraphQL faces challenges like query complexity, where really deep queries can slow down servers. Also, caching and setting limits on requests can be tricky, which affects how well servers run.