GraphQL 101: From Basics to Advanced

GraphQL is a query language for APIs and a runtime for executing those queries. It provides a complete and understandable description of the data in your API, empowering clients to request only the data they need and nothing more.

Why Use GraphQL?

GraphQL was created to solve common issues with REST APIs, such as over-fetching, under-fetching, and multiple network requests. Unlike REST, which requires multiple API calls to obtain related data, GraphQL can fetch exactly what is needed in a single request.

Key Benefits:

  • Efficient Data Fetching: Clients can request only the required fields, reducing unnecessary data transfer.

  • Single Endpoint: Unlike REST APIs, which often have multiple endpoints, GraphQL provides a single endpoint (/graphql).

  • Strongly Typed Schema: GraphQL APIs are schema-driven, reducing the chances of errors.

  • Real-time Updates: GraphQL supports subscriptions, allowing real-time data fetching.

  • Better Developer Experience: The self-documenting schema makes it easier to understand and use APIs.


GraphQL vs. REST API

GraphQL is often compared to REST, but it offers more flexibility. Here's a comparison:

FeatureGraphQLREST
Data FetchingFetches only required dataOften over-fetches or under-fetches
EndpointsSingle endpoint (/graphql)Multiple endpoints per resource
TypingStrongly typed schemaNo enforced schema
Real-time SupportBuilt-in subscriptionsRequires WebSockets or polling
PerformanceLess network overheadHigher overhead with multiple requests

Example:

Fetching user and their posts:

REST API (Multiple Calls)

  1. GET /user/1 โ†’ Fetch user details

  2. GET /user/1/posts โ†’ Fetch posts separately

GraphQL (Single Query)

query {
  user(id: "1") {
    name
    posts {
      title
      content
    }
  }
}

This makes GraphQL faster and more efficient.


Setting Up a GraphQL Server

To build a GraphQL server, we need a GraphQL schema and resolvers to handle queries.

Step 1: Install Dependencies

Using Apollo Server (Node.js example):

npm install apollo-server graphql

Step 2: Define the Schema

The schema defines the structure of the API.

type Query {
  hello: String
}

Step 3: Create Resolvers

Resolvers return the data requested in the query.

const resolvers = {
  Query: {
    hello: () => 'Hello, GraphQL!',
  },
};

Step 4: Start the Server

const { ApolloServer, gql } = require('apollo-server');

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);
});

This starts a GraphQL API that can be queried at http://localhost:4000.


Queries

A query is a request to fetch data from a GraphQL server.

Example Query

query {
  user(id: "1") {
    name
    email
  }
}

Schema Definition:

type Query {
  user(id: ID!): User
}

type User {
  id: ID!
  name: String
  email: String
}

Resolver:

const resolvers = {
  Query: {
    user: (_, { id }) => users.find(user => user.id === id),
  },
};

This returns only the requested fields, making it efficient.


Mutations

A mutation is used to modify data (create, update, delete records).

Example Mutation

mutation {
  createUser(name: "Alice", email: "alice@example.com") {
    id
    name
  }
}

Schema:

type Mutation {
  createUser(name: String!, email: String!): User
}

Resolver:

Mutation: {
  createUser: (_, { name, email }) => {
    const newUser = { id: users.length + 1, name, email };
    users.push(newUser);
    return newUser;
  },
};

This allows clients to modify data easily.


Subscriptions

Subscriptions allow real-time updates by pushing data to clients when an event occurs.

Example Subscription

subscription {
  userCreated {
    id
    name
  }
}

Schema:

type Subscription {
  userCreated: User
}

Resolver with PubSub:

const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();

const resolvers = {
  Subscription: {
    userCreated: {
      subscribe: () => pubsub.asyncIterator(['USER_CREATED']),
    },
  },
  Mutation: {
    createUser: (_, { name, email }) => {
      const newUser = { id: users.length + 1, name, email };
      users.push(newUser);
      pubsub.publish('USER_CREATED', { userCreated: newUser });
      return newUser;
    },
  },
};

When a new user is created, all subscribed clients get updated automatically.


Directives

Directives are special annotations in GraphQL that modify query execution.

Example: @deprecated Directive

type Query {
  oldField: String @deprecated(reason: "Use newField instead")
  newField: String
}

This warns developers when they use oldField.


Caching in GraphQL

GraphQL doesnโ€™t provide built-in caching, but tools like Apollo Server offer caching mechanisms.

Example: Apollo Cache

const server = new ApolloServer({
  typeDefs,
  resolvers,
  cache: "bounded",
});

This optimizes performance by reducing repeated database queries.


GraphQL Status Codes

GraphQL responses always return a 200 OK status code at the HTTP level, even for errors. Instead of relying on HTTP status codes for error handling, GraphQL includes a errors field in the response body.

Example Response with Errors:

{
  "errors": [
    {
      "message": "User not found",
      "locations": [{ "line": 2, "column": 3 }],
      "path": ["user"]
    }
  ],
  "data": {
    "user": null
  }
}

This approach ensures consistent response formats, making it easier for clients to handle errors.

For handling authentication or other specific cases, middleware can be used to return appropriate HTTP status codes before executing the GraphQL query.


GraphQL Playground

GraphQL Playground is an interactive tool for testing queries.

Key Features

  • Auto-completion: Suggests queries based on schema.

  • Schema Documentation: Helps explore API structure.

  • Query History: Stores previously executed queries.

  • Real-time Subscriptions: Allows subscription testing.

Enable Playground in Apollo Server

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: true, 
  playground: true,
});

Available at http://localhost:4000.


GraphQL Code Generation

GraphQL Codegen automates the creation of strongly typed GraphQL code.

Installation

npm install @graphql-codegen/cli

Configuration

schema: "http://localhost:4000/graphql"
generates:
  src/generated/graphql.ts:
    plugins:
      - "typescript"
      - "typescript-operations"
      - "typescript-resolvers"

Run Codegen

npx graphql-codegen

This helps in generating types and queries automatically.

Advanced Concepts

  1. Batching and Caching with DataLoader: DataLoader optimizes queries by batching and caching requests.

  2. Custom Scalars: Define custom types like Date.

  3. Authentication and Authorization: Use middleware to handle authentication.


GraphQL provides unparalleled flexibility and power for building APIs. Developers can build robust, scalable, and efficient applications by understanding their fundamentals and leveraging advanced features. It is a powerful alternative to REST, offering flexibility, efficiency, and real-time capabilities.

๐Ÿ‘‹ Enjoyed this blog?

Reach out in the comments below or on LinkedIn to let me know what you think of it.

For more updates, do follow me here :)

0
Subscribe to my newsletter

Read articles from Aakanksha Bhende directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aakanksha Bhende
Aakanksha Bhende

Software Engineer | Open Source Enthusiast | Mentor | Learner I love documenting stuff that I come across and find interesting. Hoping that you will love reading it and get to know something new :)