GraphQL Core Concepts
What is GraphQL
Section titled “What is GraphQL”- GraphQL is a query language for APIs, developed by Facebook
- It solves the pain points of REST, like
over-fetchingorunder-fetching
It can be divided into four parts :-
- Schema - GraphQL Blueprint
- Queries - Fetching Data
- Mutations - Create, Update, Delete (CUD)
- Subscriptions - Real-Time Updates
Schema
Schema - GraphQL Blueprint
Section titled “Schema - GraphQL Blueprint”- The
schemais the heart of aGraphQL API. - It defines what data is
available, how it’sstructured, and whatoperations(queries, mutations, subscriptions) you can perform. - Written in
Schema Definition Language(SDL), it’sreadableanddeclarative.
# Define the overall structure of the GraphQL API,# Specifying the entry points for queries, mutations, and subscriptions
schema {
# The root type for reading data (queries) query: Query
# The root type for modifying data (mutations) mutation: Mutation
# The root type for real-time data updates (subscriptions) subscription: Subscription}
# Define the Query type, which contains fields that can be queriedtype Query { # A simple field that returns a String value, e.g., "Hello, World!" hello: String}- This schema declares a
Querytype with ahellofield that returns astring. - The schema ensures consistency, so clients know exactly what’s possible.
Types : The Building Blocks of Data
Section titled “Types : The Building Blocks of Data”- Scalar Types
- Object Types
- Union Types
1. Scalar Types - The Primitives
ID: A unique identifier, often a stringString,Int,Float,Boolean
2. Object Types - The Structure
# Define a Post type to represent a blog post or similar content entity in the GraphQL schematype Post { # A unique identifier for the post, marked as non-nullable (required) id: ID! # The title of the post, marked as non-nullable (required) title: String! # The content of the post, optional (nullable) content: String # The author of the post, represented by a User type; can be null if not assigned author: User}Enums: Restricted Values
# Define the enum typeenum UserRole { ADMIN EDITOR VIEWER}
# Define the User type that uses the enumtype User { role: UserRole}Interfaces: Shared Fields
- Interfaces define a contract that multiple types can implement, sharing common fields.
# Define an interface named Character that specifies a common structure for types like Hero and Villaininterface Character { id: ID! name: String!}
# Define a Hero type that implements the Character interface, adding specific fields for a herotype Hero implements Character { id: ID! name: String! powers: [String]}
# Define a Villain type that implements the Character interface, adding specific fields for a villaintype Villain implements Character { id: ID! name: String! evilPlan: String}Union Types: Mixing Unrelated Types
- Unions allow a field to return one of several types that don’t share fields.
# Define the union typeunion SearchResult = User | Post
# Define the User typetype User { id: ID! name: String! email: String}
# Define the Post typetype Post { id: ID! title: String! content: String}
# Define the schema with Query typeschema { query: Query}
# Define the Query type that includes the search fieldtype Query { search(query: String!): [SearchResult]}Response:
{ "data": { "search": [ { "name": "Alice", "email": "alice@example.com" }, { "title": "GraphQL Basics", "content": "Learn GraphQL..." } ] }}Directives: Decorators for Extra Behavior
- Directives are like decorators
- They add extra behavior or metadata to fields, types, or queries without altering their core functionality
# Define a custom directive named @uppercase that can be applied to FIELD_DEFINITION locationsdirective @uppercase on FIELD_DEFINITION
# Define the Query type, which serves as the entry point for reading datatype Query { # A field that returns a String value, decorated with the @uppercase directive to transform its output greeting: String @uppercase}- Here,
@uppercaseonFIELD_DEFINITIONmight transform thegreetingfield’s value (e.g., “hello” to “HELLO”).
Built-In Directives
@include(if: Boolean): Include a field if the condition is true.@skip(if: Boolean): Skip a field if the condition is true.@deprecated(reason: String): Mark a field as deprecated.
Custom Directives
- You can define custom directives, like
@uppercaseabove, or for authorization.
Authorization with Directives
- Custom directives like
@auth(role: String)can enforce access control.
# Define the Query type, which serves as the entry point for reading datatype Query { # A field that returns a String value, restricted to users with the ADMIN role using the @auth directive secretData: String @auth(role: "ADMIN")}- This ensures only admins access
secretData. Directives are powerful for adding behaviors like authorization or formatting.
Queries
Queries - Fetching Data
Section titled “Queries - Fetching Data”Queries are how you read data in GraphQL. They’re read-only and can range from simple to complex.
Simple Query
{ hello}**Response: **
{ "data": { "hello": "World" } }Arguments
- Fields can take arguments for filtering:
{ user(id: "123") { name }}Variables
- Variables make queries dynamic, keeping them reusable:
query GetUser($id: ID!) { user(id: $id) { name }}Fragments: Reusable Selections
- Fragments avoid duplication by defining reusable sets of fields:
# Define a reusable fragment named UserInfo that specifies a set of fields to fetch from a User typefragment UserInfo on User { name email}# A query to fetch data for a user and their friend using the UserInfo fragment{ # Fetch data for a user with the specified ID (e.g., "123") user(id: "123") { # Apply the UserInfo fragment to include name and email ...UserInfo } # Fetch data for a friend with the specified ID (e.g., "456") friend(id: "456") { # Apply the UserInfo fragment to include name and email ...UserInfo }}- Inline fragments handle abstract types (unions/interfaces) by specifying type-specific fields:
# A query to search for data using a query string, handling results that could be either User or Post types{ # Fetch search results based on the query string "graphql" search(query: "graphql") { # Inline fragment to specify fields for User type results ... on User { name email } # Inline fragment to specify fields for Post type results ... on Post { title content } }}- Inline fragments are critical for unions and interfaces, as GraphQL needs to know which fields to fetch for each possible type.
Mutations
Mutations - Create, Update, Delete (CUD)
Section titled “Mutations - Create, Update, Delete (CUD)”Mutations handle data modification — create, update, delete. They’re like queries but change data and return the updated state for confirmation.
Input vs. Output Types
- Input Types: Bundle mutation arguments into a single object using the
inputkeyword. They’re limited to scalars, enums, or other input types. - Output Types: Regular object types (e.g.,
User) returned to show the result.
# Define the Mutation type, which serves as the entry point for modifying datatype Mutation { # Create a new user with the provided input data, returning the created User object createUser(input: CreateUserInput!): User! # Update an existing user with the specified ID and input data, returning the updated User object updateUser(id: ID!, input: UpdateUserInput!): User! # Delete a user with the specified ID, returning a Boolean indicating success deleteUser(id: ID!): Boolean!}# Define an input type for creating a new user, requiring name and emailinput CreateUserInput { name: String! email: String! age: Int}# Define an input type for updating an existing user, with optional fieldsinput UpdateUserInput { name: String email: String age: Int}# Define the User type to represent a user entity, used as the return type for mutationstype User { id: ID! name: String! email: String age: Int}Create Mutation
mutation { createUser(input: { name: "Bob" email: "bob@example.com" age: 30 }) { id name email }}Response:
{ "data": { "createUser": { "id": "456", "name": "Bob", "email": "bob@example.com" } }}Update Mutation
mutation { updateUser(id: "456", input: { name: "Robert" age: 31 }) { id name age }}Response:
{ "data": { "updateUser": { "id": "456", "name": "Robert", "age": 31 } }}Delete Mutation
mutation { deleteUser(id: "456") { success }}Response: gql{ "data": { "deleteUser": true } }
Subscriptions
Subscriptions: Real-Time Updates
Section titled “Subscriptions: Real-Time Updates”Subscriptions enable real-time data pushes from the server to the client, making them ideal for live updates like chat messages, stock prices, or notifications.
Unlike queries (one-time fetches) or mutations (data changes), subscriptions maintain an open connection, allowing the server to send data whenever an event occurs.
How Subscriptions Work
- Subscriptions are defined in the schema under the
Subscriptiontype. They often take arguments to filter events and return data when triggered.
# Define the Subscription type, which serves as the entry point for real-time data updatestype Subscription { # Subscribe to new messages in a specific channel, returning the added Message messageAdded(channel: String!): Message # Subscribe to online status updates for a specific user, returning the UserStatus userOnline(userId: ID!): UserStatus}# Define the Message type to represent a message entity in real-time updatestype Message { id: ID! text: String! author: User}# Define the UserStatus type to represent a user's online statustype UserStatus { user: User isOnline: Boolean!}Subscription Query:
subscription { messageAdded(channel: "general") { text author { name } }}- The client subscribes to
messageAddedfor a specific channel. - When a new message is added (e.g., via a mutation elsewhere), the server pushes the update to all subscribed clients.
- Response: Multiple pushes over time, like
{ “data”: { “messageAdded”: { “text”: “Hi!”, “author”: { “name”: “Alice” } } } }
subscription { userOnline(userId: "123") { user { name } isOnline }}- This pushes updates whenever the user’s online status changes.
Key Points
- Subscriptions use protocols like WebSockets for persistent connections.
- They’re event-driven: The server decides when to send data based on triggers (e.g., database changes).
- Unsubscribe when done to free resources.
- Great for real-time apps, but not for static data — use queries for that.
