← Back to TypeScript Course | Chapter 28: Real World Projects | Lesson 11 of 14

GraphQL API with TypeScript

GraphQL APIs expose typed schemas and operations. TypeScript can model resolver arguments, results, and domain objects around the GraphQL layer.
Syntax
typescript
type Resolver<TArgs, TResult> = (parent: unknown, args: TArgs, context: unknown) => Promise<TResult>;

const resolveField: Resolver<{ id: string }, TypeName> = async (parent, args, context) => {
    return { /* fields matching TypeName */ };
};

Core Concept

A typed GraphQL API keeps its TypeScript resolver types in sync with its GraphQL schema, so a resolver that returns the wrong shape for a field is caught at compile time instead of producing a confusing runtime GraphQL error.

Example: Core Concept

typescript
interface User {
  id: string;
  name: string;
}
// A resolver returning an incomplete User object is a compile-time error.
function resolveUser(): User {
  return { id: "1", name: "Ravi" };
}
console.log(resolveUser());

Basic Setup

A basic setup uses a code generator (like GraphQL Code Generator) that reads your .graphql schema files and produces matching TypeScript types and resolver signatures automatically.

Example: Basic Setup

typescript
// graphql-codegen reads schema.graphql, generates matching TS types.
// codegen.yml: { generates: { "types.ts": { plugins: ["typescript"] } } }
console.log("Code generation keeps resolver types in sync with the schema");

Typed Example

A typed example: a schema field user(id: ID!): User generates a resolver signature (parent, args: { id: string }, context) => Promise<User>, so returning an object missing a required User field is flagged immediately.

Example: Typed Example

typescript
interface User { id: string; name: string; }
interface QueryArgs { id: string; }
async function userResolver(parent: unknown, args: QueryArgs): Promise<User> {
  return { id: args.id, name: "Ravi" };
}
userResolver(null, { id: "1" }).then(console.log);

Project Usage

In a real project, regenerating types as part of the build (whenever the schema changes) keeps resolvers from silently drifting out of sync with the schema they're supposed to implement.

Example: Project Usage

typescript
// package.json: "build": "graphql-codegen && tsc"
console.log("Regenerating types on every schema change keeps resolvers honest");

Best Practices

Run code generation as an automated build/CI step rather than a manual one-off command, since a schema change that isn't followed by regeneration will leave stale, misleading types in the codebase.

Example: Best Practices

typescript
// CI step: npx graphql-codegen --check
console.log("Run codegen in CI, not just manually, to avoid stale types");
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Editing the generated types by hand, so the next code generation overwrites the changes.
  2. Forgetting to re-run the code generator after changing the schema, so resolver types are out of date.
  3. Typing resolver arguments as any, which throws away the value of generated types.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.