본문으로 건너뛰기
Previous
Next
GraphQL Complete Guide | Schema· Resolver

GraphQL Complete Guide | Schema· Resolver

GraphQL Complete Guide | Schema· Resolver

이 글의 핵심

A practical guide to building efficient APIs with GraphQL?�defining schemas, resolvers, queries, mutations, and subscriptions, plus Apollo Server and Apollo Client examples in TypeScript.

What this post is about

This is a complete guide to building efficient APIs with GraphQL. It covers schema definition, resolvers, queries, mutations, subscriptions, and Apollo Server/Client?�with runnable examples.

From experience: Migrating a REST API to GraphQL cut API calls by about 70% and roughly doubled perceived performance in our mobile app by shipping exactly the fields each screen needed.

Introduction: ?�Our REST API feels wasteful??

Real-world pain points

Scenario 1: Over-fetching

You receive fields you never use. GraphQL lets the client request only what it needs.

Scenario 2: Under-fetching

You chain several REST calls to assemble a screen. GraphQL can fetch the graph in one request.

Scenario 3: Version sprawl

You maintain /v1, /v2, and so on. GraphQL typically evolves the schema without URL versions?�clients opt into new fields.


1. What is GraphQL?

Core traits

GraphQL is a query language for APIs.

Main benefits:

  • Precise data: ask for exactly what you need
  • Single endpoint: often one /graphql URL
  • Type system: strong contracts between client and server
  • Real time: subscriptions for push-style updates
  • Self-describing: the schema doubles as documentation

REST vs GraphQL:

  • REST: e.g. three calls (user, posts, comments)
  • GraphQL: one call for a composed query

2. Apollo Server

Install

npm install @apollo/server graphql

Minimal server

// server.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
const typeDefs = `#graphql
  type User {
    id: ID!
    name: String!
    email: String!
  }
  type Query {
    users: [User!]!
    user(id: ID!): User
  }
`;
const users = [
  { id: '1', name: 'John', email: '[email protected]' },
  { id: '2', name: 'Jane', email: '[email protected]' },
];
const resolvers = {
  Query: {
    users: () => users,
    user: (_: any, { id }: { id: string }) => 
      users.find(u => u.id === id),
  },
};
const server = new ApolloServer({
  typeDefs,
  resolvers,
});
const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});
console.log(`Server ready at ${url}`);

3. Defining the schema

Types

type User {
  id: ID!
  name: String!
  email: String!
  age: Int
  posts: [Post!]!
}
type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  comments: [Comment!]!
  createdAt: String!
}
type Comment {
  id: ID!
  text: String!
  author: User!
  post: Post!
}

Query

type Query {
  users: [User!]!
  user(id: ID!): User
  posts(limit: Int, offset: Int): [Post!]!
  post(id: ID!): Post
}

Mutation

type Mutation {
  createUser(name: String!, email: String!): User!
  updateUser(id: ID!, name: String, email: String): User!
  deleteUser(id: ID!): Boolean!
  
  createPost(title: String!, content: String!, authorId: ID!): Post!
}

Subscription

type Subscription {
  postCreated: Post!
  commentAdded(postId: ID!): Comment!
}

4. Resolvers

Basic resolvers

const resolvers = {
  Query: {
    users: async () => {
      return await db.user.findMany();
    },
    
    user: async (_: any, { id }: { id: string }) => {
      return await db.user.findUnique({ where: { id: parseInt(id) } });
    },
  },
  Mutation: {
    createUser: async (_: any, { name, email }: { name: string; email: string }) => {
      return await db.user.create({
        data: { name, email },
      });
    },
  },
  User: {
    posts: async (parent: any) => {
      return await db.post.findMany({
        where: { authorId: parent.id },
      });
    },
  },
};

5. Apollo Client (React)

Install

npm install @apollo/client graphql

Setup

// src/lib/apollo.ts
import { ApolloClient, InMemoryCache } from '@apollo/client';
export const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
  cache: new InMemoryCache(),
});
// src/App.tsx
import { ApolloProvider } from '@apollo/client';
import { client } from './lib/apollo';
function App() {
  return (
    <ApolloProvider client={client}>
      <YourApp />
    </ApolloProvider>
  );
}

6. Using queries

useQuery

import { gql, useQuery } from '@apollo/client';
const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
    }
  }
`;
function UsersList() {
  const { loading, error, data } = useQuery(GET_USERS);
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  return (
    <ul>
      {data.users.map((user) => (
        <li key={user.id}>
          {user.name} ({user.email})
        </li>
      ))}
    </ul>
  );
}

Variables

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      posts {
        id
        title
      }
    }
  }
`;
function UserProfile({ userId }: { userId: string }) {
  const { data } = useQuery(GET_USER, {
    variables: { id: userId },
  });
  return (
    <div>
      <h1>{data?.user.name}</h1>
      <p>{data?.user.email}</p>
      <h2>Posts</h2>
      <ul>
        {data?.user.posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

7. Using mutations

useMutation

import { gql, useMutation } from '@apollo/client';
const CREATE_USER = gql`
  mutation CreateUser($name: String!, $email: String!) {
    createUser(name: $name, email: $email) {
      id
      name
      email
    }
  }
`;
function CreateUserForm() {
  const [createUser, { loading, error }] = useMutation(CREATE_USER, {
    refetchQueries: [{ query: GET_USERS }],
  });
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
    await createUser({
      variables: {
        name: formData.get('name'),
        email: formData.get('email'),
      },
    });
  };
  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Name" required />
      <input name="email" type="email" placeholder="Email" required />
      <button type="submit" disabled={loading}>
        {loading ? 'Creating...' : 'Create User'}
      </button>
      {error && <p>Error: {error.message}</p>}
    </form>
  );
}

8. Subscriptions

Server

import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import { makeExecutableSchema } from '@graphql-tools/schema';
const schema = makeExecutableSchema({ typeDefs, resolvers });
const wsServer = new WebSocketServer({
  server: httpServer,
  path: '/graphql',
});
useServer({ schema }, wsServer);

Client

import { useSubscription, gql } from '@apollo/client';
const POST_CREATED = gql`
  subscription OnPostCreated {
    postCreated {
      id
      title
      author {
        name
      }
    }
  }
`;
function PostFeed() {
  const { data, loading } = useSubscription(POST_CREATED);
  if (loading) return <p>Waiting for posts...</p>;
  return (
    <div>
      <p>New post: {data?.postCreated.title}</p>
    </div>
  );
}

Tying this to interviews and your resume

N+1 issues, schema design, and REST vs GraphQL trade-offs show up often in API and backend interviews. For structured prep, see the Technical Interview Preparation Guide. To turn project experience into resume-ready outcomes, use the project and impact sections in [Developer Job Hunting: Practical Tips](/en/blog/developer-job-hunting-practical-tips/.


Summary and checklist

Key takeaways

  • GraphQL: a query language for APIs
  • Precise data: request only the fields you need
  • Single endpoint: typically /graphql
  • Type system: strong contracts and tooling
  • Real time: subscriptions when you need live updates
  • Apollo: the most widely used JS/TS stack for GraphQL

Implementation checklist

  • Set up Apollo Server
  • Define the schema
  • Implement resolvers
  • Configure Apollo Client
  • Implement queries and mutations
  • Add subscriptions (optional)
  • Deploy

  • [tRPC Complete Guide](/en/blog/trpc-complete-guide/
  • [NestJS Complete Guide](/en/blog/nestjs-complete-guide/
  • [Prisma Complete Guide](/en/blog/prisma-complete-guide/

Keywords covered in this post

GraphQL, API, Apollo, Schema, Resolver, Backend, TypeScript

Frequently asked questions (FAQ)

Q. GraphQL vs REST?�which should I choose?

A. GraphQL fits complex, client-driven data needs. REST stays simpler and is often easier to cache with standard HTTP tooling. For mobile apps with varied UIs, GraphQL is a strong fit; for straightforward APIs, REST may be enough.

Q. How do I solve the N+1 problem?

A. Use DataLoader for per-request batching and caching of related loads.

Q. How should I approach caching?

A. Apollo Client caches query results on the client. For server-side caching, add Redis (or similar) where resolver work is expensive.

Q. Is GraphQL safe for production?

A. Yes. Many large companies run it in production?�plan for performance, limits, and observability like any critical API layer.


?�주 묻는 질문 (FAQ)

Q. ???�용???�무?�서 ?�제 ?�나??

A. Build efficient APIs with GraphQL: schema design, resolvers, queries, mutations, subscriptions, and Apollo Server/Client???�무?�서????본문???�제?� ?�택 가?�드�?참고???�용?�면 ?�니??

Q. ?�행?�로 ?�으�?좋�? 글?�?

A. �?글 ?�단???�전 글 ?�는 관??글 링크�??�라가�??�서?��?배울 ???�습?�다. C++ ?�리�?목차?�서 ?�체 ?�름???�인?????�습?�다.

Q. ??깊이 공�??�려�?

A. cppreference?� ?�당 ?�이브러�?공식 문서�?참고?�세?? 글 말�???참고 ?�료 링크???�용?�면 좋습?�다.


같이 보면 좋�? 글 (?��? 링크)

??주제?� ?�결?�는 ?�른 글?�니??

  • [tRPC Complete Guide | End-to-End TypeScript Type Safety](/en/blog/trpc-complete-guide/
  • [NestJS Complete Guide | Build Scalable Node.js APIs](/en/blog/nestjs-complete-guide/
  • 개발??기술 면접 ?�벽 ?��?가?�드 | ?�고리즘부???�스???�계까�?
  • [Developer Job Hunting Guide | Resume· Portfolio](/en/blog/developer-job-hunting-practical-tips/

??글?�서 ?�루???�워??(관??검?�어)

GraphQL, API, Apollo, Schema, Resolver, Backend, TypeScript ?�으�?검?�하?�면 ??글???��????�니??