Real-world examples of GraphQL with TypeScript: practical examples for 2025
Most tutorials start with definitions; let’s start with code. A minimal but realistic example of GraphQL with TypeScript is a user service with queries and mutations.
// src/schema/user.ts
import { gql } from 'graphql-tag';
export const typeDefs = gql`
type User {
id: ID!
email: String!
name: String
createdAt: String!
}
type Query {
me: User
user(id: ID!): User
users(limit: Int = 20, offset: Int = 0): [User!]!
}
input CreateUserInput {
email: String!
name: String
}
type Mutation {
createUser(input: CreateUserInput!): User!
}
`;
On its own, this is just GraphQL SDL. The fun begins when we connect it to TypeScript types so our resolvers and client code stay in sync.
Modern examples of GraphQL with TypeScript: practical examples almost always use code generation. Tools like @graphql-codegen/cli read this schema and spit out TypeScript interfaces for User, Query, and Mutation. That’s where the productivity gain shows up.
Server-side examples of GraphQL with TypeScript using Apollo
A very common pattern in production is Apollo Server + TypeScript. Here’s a practical example of GraphQL with TypeScript for a Node server using Apollo Server 4.
// src/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { typeDefs } from './schema/user';
// In a real app, this would be a database
const users = new Map<string, { id: string; email: string; name?: string; createdAt: string }>();
interface Context {
currentUserId?: string;
}
const resolvers = {
Query: {
me: (_parent: unknown, _args: unknown, ctx: Context) => {
if (!ctx.currentUserId) return null;
return users.get(ctx.currentUserId) ?? null;
},
user: (_parent: unknown, args: { id: string }) => {
return users.get(args.id) ?? null;
},
users: (_parent: unknown, args: { limit?: number; offset?: number }) => {
const all = Array.from(users.values());
const { limit = 20, offset = 0 } = args;
return all.slice(offset, offset + limit);
},
},
Mutation: {
createUser: (
_parent: unknown,
args: { input: { email: string; name?: string } }
) => {
const id = `user_${users.size + 1}`;
const newUser = {
id,
email: args.input.email,
name: args.input.name,
createdAt: new Date().toISOString(),
};
users.set(id, newUser);
return newUser;
},
},
};
async function bootstrap() {
const server = new ApolloServer<Context>({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
context: async () => {
// In real life, read an auth header, verify JWT, etc.
return { currentUserId: 'user_1' };
},
listen: { port: 4000 },
});
console.log(`GraphQL server running at ${url}`);
}
bootstrap().catch((err) => {
console.error('Failed to start server', err);
process.exit(1);
});
This is one of the best examples for explaining why TypeScript matters: the Context type is wired into Apollo, so any resolver that tries to access a non-existent context field will fail at compile time instead of in production.
If you want stronger typing between schema and resolvers, you can generate resolver types using GraphQL Code Generator. That gives you Resolvers and QueryResolvers types that ensure every field is implemented and matches the schema.
Strongly-typed client examples using React, Apollo, and codegen
On the client side, examples of GraphQL with TypeScript: practical examples usually combine React, Apollo Client, and generated hooks. This is where developers feel the payoff: no more guessing field names or arguments.
First, define your operations:
// src/graphql/operations.ts
import { gql } from '@apollo/client';
export const GET_ME = gql`
query GetMe {
me {
id
email
name
}
}
`;
export const CREATE_USER = gql`
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
email
name
}
}
`;
Then configure GraphQL Code Generator to create typed hooks:
## codegen.yml
schema: 'http://localhost:4000/graphql'
documents: 'src/graphql/**/*.ts'
generates:
src/graphql/generated.ts:
plugins:
- 'typescript'
- 'typescript-operations'
- 'typescript-react-apollo'
config:
withHooks: true
After running graphql-codegen, you get useGetMeQuery and useCreateUserMutation hooks with full TypeScript types.
// src/components/Profile.tsx
import React from 'react';
import { useGetMeQuery } from '../graphql/generated';
export function Profile() {
const { data, loading, error } = useGetMeQuery();
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error.message}</p>;
if (!data?.me) return <p>No user</p>;
return (
<section>
<h2>Profile</h2>
<p>ID: {data.me.id}</p>
<p>Email: {data.me.email}</p>
{data.me.name && <p>Name: {data.me.name}</p>}
</section>
);
}
This is a very practical example of GraphQL with TypeScript: your React component is fully typed based on the schema and the query. If you remove name from the schema, the compiler complains wherever your UI tries to access data.me.name.
More examples of GraphQL with TypeScript: practical examples across use cases
To make this concrete, here are several real examples of GraphQL with TypeScript patterns you’ll actually use.
Example: typed pagination and filtering
APIs in 2024–2025 are expected to support rich filtering. Here’s an example of GraphQL with TypeScript for cursor-based pagination with filters.
// schema
const typeDefs = gql`
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
type Post {
id: ID!
title: String!
status: PostStatus!
createdAt: String!
}
type PostEdge {
cursor: String!
node: Post!
}
type PostConnection {
edges: [PostEdge!]!
hasNextPage: Boolean!
}
input PostFilter {
status: PostStatus
search: String
}
type Query {
posts(first: Int = 10, after: String, filter: PostFilter): PostConnection!
}
`;
With codegen, you get TypeScript types like PostStatus, PostFilter, and PostConnection. Your resolver can then use those types directly:
import { QueryPostsArgs, PostConnection } from './graphql/generated';
const resolvers = {
Query: {
posts: async (
_parent: unknown,
args: QueryPostsArgs
): Promise<PostConnection> => {
const { first = 10, after, filter } = args;
// TypeScript knows filter?.status is PostStatus | undefined
// and filter?.search is string | undefined
// ...fetch from database using typed filter
return {
edges: [],
hasNextPage: false,
};
},
},
};
This pattern shows up in many of the best examples of production GraphQL APIs: the schema becomes the single source of truth, and TypeScript keeps implementation honest.
Example: typed authentication context
Security is not glamorous, but it’s where typing pays off. Here’s a practical example of GraphQL with TypeScript for an auth-aware context.
// src/context.ts
export interface AuthUser {
id: string;
email: string;
roles: string[];
}
export interface Context {
user: AuthUser | null;
}
export async function buildContext({ req }: { req: Request }): Promise<Context> {
const authHeader = req.headers.get('authorization');
if (!authHeader) return { user: null };
const token = authHeader.replace('Bearer ', '');
// Verify token, load user from DB, etc.
const user: AuthUser = {
id: 'user_1',
email: 'user@example.com',
roles: ['ADMIN'],
};
return { user };
}
In your resolvers:
import { Context } from './context';
const resolvers = {
Mutation: {
deleteUser: (
_parent: unknown,
args: { id: string },
ctx: Context
) => {
if (!ctx.user || !ctx.user.roles.includes('ADMIN')) {
throw new Error('Not authorized');
}
// delete user
return true;
},
},
};
This is one of the cleaner examples of GraphQL with TypeScript: your auth logic is typed, shared, and enforced everywhere.
Example: typed input validation with Zod or Yup
A lot of teams in 2024 pair GraphQL with runtime validation libraries like Zod. Here’s a real example of GraphQL with TypeScript combining schema, validation, and resolvers.
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).optional(),
});
type CreateUserInput = z.infer<typeof CreateUserSchema>;
const resolvers = {
Mutation: {
createUser: (
_parent: unknown,
args: { input: CreateUserInput }
) => {
const parsed = CreateUserSchema.parse(args.input);
// parsed is fully typed and validated
return createUserInDb(parsed);
},
},
};
This pattern is showing up in many modern examples of GraphQL with TypeScript: practical examples because it combines static and runtime safety.
Schema-first vs code-first: examples include Nexus and TypeGraphQL
In 2024–2025, two styles dominate the examples of GraphQL with TypeScript you’ll see:
- Schema-first: write SDL, then generate TS types (Apollo, Yoga + Codegen)
- Code-first: write TypeScript, generate schema from it (Nexus, TypeGraphQL, Pothos)
Here’s a code-first example of GraphQL with TypeScript using Nexus:
import { makeSchema, objectType, queryType } from 'nexus';
const User = objectType({
name: 'User',
definition(t) {
t.nonNull.id('id');
t.nonNull.string('email');
t.string('name');
},
});
const Query = queryType({
definition(t) {
t.field('me', {
type: User,
resolve: (_root, _args, ctx) => ctx.user,
});
},
});
export const schema = makeSchema({
types: [User, Query],
});
Because everything is written in TypeScript, your IDE knows the shape of User and ctx.user without separate codegen. Code-first frameworks are popular in many of the best examples where teams want to stay entirely within TypeScript and avoid SDL.
2024–2025 trends: where GraphQL + TypeScript is heading
Looking at real-world adoption and conference talks over the past two years, a few trends show up repeatedly in examples of GraphQL with TypeScript: practical examples:
- Widespread use of GraphQL Code Generator to avoid hand-written types.
- Federated schemas across microservices, each written in TypeScript.
- Stronger alignment with typed ORMs like Prisma, which already uses TypeScript heavily.
- Better tooling around caching and performance in Apollo Client and urql, with TypeScript types guiding cache updates.
If you’re designing a new API today, it’s hard to justify not using TypeScript with GraphQL. The examples include everything from small startups to large organizations standardizing on this stack because it cuts down on runtime bugs and onboarding time.
For broader context on API reliability and typed interfaces, it’s worth looking at how strongly-typed systems are discussed in academic and industry research. While not GraphQL-specific, resources like MIT OpenCourseWare on type systems or computer science curricula at places like Harvard University provide background on why static typing tends to reduce certain classes of errors.
FAQ: common questions about examples of GraphQL with TypeScript
Q: What are some simple examples of GraphQL with TypeScript I can start with?
Start with a small user/profile API: a User type, me query, and updateProfile mutation. Implement it with Apollo Server in TypeScript, then add a React client with Apollo Client and codegen hooks. This gives you a full-stack, end-to-end example of GraphQL with TypeScript without a huge amount of code.
Q: Can you show an example of error handling with GraphQL and TypeScript?
A practical pattern is to define a union type for results. For instance, type CreateUserResult = CreateUserSuccess | ValidationError. In TypeScript, you then narrow on __typename in your client code. This is one of the best examples of combining GraphQL’s type system with TypeScript’s discriminated unions for safe error handling.
Q: Do I always need code generation for GraphQL with TypeScript?
No. Code-first libraries like Nexus, TypeGraphQL, and Pothos let you define schemas directly in TypeScript, so types come for free. That said, schema-first plus codegen is still the most common pattern in real examples because it works well with language-agnostic tooling and federated schemas.
Q: How do modern teams structure large projects that use GraphQL and TypeScript?
Typical examples include a schema/ directory for SDL or code-first types, a resolvers/ directory with typed resolvers, a context.ts for auth and per-request data, and a graphql/ directory on the client for queries and generated types. The consistent theme in these examples of GraphQL with TypeScript is keeping the schema as the source of truth and generating everything else.
Q: Where can I learn more about typed APIs and best practices?
For deeper background on typed interfaces and reliability, look at university material like Stanford’s CS resources or open courses at MIT OpenCourseWare. While they’re not GraphQL tutorials, they explain why strong typing and clear contracts, like the ones in these examples, tend to improve long-term maintainability.
Related Topics
Best examples of using fragments in GraphQL API (with real patterns)
Practical examples of using directives in GraphQL (with real patterns)
Real-world examples of GraphQL with Node.js: 3 practical builds you can copy
Real-world examples of optimizing GraphQL queries
Real-world examples of GraphQL with TypeScript: practical examples for 2025
Examples of Schema Definition in GraphQL: 3 Practical Examples for Real APIs
Explore More GraphQL API Examples
Discover more examples and insights in this category.
View All GraphQL API Examples