The site is under development.

GraphQl Tutorial

What is GraphQL?
GraphQL is a query language for APIs and a runtime for executing those queries with your existing data. Unlike traditional REST APIs that expose multiple endpoints for different resources, GraphQL lets clients specify exactly what data they need in a single request. It enables more efficient, powerful, and flexible client-server communication.

Example:
// A GraphQL query to get a user's name and email
{
  user(id: "1") {
    name
    email
  }
}
      

REST vs GraphQL
REST (Representational State Transfer) uses fixed endpoints for each resource, which can lead to over-fetching or under-fetching of data. GraphQL, on the other hand, allows clients to query only the data they need. While REST might require multiple network calls to fetch related data, GraphQL consolidates this into a single query.

Example:
// REST example (multiple endpoints)
GET /users/1
GET /users/1/posts

// GraphQL equivalent
{
  user(id: "1") {
    name
    posts {
      title
    }
  }
}
      

History and Evolution
GraphQL was developed internally by Facebook in 2012 and publicly released in 2015. It was designed to address the shortcomings of REST in mobile and web environments. Since then, it has been adopted by major companies like GitHub, Shopify, and Twitter, and has grown into a robust ecosystem with tools, libraries, and a strong community.

Example:
// First public version released in 2015
// Adopted by GitHub in 2016 with GitHub's GraphQL API v4
// Now widely supported by clients and servers in JavaScript, Python, Java, and more.
      

Benefits of using GraphQL
- **Efficient Data Fetching**: Only retrieve what you ask for.
- **Single Endpoint**: Unlike REST, it uses a single endpoint.
- **Strong Typing**: GraphQL APIs are defined by a schema that specifies types and relationships.
- **Tooling & Introspection**: GraphQL supports automatic API documentation and tools like GraphiQL for testing.

Example:
// Schema definition
type User {
  id: ID!
  name: String!
  email: String!
}

// Query
{
  user(id: "1") {
    name
    email
  }
}
      

Use cases in modern applications
GraphQL is commonly used in applications that require dynamic or complex data fetching, such as: - **Mobile apps**: Reduce bandwidth by fetching only required data. - **Single-page apps (SPAs)**: Load data efficiently for various UI components. - **Microservices**: Aggregate data from multiple services into a unified schema. - **Real-time data**: Combine with subscriptions for live updates.

Example:
// A React component using Apollo Client to fetch GraphQL data
import { useQuery, gql } from '@apollo/client';

const GET_USER = gql`
  query {
    user(id: "1") {
      name
      email
    }
  }
`;

function UserProfile() {
  const { loading, error, data } = useQuery(GET_USER);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :( </p>;

  return (
    <div>
      <h1>{data.user.name}</h1>
      <p>{data.user.email}</p>
    </div>
  );
}
      

Schema Definition Language (SDL)
SDL is used to define a GraphQL schema in a human-readable format. It describes types, queries, mutations, and how they are related.

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

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

Types: Query, Mutation, Subscription
- **Query**: Used to read data.
- **Mutation**: Used to write or modify data.
- **Subscription**: Used for real-time updates via WebSockets.

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

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

type Subscription {
  userCreated: User
}
      

Scalars: Int, Float, String, Boolean, ID
Scalars represent the leaves of a query. They're the basic data types.

type Product {
  id: ID!
  name: String!
  price: Float!
  available: Boolean!
}
      

Object Types and Fields
Object types group related fields. Each field can return scalar, object, or list types.

type Book {
  title: String!
  author: Author
}

type Author {
  name: String!
}
      

Non-nullable and List Types
- `String!` means the value cannot be null.
- `[String]` is a list of strings that may contain nulls.
- `[String!]!` is a non-null list of non-null strings.

type Course {
  title: String!
  tags: [String!]!
}
      

Nested Queries
You can query deeply related data in one go.

{
  book(id: "1") {
    title
    author {
      name
      age
    }
  }
}
      

Choosing a Backend (Node.js, Python, etc.)
GraphQL servers can be built in any backend language. The most popular choices include:
- **Node.js**: Fast and widely supported.
- **Python**: Popular in scientific and AI fields.
- **Java/Go/.NET**: Used in enterprise-level projects.

// Example libraries:
Node.js -> Apollo Server
Python -> Graphene
Java -> graphql-java
      

Using Apollo Server (Node.js)
Apollo Server is a popular, fully-featured GraphQL server for Node.js.

npm install apollo-server graphql
      

Basic GraphQL Server Setup
Here's a minimal example of an Apollo GraphQL server.

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

// Schema
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

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

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

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

GraphQL Playground / GraphiQL
- **GraphQL Playground**: GUI to test queries, available with Apollo.
- **GraphiQL**: Lightweight browser IDE embedded into many setups.
They support autocomplete, docs, and query building.

// Access it at http://localhost:4000/ after running Apollo Server
// You can run queries like:
{
  hello
}
      

First Schema and Resolver
A schema defines types and available operations. Resolvers return actual data.

const typeDefs = gql`
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

const books = [
  { title: '1984', author: 'George Orwell' },
  { title: 'Brave New World', author: 'Aldous Huxley' },
];

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

What is GraphQL in simple words?
GraphQL is a way to ask for data from a website or app, like saying: "Give me just the info I want." Instead of getting a full menu when you only want a drink, GraphQL lets you order *exactly* what you need and nothing more.

// A GraphQL request that says:
// "Give me just the name of the user with ID 1"
{
  user(id: "1") {
    name
  }
}
      

Why should beginners care?
GraphQL is simple, smart, and clean. It saves time, internet data, and makes it easy to work with just the pieces of information you need — perfect for mobile apps, websites, and beginners learning how apps talk to each other.

// Imagine you have a big book of users.
// You don’t want the whole book — just the title page.
// GraphQL lets you grab just the title page!
{
  user(id: "1") {
    name
  }
}
      

What's the difference between REST and GraphQL for beginners?
- **REST** is like visiting different stores for milk, eggs, and bread (multiple calls).
- **GraphQL** is like going to one big store and picking everything you want in one trip (single call).

// REST (multiple requests)
GET /user/1
GET /user/1/posts

// GraphQL (one request)
{
  user(id: "1") {
    name
    posts {
      title
    }
  }
}
      

How do I try GraphQL without coding?
Use a tool like: - **Apollo Sandbox** (online) - **GraphQL Playground** (installed locally) - **GraphiQL** (built into many GraphQL sites) You type in your request on the left, press "Run", and see the result on the right.

// Type this into GraphQL Playground:
{
  hello
}

// And get this back:
{
  "data": {
    "hello": "Hello world!"
  }
}
      

A full beginner example
Let’s build a simple “hello world” GraphQL server in Node.js that just replies with a message.

// Step 1: Install the tools
npm install apollo-server graphql

// Step 2: Create index.js
const { ApolloServer, gql } = require('apollo-server');

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = {
  Query: {
    hello: () => "Welcome to GraphQL!",
  },
};

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

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

What are Mutations?
In GraphQL, **mutations** are used when you want to *change* or *modify* data — like creating a new user, updating a post, or deleting a comment. While **queries** are for reading data, **mutations** are for writing data.

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

Creating Data with Mutations
You can use a mutation to insert (create) a new record into your database, like adding a new user or product.

// Mutation to create a new user
mutation {
  addUser(name: "Alice", email: "alice@example.com") {
    id
    name
    email
  }
}
      

Updating and Deleting Data
You can also update or delete data using mutations. These usually require an ID to identify the item.

// Update user info
mutation {
  updateUser(id: "1", name: "Alice Smith") {
    id
    name
  }
}

// Delete a user
mutation {
  deleteUser(id: "1") {
    success
    message
  }
}
      

Input Types
GraphQL allows you to send multiple fields using an **input object**, which is a cleaner way to pass arguments.

// Define an input type in the schema
input NewUserInput {
  name: String!
  email: String!
}

type Mutation {
  addUser(input: NewUserInput): User
}

// Using the input in a mutation
mutation {
  addUser(input: {
    name: "Bob"
    email: "bob@example.com"
  }) {
    id
    name
  }
}
      

Mutation Variables
Variables let you reuse mutations with different values — great for clean code and reusability.

// Mutation with variables
mutation AddUser($name: String!, $email: String!) {
  addUser(name: $name, email: $email) {
    id
    name
  }
}

// Variables
{
  "name": "Charlie",
  "email": "charlie@example.com"
}
      

Return Values and Error Handling
Mutations can return useful information — the object that was created, a message, or success status. You can also handle errors gracefully.

type MutationResponse {
  success: Boolean!
  message: String!
  user: User
}

type Mutation {
  addUser(name: String!, email: String!): MutationResponse
}

// Example response:
{
  "data": {
    "addUser": {
      "success": true,
      "message": "User created successfully",
      "user": {
        "id": "5",
        "name": "Charlie"
      }
    }
  }
}
      

What are subscriptions?
Subscriptions let your app receive data in *real time*. When something changes on the server (like a new message), it’s pushed to the client without refreshing.

type Subscription {
  newMessage: Message
}
      

Setting up WebSocket support
Subscriptions require a WebSocket connection (not regular HTTP). Apollo Server supports this using `graphql-ws` or `subscriptions-transport-ws`.

// Install WebSocket support
npm install graphql-ws
      

Subscribing to events
Use `PubSub` in Apollo Server to publish and listen to events.

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

const resolvers = {
  Subscription: {
    newMessage: {
      subscribe: () => pubsub.asyncIterator(['NEW_MESSAGE']),
    },
  },
};

// Somewhere in your mutation:
pubsub.publish('NEW_MESSAGE', { newMessage: { text: "Hi!" } });
      

Using Apollo and @graphql/subscriptions
Combine Apollo Server with the GraphQL WebSocket setup.

// Define typeDefs
type Subscription {
  messagePosted: String
}

// Publish an event
pubsub.publish('messagePosted', { messagePosted: "New message!" });
      

Real-time updates in the frontend
Apollo Client supports subscriptions with WebSocket links.

import { gql, useSubscription } from '@apollo/client';

const NEW_MESSAGES = gql`
  subscription {
    newMessage {
      text
    }
  }
`;

function ChatBox() {
  const { data } = useSubscription(NEW_MESSAGES);
  return <div>{data?.newMessage?.text}</div>;
}
      

What are Resolvers?
Resolvers are functions that tell your GraphQL server **how** to fetch the data for each field in your schema. When a query or mutation runs, the corresponding resolver is called to return the value.

// Schema
type Query {
  hello: String
}

// Resolver
const resolvers = {
  Query: {
    hello: () => "Hello from the server!"
  }
};
      

Resolver Map Structure
Resolvers are written as a **map** that mirrors the schema. Each type (like `Query`, `Mutation`, or `User`) has its own section.

const resolvers = {
  Query: {
    getUser: () => { /* return user */ }
  },
  Mutation: {
    createUser: () => { /* add user */ }
  },
  User: {
    fullName: (parent) => `${parent.firstName} ${parent.lastName}`
  }
};
      

Parent (Root) Object
Each resolver receives a **parent** object (sometimes called root or source) which is the result from the parent field. It helps resolve nested fields.

type User {
  id: ID!
  firstName: String!
  lastName: String!
  fullName: String!
}

// Resolver
User: {
  fullName: (parent) => {
    return `${parent.firstName} ${parent.lastName}`;
  }
}
      

Field-level Resolvers
Every field can have its own resolver. This is useful for computed fields or when fields come from different sources.

type Product {
  id: ID!
  price: Float!
  tax: Float!
  totalPrice: Float!
}

// Resolver
Product: {
  totalPrice: (parent) => {
    return parent.price + parent.tax;
  }
}
      

Async Resolvers and Promises
Resolvers can return promises. This allows you to perform async actions like database calls or API requests.

Query: {
  async getUser(_, args) {
    const user = await db.findUserById(args.id);
    return user;
  }
}
      

Resolver Best Practices
Keep resolvers small and focused. Avoid logic inside schema definitions. Use services or helper functions to separate business logic. Always handle async errors gracefully. Don't forget to validate user input in mutations.

// Example: clean structure
Query: {
  user: (_, { id }, { dataSources }) => {
    return dataSources.userAPI.getUserById(id);
  }
}
      

Using arguments in queries/mutations
Arguments let you send specific data when calling a query or mutation. For example, you can get a user by ID or filter a list.

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

// Query example
{
  user(id: "1") {
    name
  }
}
      

Defining argument types
Argument types must be declared in your schema with a type like `String`, `Int`, or `ID`.

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

Required and optional arguments
- `!` means required (must provide)
- Without `!` means optional

type Query {
  searchBooks(keyword: String!): [Book]
}
      

Query variables with clients
Query variables allow dynamic values to be passed instead of hardcoding them. Useful in apps like React or mobile.

// Query with variable
query GetUser($id: ID!) {
  user(id: $id) {
    name
  }
}

// Variables
{
  "id": "5"
}
      

What are fragments?
Fragments let you reuse parts of queries. They're helpful when you need the same fields in multiple places (like user details in many queries).

fragment userFields on User {
  name
  email
}
      

Reusing selection sets
You can "insert" the fragment wherever you’d repeat the same fields.

{
  user(id: "1") {
    ...userFields
  }

  admin(id: "2") {
    ...userFields
  }
}

fragment userFields on User {
  name
  email
}
      

Named fragments
A named fragment is reusable with a name like `userFields`.

fragment userFields on User {
  name
  email
}
      

Inline fragments for unions/interfaces
Inline fragments are used when working with interfaces or union types to choose specific fields depending on the actual type.

{
  search(id: "1") {
    ... on User {
      name
    }
    ... on Admin {
      role
    }
  }
}
      

Type conditions
Type conditions (`... on TypeName`) help GraphQL understand which type's fields to return when one field could return multiple types.

{
  result {
    ... on Book {
      title
    }
    ... on Author {
      name
    }
  }
}
      

Built-in Directives: @include, @skip, @deprecated
Directives are special instructions that change how queries behave. - `@include(if: true)` includes a field if the condition is true. - `@skip(if: true)` skips a field if the condition is true. - `@deprecated(reason: "...")` marks a field or type as outdated.

query GetUser($showEmail: Boolean!) {
  user(id: "1") {
    name
    email @include(if: $showEmail)
  }
}

// In schema:
type User {
  name: String!
  oldField: String @deprecated(reason: "Use 'newField' instead.")
}
      

Creating Custom Directives
You can define your own directive in the schema and use it with custom logic on the server.

// In SDL
directive @log on FIELD_DEFINITION

// Example use
type Query {
  users: [User] @log
}
      

Using Directives in Clients
In clients like Apollo or Relay, you can dynamically include or skip fields using variables and directives.

// Query with @skip
query GetUser($hideEmail: Boolean!) {
  user(id: "2") {
    name
    email @skip(if: $hideEmail)
  }
}

// Variables
{
  "hideEmail": true
}
      

Creating Input Types
Input types allow grouping multiple arguments into a single input object for mutations.

input CreateUserInput {
  name: String!
  email: String!
}

type Mutation {
  createUser(input: CreateUserInput): User
}
      

Defining Reusable Custom Types
You can define complex objects as types and reuse them in queries, mutations, or other types.

type Address {
  city: String!
  country: String!
}

type User {
  name: String!
  address: Address
}
      

Using Enums in Schema
Enums restrict values to a fixed set of options. Great for things like status or categories.

enum Role {
  ADMIN
  USER
  GUEST
}

type User {
  name: String!
  role: Role!
}
      

Union and Interface Types
- **Unions** allow a field to return one of several object types.
- **Interfaces** define common fields across types.

// Union example
union SearchResult = Book | Author

type Book {
  title: String!
}

type Author {
  name: String!
}

type Query {
  search(keyword: String!): [SearchResult]
}

// Interface example
interface Animal {
  name: String!
}

type Dog implements Animal {
  name: String!
  breed: String!
}

type Cat implements Animal {
  name: String!
  color: String!
}
      

Designing scalable schemas
Good schema design keeps your API fast and easy to maintain. Think in terms of real-world objects and relationships. Plan for how types grow over time.

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

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

Modularity and reusability
Break your schema into small, manageable parts (modules). This keeps things organized as the API grows.

// userSchema.js
const typeDefs = gql`
  type User {
    id: ID!
    name: String!
  }

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

Schema stitching
Stitching lets you combine multiple GraphQL APIs into a single schema — useful in microservices.

// Using tools like @graphql-tools/stitch
const stitchedSchema = stitchSchemas({
  subschemas: [userSchema, postSchema],
});
      

Schema-first vs code-first approach
- **Schema-first**: Write the schema using SDL first, then add resolvers.
- **Code-first**: Use code (e.g., classes) to define the schema programmatically.

// Schema-first
typeDefs = gql`
  type User {
    id: ID!
    name: String!
  }
`

// Code-first (e.g. with Nexus or TypeGraphQL)
const User = objectType({
  name: 'User',
  definition(t) {
    t.id('id');
    t.string('name');
  }
});
      

Documentation with descriptions
Add triple quotes (`"""`) in your schema to describe types and fields. This helps auto-document tools like GraphiQL.

"""A user who writes blog posts"""
type User {
  """Unique ID for the user"""
  id: ID!

  """User's full name"""
  name: String!
}
      

Standard GraphQL error format
GraphQL always returns a well-defined error format in the response JSON. Errors are listed under the `errors` field.

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

Handling resolver exceptions
Use `try...catch` in your resolvers to manage exceptions gracefully and avoid crashing your server.

const resolvers = {
  Query: {
    user: async (_, { id }) => {
      try {
        const user = await getUser(id);
        if (!user) throw new Error("User not found");
        return user;
      } catch (error) {
        throw new Error(error.message);
      }
    }
  }
};
      

Custom error messages
Instead of vague errors, you can return custom messages or structured error objects for better client handling.

throw new GraphQLError("Invalid email format", {
  extensions: {
    code: "BAD_USER_INPUT",
    field: "email"
  }
});
      

Logging and error monitoring
Use logging tools to track GraphQL errors. Combine with services like Sentry, Datadog, or LogRocket.

// Basic logging
console.error(error);

// With Sentry
Sentry.captureException(error);
      

Securing endpoints
GraphQL endpoints should be protected from unauthorized access. Use HTTPS and validate tokens before processing requests.

// Middleware to protect server
app.use((req, res, next) => {
  const token = req.headers.authorization;
  if (!token || token !== "valid_token") {
    return res.status(401).send("Unauthorized");
  }
  next();
});
      

Role-based access with custom logic
You can control access using roles like "admin", "user", or "guest".

const resolvers = {
  Query: {
    privateData: (parent, args, context) => {
      if (context.user.role !== "admin") {
        throw new Error("Access denied");
      }
      return "Top Secret Info";
    }
  }
};
      

Token-based authentication (JWT)
Use JWTs (JSON Web Tokens) to validate users. Clients send the token, and the server verifies it.

import jwt from 'jsonwebtoken';

const context = ({ req }) => {
  const token = req.headers.authorization || "";
  const user = jwt.verify(token, "my_secret_key");
  return { user };
};
      

Context object in resolvers
The context object is shared across all resolvers in a request and usually contains user data.

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    const token = req.headers.authorization;
    const user = getUserFromToken(token);
    return { user };
  }
});
      

Guarding fields and types
You can protect specific fields or types based on roles or user identity.

type Query {
  profile: User @auth(requires: USER)
  adminPanel: AdminData @auth(requires: ADMIN)
}
      

Integrating SQL (TypeORM, Prisma)
GraphQL can connect to SQL databases using ORMs like: - **TypeORM** (for TypeScript and Node.js) - **Prisma** (modern and type-safe ORM) These tools let you define models and use them in resolvers.

// Prisma user model (schema.prisma)
model User {
  id    Int     @id @default(autoincrement())
  name  String
  email String  @unique
}

// Resolver using Prisma
const resolvers = {
  Query: {
    users: async (_, __, { prisma }) => await prisma.user.findMany()
  }
};
      

Integrating NoSQL (MongoDB, Firebase)
GraphQL can also work with NoSQL databases like: - **MongoDB**: popular document store - **Firebase Firestore**: realtime NoSQL database

// MongoDB example with Mongoose
const User = mongoose.model("User", userSchema);

Query: {
  users: () => User.find()
}
      

Using ORMs with Resolvers
ORMs help simplify database operations. You call the ORM methods directly in resolvers to read or write data.

// Using Prisma in mutation resolver
Mutation: {
  createUser: (_, { name, email }, { prisma }) => {
    return prisma.user.create({
      data: { name, email }
    });
  }
}
      

Filtering and Pagination
You can add arguments to filter data (e.g. by name or age), and apply pagination using limit/offset or cursors.

// Schema
type Query {
  users(name: String, skip: Int, take: Int): [User]
}

// Resolver with filtering
Query: {
  users: (_, { name, skip = 0, take = 10 }, { prisma }) => {
    return prisma.user.findMany({
      where: { name: { contains: name } },
      skip,
      take
    });
  }
}
      

Offset-based Pagination
Offset pagination uses `skip` and `limit` to get a specific range of data.

// Schema
type Query {
  posts(skip: Int, limit: Int): [Post]
}

// Resolver
Query: {
  posts: (_, { skip = 0, limit = 10 }, { db }) => {
    return db.post.find().skip(skip).limit(limit);
  }
}
      

Cursor-based Pagination
Cursor-based pagination uses an ID or timestamp as a pointer to the next set of results. It's more efficient for large or changing datasets.

// Schema
type Query {
  postsAfter(cursor: ID, limit: Int): [Post]
}

// Resolver
Query: {
  postsAfter: async (_, { cursor, limit = 5 }, { prisma }) => {
    return prisma.post.findMany({
      take: limit,
      skip: cursor ? 1 : 0,
      cursor: cursor ? { id: cursor } : undefined
    });
  }
}
      

Filtering with Query Arguments
You can pass arguments to filter data, such as filtering by status, category, or text.

// Schema
type Query {
  products(category: String, inStock: Boolean): [Product]
}

// Resolver
Query: {
  products: (_, { category, inStock }, { db }) => {
    return db.product.find({ category, inStock });
  }
}
      

Sorting Results
Sorting lets users view data in a specific order (e.g., newest first or A–Z).

// Schema
type Query {
  posts(orderBy: String): [Post]
}

// Resolver (example using Prisma)
Query: {
  posts: (_, { orderBy = "createdAt_DESC" }, { prisma }) => {
    return prisma.post.findMany({
      orderBy: {
        createdAt: orderBy === "createdAt_DESC" ? "desc" : "asc"
      }
    });
  }
}
      

GraphQL Code Generator
Automatically generates TypeScript types and query/mutation hooks from your GraphQL schema and queries. Saves time and prevents type errors.

// Install
npm install @graphql-codegen/cli --save-dev

// Example config file (codegen.yml)
schema: http://localhost:4000/graphql
documents: src/**/*.graphql
generates:
  src/generated/graphql.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
      

Apollo Federation (microservices)
Break a large GraphQL API into smaller services. Apollo Federation lets you stitch them together into a single graph.

// Example: extend types across services
// In Users service
extend type User @key(fields: "id") {
  id: ID! @external
  posts: [Post]
}
      

GraphQL Mesh
Mesh lets you unify REST, SOAP, gRPC, and GraphQL APIs into one GraphQL schema — even without writing resolvers!

// Example config
sources:
  - name: RESTCountries
    handler:
      openapi:
        source: https://restcountries.com/v3/openapi.json
      

GraphQL Yoga
A full-featured, batteries-included GraphQL server that’s simple and fast. Great for beginners.

// Install
npm install graphql-yoga

// Quick setup
import { createServer } from 'graphql-yoga';

const server = createServer({
  schema: {
    typeDefs: `type Query { hello: String }`,
    resolvers: { Query: { hello: () => 'Hi Yoga!' } },
  },
});

server.start();
      

Altair, GraphiQL, Postman
- **Altair**: Feature-rich GraphQL client with headers, variables, history.
- **GraphiQL**: Simple web-based query tool.
- **Postman**: Now supports GraphQL with variables and introspection.

// Use Altair at: https://altair.sirmuel.design/
// Use GraphiQL in-browser if Apollo Server includes it
      

Query complexity analysis
Helps prevent expensive, slow queries by assigning "weights" to fields. Blocks overly complex queries.

import { createComplexityLimitRule } from 'graphql-validation-complexity';

const server = new ApolloServer({
  validationRules: [createComplexityLimitRule(1000)],
});
      

Limiting nested queries
Too many nested fields = slow response. Set depth limits.

const depthLimit = require('graphql-depth-limit');

const server = new ApolloServer({
  validationRules: [depthLimit(5)],
});
      

Caching with DataLoader
DataLoader batches and caches database calls per request, avoiding repeated DB lookups (great for `N+1` problems).

const DataLoader = require('dataloader');
const userLoader = new DataLoader(async (ids) => {
  return getUsersByIds(ids); // Batch DB call
});
      

Batching and avoiding N+1 problems
Without batching, GraphQL may run one query per item. DataLoader fixes this by batching multiple loads into one.

// N+1 example:
posts: () => posts.map(post => getUser(post.userId)) // BAD

// Better with DataLoader:
posts: () => posts.map(post => userLoader.load(post.userId)) // GOOD
      

Apollo caching techniques
Apollo Client offers caching (in-memory, normalized) to reduce refetching. Cache policies can be fine-tuned for performance.

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

Unit testing resolvers
Test each resolver function independently to verify it returns correct data or handles errors properly.

// Jest example: test a resolver
const resolvers = {
  Query: {
    hello: () => 'Hello world',
  },
};

test('hello resolver returns correct string', () => {
  expect(resolvers.Query.hello()).toBe('Hello world');
});
      

Integration tests with Jest/Supertest
Test the full API by sending GraphQL queries/mutations over HTTP to your server.

import request from 'supertest';
import { server } from '../index'; // your GraphQL server

test('integration test for hello query', async () => {
  const response = await request(server)
    .post('/graphql')
    .send({ query: '{ hello }' });
  expect(response.body.data.hello).toBe('Hello world');
});
      

Mocking schema and resolvers
Use mocks to simulate responses without needing a full backend, speeding up tests.

import { makeExecutableSchema } from '@graphql-tools/schema';
import { addMocksToSchema } from '@graphql-tools/mock';

const schema = makeExecutableSchema({ typeDefs });
const mockedSchema = addMocksToSchema({ schema });

test('mocked hello', async () => {
  // Test mocked schema here
});
      

GraphQL test clients
Tools like Apollo Client’s testing utilities or graphql-request help test queries easily.

import { createTestClient } from 'apollo-server-testing';
const { query } = createTestClient(server);

test('test hello query', async () => {
  const res = await query({ query: gql`{ hello }` });
  expect(res.data.hello).toBe('Hello world');
});
      

What is Apollo Federation?
Apollo Federation is a way to build a single GraphQL API by composing multiple smaller GraphQL services called subgraphs.

// Instead of one big schema, break it into smaller services
// Each subgraph handles a part of your data
      

Gateway and subgraph services
The **gateway** acts as a single entry point that combines subgraphs’ schemas and resolves queries across them.

// Gateway setup example
const { ApolloGateway } = require('@apollo/gateway');
const { ApolloServer } = require('apollo-server');

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'users', url: 'http://localhost:4001/graphql' },
    { name: 'products', url: 'http://localhost:4002/graphql' },
  ],
});

const server = new ApolloServer({ gateway, subscriptions: false });

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

@key, @extends directives
These directives help link types between subgraphs by identifying and extending types.

# In users service
type User @key(fields: "id") {
  id: ID!
  name: String
}

# In products service extending User
extend type User @key(fields: "id") {
  id: ID! @external
  reviews: [Review]
}
      

Service discovery
The gateway automatically discovers and updates subgraph services, allowing dynamic schema updates.

// Managed federation lets Apollo Studio handle this dynamically
      

Merging schemas dynamically
Federation merges the subgraph schemas into a single unified schema, letting clients query seamlessly across services.

// Clients query the gateway as if it’s one API
{
  user(id: "1") {
    name
    reviews {
      body
    }
  }
}
      

GraphQL as API Gateway
GraphQL can serve as a single entry point (API Gateway) for multiple microservices, aggregating their data and simplifying frontend queries.

// GraphQL server acts as a gateway,
// forwarding queries to different microservices
query {
  user(id: "1") {
    name
    orders {
      id
      total
    }
  }
}
      

Combining REST with GraphQL
GraphQL servers often wrap existing REST APIs, combining their data under one schema for easier frontend use.

const fetchUser = () => fetch('/api/user').then(res => res.json());
const fetchOrders = () => fetch('/api/orders').then(res => res.json());

const resolvers = {
  Query: {
    user: () => fetchUser(),
    orders: () => fetchOrders(),
  }
};
      

Distributed schema design
Each microservice owns its schema piece. These can be merged or stitched together to form a unified API.

// Using schema stitching or Apollo Federation
const gateway = new ApolloGateway({
  serviceList: [
    { name: 'users', url: 'http://users-service/graphql' },
    { name: 'orders', url: 'http://orders-service/graphql' },
  ],
});
      

GraphQL Mesh for legacy APIs
GraphQL Mesh creates a unified GraphQL API over existing REST, SOAP, or gRPC services without rewriting them.

// mesh.config.yaml example
sources:
  - name: LegacyREST
    handler:
      openapi:
        source: https://legacyapi.example.com/openapi.json
      

React: Apollo Client, useQuery, useMutation
Apollo Client integrates GraphQL with React using hooks like `useQuery` and `useMutation` for fetching and modifying data.

import { useQuery, useMutation, gql } from '@apollo/client';

const GET_USERS = gql`
  query {
    users {
      id
      name
    }
  }
`;

function UsersList() {
  const { loading, error, data } = useQuery(GET_USERS);
  if (loading) return 'Loading...';
  if (error) return `Error! ${error.message}`;

  return data.users.map(user => <div key={user.id}>{user.name}</div>);
}
      

Vue: Apollo and Vue Apollo
Vue Apollo provides Apollo integration for Vue.js, allowing queries and mutations inside Vue components.

import { useQuery, gql } from '@vue/apollo-composable';

const GET_TODOS = gql`
  query {
    todos {
      id
      text
    }
  }
`;

export default {
  setup() {
    const { result, loading, error } = useQuery(GET_TODOS);
    return { result, loading, error };
  }
};
      

Angular: Apollo Angular
Apollo Angular integrates GraphQL with Angular using services and components.

// In Angular service
@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private apollo: Apollo) {}

  getUsers() {
    return this.apollo.watchQuery({
      query: gql`
        query {
          users {
            id
            name
          }
        }
      `
    }).valueChanges;
  }
}
      

Svelte & Next.js support
- **Svelte** uses community Apollo clients for GraphQL queries.
- **Next.js** supports Apollo and other GraphQL clients with SSR (server-side rendering) for SEO and performance.

// Example: Next.js page with Apollo
import { gql, useQuery } from '@apollo/client';

const GET_POSTS = gql`
  query {
    posts {
      id
      title
    }
  }
`;

export default function Posts() {
  const { data, loading } = useQuery(GET_POSTS);
  if (loading) return <p>Loading...</p>;
  return <ul>{data.posts.map(post => <li key={post.id}>{post.title}</li>)}</ul>;
}
      

Installing @nestjs/graphql
NestJS offers a dedicated GraphQL package to integrate GraphQL easily into your backend. Install it with:
npm install @nestjs/graphql graphql-tools graphql apollo-server-express

Code-first vs schema-first in NestJS
- **Code-first**: Define your GraphQL schema using TypeScript classes and decorators, NestJS generates schema automatically.
- **Schema-first**: Write the GraphQL schema using SDL (Schema Definition Language) and implement resolvers separately.

// Code-first example:
@ObjectType()
class User {
  @Field()
  name: string;

  @Field()
  email: string;
}
      

Using resolvers and decorators
Resolvers are classes or methods that handle queries and mutations. Decorators like `@Query()`, `@Mutation()`, and `@Resolver()` annotate these methods for NestJS GraphQL.

@Resolver(of => User)
export class UserResolver {
  @Query(returns => User)
  getUser() {
    return { name: 'Alice', email: 'alice@example.com' };
  }
}
      

GraphQL modules and guards
NestJS organizes code into modules. You import `GraphQLModule` into your app module to set up GraphQL.
Guards can protect GraphQL endpoints for authentication and authorization.

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true,
    }),
  ],
})
export class AppModule {}
      

Deploying Apollo Server (Heroku, Vercel, AWS)
Apollo Server apps can be deployed on cloud providers:
- **Heroku**: Easy PaaS, supports Node.js directly.
- **Vercel**: Focus on serverless functions.
- **AWS**: More control with EC2, Lambda, or Elastic Beanstalk.

// Example: Deploying a Node.js Apollo Server to Heroku
// 1. Create a Git repo with your server code
// 2. heroku create
// 3. git push heroku main
// 4. heroku open
      

Schema introspection security
Schema introspection lets clients discover the schema, useful for dev tools but a potential security risk if exposed in production.
You can disable introspection in Apollo Server to prevent schema leak.

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: false, // disable in production
});
      

Monitoring with Apollo Studio
Apollo Studio is a powerful tool to track queries, errors, performance, and usage analytics of your GraphQL API.

// Sign up at Apollo Studio
// Connect your server with Apollo Studio by adding API key in Apollo Server setup
      

Logging and health checks
Proper logging helps monitor errors and usage. Health checks endpoints help services stay up and alert on issues.

// Example: Express health check endpoint
app.get('/health', (req, res) => {
  res.status(200).send('OK');
});
      

Overview
REST and GraphQL are two popular approaches to building APIs that let clients communicate with servers and fetch or modify data. Here’s a detailed comparison of how they differ and when to use each.
1. Data Fetching Patterns
- **REST:** Uses multiple endpoints (URLs) for different resources. You often need to make several requests to fetch related data.
- **GraphQL:** Uses a single endpoint. Clients specify exactly what data they want in a single query, reducing over-fetching and under-fetching.

// REST example - multiple requests needed to get user and posts
GET /users/1
GET /users/1/posts

// GraphQL example - one query fetches user and posts
{
  user(id: "1") {
    name
    posts {
      title
      content
    }
  }
}
      

2. Flexibility
- **REST:** Fixed response structures defined by server. Clients get all data in each endpoint, sometimes more than needed.
- **GraphQL:** Clients decide exactly which fields they want. This saves bandwidth and improves efficiency, especially on slow or mobile networks.

// REST returns all user data, even if client only needs name
GET /users/1
// Response:
{
  "id": "1",
  "name": "Alice",
  "email": "alice@example.com",
  "address": {...},
  "phone": "123456789"
}

// GraphQL query only fetches the name
{
  user(id: "1") {
    name
  }
}
      

3. Versioning
- **REST:** Often requires versioned endpoints (e.g., /api/v1/users) to handle API changes.
- **GraphQL:** Uses a single evolving schema. Fields can be deprecated without breaking clients, allowing smoother updates.

// REST versioning example
GET /api/v1/users/1
GET /api/v2/users/1

// GraphQL uses @deprecated directive instead
type User {
  oldField: String @deprecated(reason: "Use newField instead")
  newField: String
}
      

4. Error Handling
- **REST:** Uses HTTP status codes (404, 500, etc.) to indicate errors.
- **GraphQL:** Returns errors in the response body alongside data. Allows partial success.

// REST response for not found
Status: 404 Not Found

// GraphQL response with partial data and errors
{
  "data": {
    "user": null
  },
  "errors": [
    {
      "message": "User not found",
      "path": ["user"]
    }
  ]
}
      

5. Tooling and Ecosystem
- **REST:** Mature ecosystem, supported everywhere.
- **GraphQL:** Growing ecosystem with powerful tools like GraphiQL, Apollo Client, Prisma, and better developer experience.
6. Real-time Data
- **REST:** Needs separate protocols like WebSockets or polling.
- **GraphQL:** Has built-in support for real-time updates with subscriptions.
Summary Table
| Feature             | REST                           | GraphQL                      |
|---------------------|--------------------------------|------------------------------|
| Endpoints           | Multiple                       | Single                       |
| Data Fetching       | Fixed responses               | Flexible queries             |
| Versioning          | URL-based                    | Schema evolution             |
| Over-fetching       | Common                       | Minimal                     |
| Tooling             | Mature                       | Growing, powerful            |
| Real-time           | Via other tech               | Built-in subscriptions       |