Foundations — Topic Progress
0 / 4 read
The Basics
What is GraphQL? Critical
Definition: GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. It provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more.
Developed by Facebook in 2012 and open-sourced in 2015, GraphQL addresses the limitations of REST APIs by allowing precise data fetching.
  • Ask for what you need: Send a GraphQL query to your API and get exactly what you interpret, nothing more and nothing less.
  • Get many resources in a single request: GraphQL queries access not just the properties of one resource but also smoothly follow references between them.
  • Describe what’s possible with a type system: GraphQL APIs are organized in terms of types and fields, not endpoints.
The Anatomy of a Request
# Client Query
query GetUser {
  user(id: "1") {
    name
    email
    friends {
      name
    }
  }
}

# Server Response (JSON)
{
  "data": {
    "user": {
      "name": "Satendra",
      "email": "sssat@example.com",
      "friends": [
        { "name": "DeepMind" }
      ]
    }
  }
}

Key Takeaways

  • GraphQL is not a database; it is a communication protocol.
  • It works over HTTP, usually on a single /graphql endpoint.
  • Response structure mirrors the request structure.
Why "Graph"? In GraphQL, your data is viewed as a collection of nodes (objects like Users, Posts, Comments) connected by edges (relationships like "User wrote Post"). This guide will show you how to traverse this graph!
GraphQL vs REST Important
Comparison: While REST is architectural-style based on resources (endpoints), GraphQL is a query-based system that uses a single endpoint to fetch graph-like data.
REST Limitations:
  • Over-fetching: Getting more data than you need (e.g., getting full user object just for a name).
  • Under-fetching: One endpoint isn't enough; you need multiple requests (e.g., /user/1 then /user/1/posts).
  • Versioning: REST often requires v1/, v2/ endpoints. GraphQL evolves without versioning by adding new fields.
GraphQL Advantages:
  • No over/under fetching: Client defines the response.
  • Strongly Typed: Schema acts as a contract between frontend and backend.
  • Introspection: Clients can query the server for its schema (enables tools like GraphiQL).
GraphQL is great for complex systems with many relations, but REST might still be simpler for basic CRUD or binary file handling.
Schema & Type System Important
Schema: The core of any GraphQL server implementation. It describes the functionality available to the clients.
Every GraphQL service defines a set of types which completely describe the set of possible data you can query on that service. When queries come in, they are validated and executed against that schema.
Operations Overview Important
  • Query: Read-only fetch.
  • Mutation: Write followed by a fetch.
  • Subscription: Long-lived request that fetches data in response to source events.
Backend Setup — Topic Progress
0 / 5 read
Getting Started
1. Installation & Initialization Critical
Setup: To build a GraphQL server with Node.js and Express, you need to initialize a project and install the Apollo Server core along with Express integration.
Terminal Commands
# Initialize project
mkdir graphql-server && cd graphql-server
npm init -y

# Install dependencies
npm install @apollo/server express graphql cors body-parser

Package Breakdown

  • @apollo/server: The modern Apollo Server (v4+).
  • express: The web framework to host our endpoint.
  • graphql: The core GraphQL implementation for JS.
  • cors: Middleware for cross-origin requests.
2. Creating the Schema (TypeDefs) Important
The Schema is the blueprint of your data. It defines the types and fields available for clients to query.
// schema.js
export const typeDefs = `#graphql
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

Deep Dive: Understanding the Syntax

  • #graphql: This is a comment tag that tells your editor (VS Code, etc.) that the string following it is GraphQL code. This enables Syntax Highlighting and Autocomplete inside a JavaScript file.
  • type Book: This defines a new "Object Type". Think of it like a Class or an Interface in other languages. It describes what a "Book" looks like (it has a title and an author).
  • String: This is a built-in "Scalar" type. Other basics include Int, Float, Boolean, and ID.
  • type Query: This is a Special Type. It is the mandatory entry point for all "read" operations. Every field inside type Query becomes an endpoint that a client can request.
  • books: [Book]: This says the books query will return an Array (List) of Book objects.
Think of the Schema as a "Contract". If it's not in the schema, the client can't ask for it!
3. Writing Resolvers Important
Resolvers: Functions that fulfill the queries defined in the schema. They fetch the actual data from databases, REST APIs, or static files.
// resolvers.js
const books = [
  { title: 'The Awakening', author: 'Kate Chopin' },
  { title: 'City of Glass', author: 'Paul Auster' },
];

export const resolvers = {
  Query: {
    books: () => books,
  },
};

How it works: The Data Bridge

  • Mapping: The structure of the resolvers object must mirror the structure of your typeDefs. Since we have a books field inside type Query, we must have a books function inside Query in our resolvers.
  • The Function: When a client queries books, GraphQL executes this function. It can return a hardcoded array (like our example), or perform a db.find() or an fetch() request.
  • Parent/Args: Although empty here, these functions receive arguments (like id) to filter data.
4. Initializing the Server Critical
Now, we combine everything and start the Express server with Apollo Middleware.
// index.js
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import cors from 'cors';
import { typeDefs } from './schema.js';
import { resolvers } from './resolvers.js';

const app = express();
const httpServer = http.createServer(app);

const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});

await server.start();

app.use(
  '/graphql',
  cors(),
  express.json(),
  expressMiddleware(server),
);

await new Promise((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000/graphql`);
5. Testing with Sandbox Important
By default, Apollo Server includes **Apollo Sandbox**, a powerful UI for testing your queries. Just visit http://localhost:4000/graphql in your browser.
You can browse your schema, see documentation, and use explorer to click-and-build queries without typing.
Core Concepts — Topic Progress
0 / 8 read
The Building Blocks
Object Types & Fields Critical
Object Types: Represent a kind of object you can fetch from your service, and what fields it has.
type Character {
  id: ID!
  name: String!
  appearsIn: [Episode!]!
}

Explanation

  • String, ID: Built-in Scalar types.
  • !: Non-nullable. Server promises to always return a value.
  • [Type]: A list (array) of that type.
Queries & Arguments Important
Every field on a GraphQL object type can have zero or more arguments.
Schema
type Query {
  user(id: ID!): User
}
Resolver
user: (parent, args, context, info) => {
  return users.find(u => u.id === args.id);
}

Resolver Arguments

  • parent: The result of the previous resolver.
  • args: The arguments passed in the query.
  • context: Shared object (Auth, Database connections).
  • info: Execution state and field details.
Mutations (Creating/Updating Data) Critical
Mutations: Used to modify server-side data. While queries are for reads, mutations are for writes, updates, and deletes.
Schema
type Mutation {
  createUser(name: String!, email: String!): User
}
Resolver
Mutation: {
  createUser: (_, { name, email }) => {
    const newUser = { id: Date.now().toString(), name, email };
    users.push(newUser);
    return newUser;
  }
}
Always return the modified object from a mutation so the client can update its cache instantly.
Input Types Important
When you have many arguments for a mutation, use an input type for better organization.
input UserInput {
  name: String!
  email: String!
  age: Int
}

type Mutation {
  createUser(input: UserInput!): User
}
Subscriptions (Real-time) Advanced
Subscriptions: Allow you to push data from the server to the clients that choose to listen to real-time messages from the server.
Usually implemented over WebSockets rather than HTTP. Used for chat apps, stock tickers, or live notifications.
type Subscription {
  postAdded: Post
}
Variables Important
Avoid hardcoding values in queries. Use variables for dynamic values.
query GetUser($id: ID!) {
  user(id: $id) {
    name
  }
}
Fragments Important
Fragments let you build sets of fields, and then include them in queries where you need them.
fragment UserDetails on User {
  id
  name
  email
}

query GetUser {
  user(id: "1") {
    ...UserDetails
  }
}
Directives (@include/@skip) Advanced
Directives provide a way to dynamically change the structure and shape of our queries using variables.
query GetUser($showEmail: Boolean!) {
  user(id: "1") {
    name
    email @include(if: $showEmail)
  }
}
Advanced Schema — Topic Progress
0 / 5 read
Polymorphism & Extensions
Interfaces Important
Interfaces: An abstract type that includes a certain set of fields that a type must include to implement the interface.
interface Character {
  id: ID!
  name: String!
}

type Human implements Character {
  id: ID!
  name: String!
  totalCredits: Int
}

type Droid implements Character {
  id: ID!
  name: String!
  primaryFunction: String
}
Unions Important
Unions represent an object that could be one of several types, but those types don't necessarily share any fields.
union SearchResult = Human | Droid | Starship

query Search($text: String!) {
  search(text: $text) {
    ... on Human { name }
    ... on Starship { name, length }
  }
}
Custom Scalars Advanced
By default, GraphQL includes Int, Float, String, Boolean, and ID. You can define your own, like Date.
scalar Date
You'll need a library like graphql-scalars to handle the parsing and serialization logic for common custom types.
Schema Stitching vs Federation Advanced
When scaling to multiple teams, you might split your schema into microservices.
  • Stitching: An older method where a gateway manually merges schemas.
  • Federation: The modern Apollo standard where subgraphs define their own entities and the gateway handles execution.
Apollo Link (Middleware) Advanced
Apollo Link is a system of modular components that you can chain together to customize the flow of data between your client and server.
const authLink = setContext((_, { headers }) => {
  return {
    headers: { ...headers, authorization: token }
  }
});
Frontend (Next.js) — Topic Progress
0 / 5 read
Apollo Client Integration
1. Setup Apollo Client Critical
npm install @apollo/client graphql
// lib/apollo-client.js
import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
  cache: new InMemoryCache(),
});

export default client;
2. ApolloProvider in Next.js Critical
In Next.js App Router, you wrap your client-side components with the provider.
// app/providers.js
'use client';
import { ApolloProvider } from '@apollo/client';
import client from '@/lib/apollo-client';

export function Providers({ children }) {
  return {children};
}
3. The useQuery Hook Important
import { useQuery, gql } from '@apollo/client';

const GET_BOOKS = gql`
  query GetBooks {
    books {
      title
      author
    }
  }
`;

function BookList() {
  const { loading, error, data } = useQuery(GET_BOOKS);

  if (loading) return 

Loading...

; if (error) return

Error: {error.message}

; return data.books.map(({ title, author }) => (

{title}

{author}

)); }
4. The useMutation Hook Important
const ADD_BOOK = gql`
  mutation AddBook($title: String!, $author: String!) {
    addBook(title: $title, author: $author) {
      title
      author
    }
  }
`;

function AddBookForm() {
  let inputTitle, inputAuthor;
  const [addBook, { data, loading, error }] = useMutation(ADD_BOOK);

  return (
    
{ e.preventDefault(); addBook({ variables: { title: inputTitle.value, author: inputAuthor.value } }); }}> inputTitle = node} /> inputAuthor = node} />
); }
5. Client-side Fragments Important
Use fragments in your frontend components to ensure they only request the data they actually need.
BookList.fragments = {
  book: gql`
    fragment BookDetails on Book {
      title
      author
    }
  `,
};
Production Ready — Topic Progress
0 / 4 read
Best Practices
1. Authentication & Context Critical
Context: A shared object that is passed to every resolver. It's the perfect place to put authentication info.
// In server setup
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

app.use(
  '/graphql',
  expressMiddleware(server, {
    context: async ({ req }) => ({
      user: await getUserFromToken(req.headers.authorization),
    }),
  }),
);
2. N+1 Problem & Dataloader Advanced
The N+1 problem occurs when you fetch a list of items (1 query) and then fetch a related item for each of those items (N queries).
Dataloader is a utility that batches and caches requests to a back-end, reducing the number of database hits.
const authorLoader = new DataLoader(async (ids) => {
  const authors = await getAuthorsByIds(ids);
  return ids.map(id => authors.find(a => a.id === id));
});
3. Error Handling Important
Apollo Server provides built-in error types like GraphQLError.
import { GraphQLError } from 'graphql';

if (!user) {
  throw new GraphQLError('Not authenticated', {
    extensions: { code: 'UNAUTHENTICATED' },
  });
}
4. Security: Depth & Complexity Critical
Malicious queries can try to crash your server by nesting calls infinitely.
  • Depth Limiting: Limit how deep a query can go.
  • Complexity Analysis: Assign costs to fields and reject queries above a certain threshold.
Use libraries like graphql-depth-limit to easily protect your server.