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
/graphqlURL - 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: 'john@example.com' },
{ id: '2', name: 'Jane', email: 'jane@example.com' },
];
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.
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
Related reading
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.
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. 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.
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- GraphQL 완벽 가이드: API 쿼리 언어
- NestJS Complete Guide | Build Scalable Node.js APIs
- JWT Authentication Guide | Access Tokens· Refresh Tokens
Keywords Covered in This Article (Related Search Terms)
This article covers GraphQL, Web-API, Apollo, Schema, Resolver, Backend, TypeScript.