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: '[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
Related reading
- [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 ?�으�?검?�하?�면 ??글???��????�니??