GraphQL API with TypeScript: Step-by-Step Tutorial — Part 3

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course
```html

GraphQL API с TypeScript: пошаговый туториал — часть 3



In the previous parts of the tutorial, we covered the basics of creating a GraphQL server in TypeScript and learned how to set up simple queries. In this final part, we will delve into advanced techniques: mutations, subscriptions, and error handling. If you are studying TypeScript for beginners and want to master modern programming of APIs, this material will be an excellent step forward. All training is built on practical examples that you can immediately apply in your projects.



1. Implementing mutations with TypeScript



Mutations are data modification operations (create, update, delete). In GraphQL, they are denoted by the keyword mutation. Let's create a mutation for adding a new user.



First, define the type in the GraphQL schema:



type Mutation {  addUser(name: String!, email: String!): User!}


Now let's implement the resolver in TypeScript. Note the strict typing:



import { Resolver, Mutation, Arg } from 'type-graphql';import { User } from '../entities/User';

@Resolver()export class UserResolver { @Mutation(() => User) async addUser( @Arg('name') name: string, @Arg('email') email: string ): Promise<User> { const user = new User(); user.name = name; user.email = email; await user.save(); return user; }}


Important: in TypeScript we use the @Mutation and @Arg decorators from the type-graphql library, which makes the code clean and type-safe. This is especially useful for programming large projects where predictability is important.



2. Subscriptions for real-time updates



Subscriptions allow clients to receive real-time updates via WebSocket. In TypeScript, their implementation requires additional server configuration.



Let's add a subscription for new messages:



import { Subscription, Root } from 'type-graphql';

@Resolver()export class MessageResolver { @Subscription(() => Message, { topics: 'NEW_MESSAGE', }) newMessage(@Root() message: Message): Message { return message; }}


To publish an event, use pubSub.publish('NEW_MESSAGE', message). This allows you to create a chat or real-time notifications. Subscriptions are a powerful tool in the arsenal of programming, and TypeScript makes their implementation safe thanks to static typing.



3. Error handling and validation



Proper error handling is key to a reliable API. In TypeScript, we can use custom error classes:



import { ApolloError } from 'apollo-server-express';

export class UserInputError extends ApolloError { constructor(message: string) { super(message, 'USER_INPUT_ERROR'); Object.defineProperty(this, 'name', { value: 'UserInputError' }); }}


In resolvers, we validate input data:



@Mutation(() => User)async createUser(@Arg('email') email: string) {  if (!email.includes('@')) {    throw new UserInputError('Invalid email format');  }  // ... creation logic}


You can also use validation decorators from class-validator:



import { IsEmail, Length } from 'class-validator';

class CreateUserInput { @Length(2, 50) name: string;

@IsEmail() email: string;}


I especially recommend this approach for TypeScript for beginners, as it automates data validation and reduces the number of bugs.



4. Performance optimization



For production projects, it is important to avoid the N+1 problem. Use DataLoader for ```

Blogs

Book Recommendations