Unlock the Power of API Development: Choosing the Right Architectural Style
The Rise of GraphQL
According to Google Trends, REST is currently the most searched and familiar API architectural style among developers. However, GraphQL, introduced by Facebook in 2015, is rapidly gaining traction. Despite being relatively new, GraphQL has already made a significant impact in the API development landscape.
Why Choose GraphQL?
So, what makes GraphQL an attractive choice for API development? Here are some compelling reasons:
- Fetch only what you need: GraphQL allows you to query data from multiple resources, eliminating the need for multiple endpoints and API calls.
- No more versioning headaches: With GraphQL, you can make changes to the API internals without affecting the resource URL, making version management a breeze.
- Strong type schema: GraphQL’s strongly typed schema ensures fewer errors and less debugging time.
Getting Started with GraphQL and Ariadne
Before diving into the world of GraphQL, it’s essential to familiarize yourself with key terms like Query, Mutation, Resolver, and Field. To get started, we’ll use Ariadne, a Python library that adopts a schema-first approach. This approach involves defining the schema for the GraphQL service and then implementing the code to match the definitions.
Building a Simple GraphQL API with Ariadne
Let’s create a simple GraphQL API that returns a list of destinations to visit. We’ll use Ariadne to define the schema and then implement the code. Once our code is ready, we can run it using uvicorn and interact with the API using the GraphQL Playground.
from ariadne import graphql_sync, make_executable_schema, load_schema_from_path, ObjectType
type_defs = load_schema_from_path("schema.graphql")
query = ObjectType("Query")
@query.field("destinations")
def resolve_destinations(_, info):
return [{"id": 1, "name": "Paris"}, {"id": 2, "name": "Rome"}]
schema = make_executable_schema(type_defs, query)
result = graphql_sync(schema, {"query": "{ destinations { id name } }"})
print(result)
Integrating Ariadne with Flask
While Ariadne can run independently, integrating it with Flask allows us to leverage the existing Flask ecosystem and utilize extensions like Flask-SQLAlchemy and Flask-Migrate. We’ll explore how to integrate Ariadne with Flask and create a more robust API.
from flask import Flask, request, jsonify
from ariadne import graphql_sync, make_executable_schema, load_schema_from_path, ObjectType
app = Flask(__name__)
type_defs = load_schema_from_path("schema.graphql")
query = ObjectType("Query")
@query.field("destinations")
def resolve_destinations(_, info):
return [{"id": 1, "name": "Paris"}, {"id": 2, "name": "Rome"}]
schema = make_executable_schema(type_defs, query)
@app.route("/graphql", methods=["POST"])
def graphql_interface():
result = graphql_sync(schema, request.get_json())
return jsonify(result)
if __name__ == "__main__":
app.run(debug=True)
Adding Mutations and Database Integration
To take our API to the next level, we’ll add mutations to allow users to add new places. We’ll also integrate with Flask-SQLAlchemy and Flask-Migrate to store data in a database instead of a list of dictionaries.
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///database.db"
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class Place(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
mutation = ObjectType("Mutation")
@mutation.field("createPlace")
def resolve_create_place(_, info, name):
place = Place(name=name)
db.session.add(place)
db.session.commit()
return {"id": place.id, "name": place.name}
schema = make_executable_schema(type_defs, query, mutation)