Skip to content

GraphQL Introduction for Comparison

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you'll learn about Graphql fundamentals. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

GraphQL is a query language and runtime for APIs where clients declaratively request exactly the data they need from a single endpoint.

What You'll Learn

By the end of this lesson, you will understand GraphQL's schema-driven design, write basic queries and mutations, and know how GraphQL's single-endpoint approach differs from REST.

Why It Matters

GraphQL addresses fundamental REST limitations: over-fetching, under-fetching, and multiple round trips. It excels for complex UIs and mobile applications.

Real-World Use

GitHub's public API v4 is GraphQL. Clients request exactly the data they need: query { Repository(owner:"facebook", name:"react") { name stargazerCount } }.

GraphQL Request Flow

sequenceDiagram
    Client->>Server: POST /graphql { "query": "user(id:1){name email}" }
    Server->>Server: Validate query against schema
    Server->>Server: Resolve fields
    Server->>Client: {"data": {"name":"Alice","email":"alice@x.com"}}

GraphQL Schema and Resolvers

# graphql_basics.py
from typing import Any, Dict, List

class GraphQLSchema:
    def __init__(self):
        self.types: Dict[str, Dict] = {}
        self.resolvers: Dict[str, callable] = {}

    def define_type(self, name: str, fields: Dict[str, str]):
        self.types[name] = fields

    def resolver(self, path: str):
        def decorator(fn):
            self.resolvers[path] = fn
            return fn
        return decorator

    def execute(self, query: str) -> Dict:
        data = {}
        errors = []

        lines = query.strip().split("\n")
        operation = lines[0].strip().split(" ")[0].lower()

        if "{" not in query:
            return {"error": "Syntax error"}

        brace_content = query[query.index("{") + 1:query.rindex("}")].strip()
        fields = [f.strip() for f in brace_content.split() if f.strip() and not f.startswith("(")]

        for field in fields:
            resolver_key = f"{operation}.{field}"
            resolver = self.resolvers.get(resolver_key)

            if resolver:
                try:
                    data[field] = resolver()
                except Exception as e:
                    errors.append(f"Cannot resolve '{field}': {e}")
            else:
                data[field] = None

        result = {"data": data}
        if errors:
            result["errors"] = errors
        return result

from graphql_basics import GraphQLSchema

schema = GraphQLSchema()
schema.define_type("User", {"id": "ID!", "name": "String!", "email": "String"})

@schema.resolver("query.users")
def resolve_users():
    return [{"id": "1", "name": "Alice", "email": "alice@x.com"}]

@schema.resolver("query.user")
def resolve_user():
    return {"id": "1", "name": "Alice", "email": "alice@x.com"}

result = schema.execute("{ users user }")
print(result)

Expected output:

{'data': {'users': [{'id': '1', 'name': 'Alice', 'email': 'alice@x.com'}], 'user': {'id': '1', 'name': 'Alice', 'email': 'alice@x.com'}}}

GraphQL vs REST Endpoint Comparison

# rest_vs_graphql_endpoints.py
from typing import Any, Dict, List

class RESTEndpoint:
    def get_user(self, user_id: int) -> Dict:
        return {"id": user_id, "name": "Alice", "email": "a@x.com", "posts": 5}

    def get_user_posts(self, user_id: int) -> List[Dict]:
        return [{"id": 10, "title": "Post", "user_id": user_id}]

    def get_user_followers(self, user_id: int) -> List[Dict]:
        return [{"id": 2, "name": "Bob"}]

class GraphQLEndpoint:
    def query(self, selection: List[str], user_id: int) -> Dict:
        user = {"id": user_id, "name": "Alice", "email": "a@x.com", "posts": 5}
        result = {}
        for field in selection:
            if field in user:
                result[field] = user[field]
        return {"data": result}

rest = RESTEndpoint()
graphql = GraphQLEndpoint()

# REST requires multiple calls
rest_result = {
    "user": rest.get_user(1),
    "posts": rest.get_user_posts(1),
}
print(f"REST (3 calls): {list(rest_result.keys())}")

# GraphQL single call
gql_result = graphql.query(["name", "email"], 1)
print(f"GraphQL (1 call): {gql_result}")

Expected output:

REST (3 calls): ['user', 'posts']
GraphQL (1 call): {'data': {'name': 'Alice', 'email': 'alice@x.com'}}

Common Mistakes

1. Not Defining a Schema First

GraphQL is schema-driven. Without a schema, there is no GraphQL. Always define types before resolvers.

2. N+1 Query Problem

Resolving a list of items triggers one query per item. Use DataLoader or batch resolvers to avoid N+1.

3. No Input Validation

GraphQL validates types but not business rules. Add validation in resolvers for required fields and constraints.

4. Overly Deep Nesting

Deeply nested queries can cause performance issues. Set query depth limits and complexity limits.

5. Ignoring Authentication

GraphQL endpoints are a single surface area. Secure all fields. Do not expose sensitive data in the schema.

Practice Questions

1. What problem does GraphQL solve?

Clients request exactly the data they need, eliminating over-fetching and under-fetching.

2. How many endpoints does a GraphQL API have?

One single endpoint, typically /graphql.

3. What is a resolver in GraphQL?

A function that fetches the data for a specific field in the schema.

4. What is the N+1 Problem?

Resolving a list of N items triggers N+1 database queries (1 for the list + N for each item's nested fields).

Challenge

Design a GraphQL schema for a blog with users, posts (with pagination), comments, and tags. Write resolvers for each type.

FAQ

Is GraphQL a database?

No, it is an API layer. Resolvers fetch data from databases, REST APIs, or other sources.

Does GraphQL replace REST?

No, they coexist. Many companies use both: REST for simple CRUD, GraphQL for complex data requirements.

Is GraphQL faster than REST?

It depends. GraphQL reduces payload size but resolver complexity can hurt. Profile both for your use case.

Does GraphQL support caching?

Yes, but differently from REST. Use persisted queries, CDN caching, and resolver-level caching.

Can I use GraphQL with any language?

Yes, GraphQL has implementations in JavaScript, Python, Ruby, Java, Go, Rust, and more.

Mini Project: Simple GraphQL Runner

# simple_graphql.py
from typing import Any, Dict

class SimpleGraphQL:
    def __init__(self):
        self.schema = {}
        self.resolvers = {}

    def type(self, name: str, fields: Dict):
        self.schema[name] = fields

    def resolve(self, name: str):
        def wrap(fn):
            self.resolvers[name] = fn
            return fn
        return wrap

    def query(self, fields: list) -> Dict:
        result = {}
        for field in fields:
            fn = self.resolvers.get(field)
            if fn:
                result[field] = fn()
        return {"data": result}

gql = SimpleGraphQL()
gql.type("User", {"id": "ID", "name": "String"})

@gql.resolve("users")
def users():
    return [{"id": 1, "name": "Alice"}]

print(gql.query(["users"]))

Expected output:

{'data': {'users': [{'id': 1, 'name': 'Alice'}]}}

What's Next

You understand GraphQL basics. Next, explore data fetching differences, then compare over-fetching in REST vs GraphQL.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro