DEV Community

Cover image for Understanding GraphQL: Core Concepts with Examples
Jakaria Masum
Jakaria Masum

Posted on

Understanding GraphQL: Core Concepts with Examples

In recent years, GraphQL has emerged as a powerful alternative to REST APIs for building modern web and mobile applications. Developed by Facebook in 2012 and open-sourced in 2015, GraphQL provides a flexible and efficient way to query and manipulate data. In this blog, we’ll explore the core concepts of GraphQL, how it works, and why it’s gaining popularity among developers. We’ll also walk through practical examples to help you understand how to use GraphQL effectively.


What is GraphQL?

GraphQL is a query language for APIs and a runtime for executing those queries by using a type system you define for your data. Unlike REST, where you have multiple endpoints for different resources, GraphQL exposes a single endpoint and allows clients to request exactly the data they need—nothing more, nothing less.

Key Features of GraphQL:

  1. Declarative Data Fetching: Clients specify the structure of the data they need, and the server responds with exactly that structure.
  2. Single Endpoint: GraphQL uses a single endpoint for all queries and mutations, simplifying API management.
  3. Strongly Typed: GraphQL APIs are strongly typed, meaning every piece of data has a defined type, reducing errors and improving tooling.
  4. Real-Time Data: With subscriptions, GraphQL supports real-time updates, making it ideal for applications like chat apps or live dashboards.

Core Concepts of GraphQL

To understand GraphQL, let’s break down its core concepts:

1. Schema and Types

The schema is the foundation of any GraphQL API. It defines the types of data you can query and the relationships between them. GraphQL uses a type system to describe the structure of the data.

Example:

type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • User and Post are object types.
  • ID! and String! are scalar types (basic data types like strings, integers, etc.).
  • The ! indicates that the field is non-nullable (it cannot return null).

2. Queries

Queries are used to fetch data from the server. They resemble the structure of the data you want to retrieve.

Example:

query {
  user(id: 1) {
    name
    email
    posts {
      title
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This query asks for:

  • The name and email of a user with id: 1.
  • The title of all posts written by that user.

The server might respond with:

{
  "data": {
    "user": {
      "name": "John Doe",
      "email": "john@example.com",
      "posts": [
        { "title": "GraphQL 101" },
        { "title": "Advanced GraphQL" }
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Mutations

Mutations are used to modify data on the server (e.g., create, update, or delete records).

Example:

mutation {
  createUser(name: "Jane Doe", email: "jane@example.com") {
    id
    name
    email
  }
}
Enter fullscreen mode Exit fullscreen mode

This mutation creates a new user and returns the id, name, and email of the created user.

The server might respond with:

{
  "data": {
    "createUser": {
      "id": "2",
      "name": "Jane Doe",
      "email": "jane@example.com"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Resolvers

Resolvers are functions that handle the logic for fetching or modifying data. Each field in a GraphQL query or mutation is backed by a resolver.

Example (in JavaScript):

const resolvers = {
  Query: {
    user: (parent, args, context, info) => {
      return users.find(user => user.id === args.id);
    }
  },
  Mutation: {
    createUser: (parent, args, context, info) => {
      const newUser = { id: String(users.length + 1), ...args };
      users.push(newUser);
      return newUser;
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

Here:

  • The user resolver fetches a user by id.
  • The createUser resolver adds a new user to the users array.

5. Subscriptions

Subscriptions enable real-time updates by allowing clients to subscribe to specific events.

Example:

subscription {
  newPost {
    id
    title
    author {
      name
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This subscription notifies the client whenever a new post is created.


Why Use GraphQL?

  1. Efficient Data Fetching: Clients can request only the data they need, reducing over-fetching and under-fetching.
  2. Single Endpoint: Simplifies API management and reduces the number of network requests.
  3. Strongly Typed Schema: Improves developer experience with better tooling and error handling.
  4. Real-Time Capabilities: Subscriptions make it easy to build real-time features.
  5. Versioning-Free: GraphQL APIs can evolve without breaking existing clients.

Example: Building a Simple GraphQL API

Let’s build a simple GraphQL API for a blog using Node.js and Apollo Server.

Step 1: Set Up the Project

mkdir graphql-blog
cd graphql-blog
npm init -y
npm install apollo-server graphql
Enter fullscreen mode Exit fullscreen mode

Step 2: Define the Schema

Create a file named schema.js:

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

const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
  }

  type Query {
    user(id: ID!): User
    posts: [Post!]!
  }

  type Mutation {
    createUser(name: String!, email: String!): User
    createPost(title: String!, content: String!, authorId: ID!): Post
  }
`;

module.exports = typeDefs;
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement Resolvers

Create a file named resolvers.js:

const users = [];
const posts = [];

const resolvers = {
  Query: {
    user: (parent, args) => users.find(user => user.id === args.id),
    posts: () => posts,
  },
  Mutation: {
    createUser: (parent, args) => {
      const newUser = { id: String(users.length + 1), ...args };
      users.push(newUser);
      return newUser;
    },
    createPost: (parent, args) => {
      const newPost = { id: String(posts.length + 1), ...args };
      posts.push(newPost);
      return newPost;
    },
  },
  User: {
    posts: (parent) => posts.filter(post => post.authorId === parent.id),
  },
  Post: {
    author: (parent) => users.find(user => user.id === parent.authorId),
  },
};

module.exports = resolvers;
Enter fullscreen mode Exit fullscreen mode

Step 4: Start the Server

Create a file named server.js:

const { ApolloServer } = require('apollo-server');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');

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

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});
Enter fullscreen mode Exit fullscreen mode

Run the server:

node server.js
Enter fullscreen mode Exit fullscreen mode

Step 5: Test the API

Open the GraphQL Playground at http://localhost:4000 and try the following queries:

  1. Create a user:
mutation {
  createUser(name: "Alice", email: "alice@example.com") {
    id
    name
    email
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Create a post:
mutation {
  createPost(title: "Hello GraphQL", content: "This is my first post!", authorId: "1") {
    id
    title
    author {
      name
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Fetch user and posts:
query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

GraphQL is a powerful tool for building modern APIs that are flexible, efficient, and easy to use. By understanding its core concepts—schema, queries, mutations, resolvers, and subscriptions—you can leverage GraphQL to create APIs that meet the specific needs of your applications. Whether you’re building a simple blog or a complex real-time application, GraphQL provides the tools you need to succeed.

Happy coding! 🚀

Top comments (0)