GraphQL Guide
GraphQL Guide
Section titled “GraphQL Guide”Learn how to build powerful GraphQL APIs with Veloce-TS v0.2.6.
Table of Contents
Section titled “Table of Contents”Overview
Section titled “Overview”Veloce-TS provides comprehensive GraphQL support with:
- Automatic Schema Generation from TypeScript types
- Decorator-based Resolvers for clean, declarative code
- Zod Integration for validation and type safety
- Type-safe Arguments with automatic validation
- Playground & Introspection for development
1. Install GraphQL Plugin
Section titled “1. Install GraphQL Plugin”import { VeloceTS } from 'veloce-ts';
const app = new VeloceTS();
app.install('graphql', { path: '/graphql', playground: true, introspection: true});2. GraphQL Configuration Options
Section titled “2. GraphQL Configuration Options”app.install('graphql', { path: '/graphql', // GraphQL endpoint path playground: true, // Enable GraphQL Playground introspection: true, // Enable schema introspection cors: true, // Enable CORS context: (req) => ({ // Custom context user: req.user, db: database })});Resolvers
Section titled “Resolvers”Basic Resolver
Section titled “Basic Resolver”import { Resolver, GQLQuery, Arg } from 'veloce-ts';import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), name: z.string(), email: z.string().email()});
@Resolver()export class UserResolver {
@GQLQuery() async users(): Promise<User[]> { return await userService.findAll(); }
@GQLQuery() async user(@Arg('id', z.string()) id: string): Promise<User | null> { return await userService.findById(id); }
@GQLQuery() async userByName(@Arg('name', z.string()) name: string): Promise<User[]> { return await userService.findByName(name); }}Mutations
Section titled “Mutations”import { GQLMutation, Arg, Body } from 'veloce-ts';
const CreateUserSchema = z.object({ name: z.string().min(1), email: z.string().email(), password: z.string().min(6)});
@Resolver()export class UserResolver {
@GQLMutation() async createUser(@Arg('input', CreateUserSchema) input: z.infer<typeof CreateUserSchema>): Promise<User> { const hashedPassword = await bcrypt.hash(input.password, 10);
const user = await userService.create({ ...input, password: hashedPassword });
return user; }
@GQLMutation() async updateUser( @Arg('id', z.string()) id: string, @Arg('input', CreateUserSchema.partial()) input: Partial<z.infer<typeof CreateUserSchema>> ): Promise<User> { return await userService.update(id, input); }
@GQLMutation() async deleteUser(@Arg('id', z.string()) id: string): Promise<boolean> { await userService.delete(id); return true; }}Subscriptions
Section titled “Subscriptions”import { GQLSubscription, Arg } from 'veloce-ts';
@Resolver()export class NotificationResolver {
@GQLSubscription() async userNotifications(@Arg('userId', z.string()) userId: string): AsyncIterable<Notification> { // Return an async iterator for real-time notifications return notificationService.getUserNotifications(userId); }
@GQLSubscription() async taskUpdates(@Arg('taskId', z.string()) taskId: string): AsyncIterable<TaskUpdate> { return taskService.getTaskUpdates(taskId); }}Decorators
Section titled “Decorators”GraphQL Decorators
Section titled “GraphQL Decorators”| Decorator | Purpose | Example |
|---|---|---|
@Resolver() | Marks a class as a GraphQL resolver | @Resolver() |
@GQLQuery() | Defines a GraphQL query | @GQLQuery() |
@GQLMutation() | Defines a GraphQL mutation | @GQLMutation() |
@GQLSubscription() | Defines a GraphQL subscription | @GQLSubscription() |
@Arg(name, schema) | Defines GraphQL arguments | @Arg('id', z.string()) |
Advanced Argument Handling
Section titled “Advanced Argument Handling”import { Arg } from 'veloce-ts';
const PaginationSchema = z.object({ page: z.number().min(1).default(1), limit: z.number().min(1).max(100).default(10), sortBy: z.enum(['name', 'email', 'createdAt']).default('createdAt'), sortOrder: z.enum(['asc', 'desc']).default('desc')});
const FilterSchema = z.object({ name: z.string().optional(), email: z.string().email().optional(), role: z.enum(['admin', 'user', 'guest']).optional()});
@Resolver()export class UserResolver {
@GQLQuery() async users( @Arg('pagination', PaginationSchema) pagination: z.infer<typeof PaginationSchema>, @Arg('filters', FilterSchema) filters: z.infer<typeof FilterSchema> ): Promise<{ users: User[]; total: number; page: number }> { const result = await userService.findWithPagination(pagination, filters);
return { users: result.users, total: result.total, page: pagination.page }; }}Schema Generation
Section titled “Schema Generation”Automatic Type Conversion
Section titled “Automatic Type Conversion”Veloce-TS automatically converts Zod schemas to GraphQL types:
// Zod Schemaconst UserSchema = z.object({ id: z.string(), name: z.string(), email: z.string().email(), age: z.number().int().min(0), isActive: z.boolean(), createdAt: z.date(), tags: z.array(z.string())});
// Automatically generates GraphQL type:// type User {// id: String!// name: String!// email: String!// age: Int!// isActive: Boolean!// createdAt: DateTime!// tags: [String!]!// }Custom Scalar Types
Section titled “Custom Scalar Types”import { GraphQLScalarType } from 'graphql';
const DateScalar = new GraphQLScalarType({ name: 'Date', description: 'Date custom scalar type', serialize: (value: Date) => value.toISOString(), parseValue: (value: string) => new Date(value), parseLiteral: (ast) => { if (ast.kind === Kind.STRING) { return new Date(ast.value); } return null; }});
// Register custom scalarapp.install('graphql', { path: '/graphql', scalars: { Date: DateScalar }});Advanced Features
Section titled “Advanced Features”Authentication Integration
Section titled “Authentication Integration”import { Auth, CurrentUser } from 'veloce-ts';
@Resolver()export class UserResolver {
@GQLQuery() @Auth() // Require authentication async me(@CurrentUser() user: any): Promise<User> { return user; }
@GQLMutation() @Auth() async updateProfile( @CurrentUser() currentUser: any, @Arg('input', UpdateProfileSchema) input: any ): Promise<User> { // Only allow users to update their own profile if (currentUser.id !== input.userId) { throw new ForbiddenException('Cannot update other users'); }
return await userService.update(currentUser.id, input); }}Error Handling
Section titled “Error Handling”import { NotFoundException, ValidationException } from 'veloce-ts';
@Resolver()export class UserResolver {
@GQLQuery() async user(@Arg('id', z.string()) id: string): Promise<User> { const user = await userService.findById(id);
if (!user) { throw new NotFoundException(`User with id ${id} not found`); }
return user; }
@GQLMutation() async createUser(@Arg('input', CreateUserSchema) input: any): Promise<User> { try { return await userService.create(input); } catch (error) { if (error.code === 'DUPLICATE_EMAIL') { throw new ValidationException('Email already exists'); } throw error; } }}DataLoader Integration
Section titled “DataLoader Integration”import DataLoader from 'dataloader';
@Resolver()export class UserResolver { private userLoader = new DataLoader(async (ids: string[]) => { const users = await userService.findByIds(ids); return ids.map(id => users.find(user => user.id === id) || null); });
@GQLQuery() async users(): Promise<User[]> { const userIds = await userService.getAllUserIds(); return await this.userLoader.loadMany(userIds); }}Examples
Section titled “Examples”Complete User Management API
Section titled “Complete User Management API”import { Resolver, GQLQuery, GQLMutation, Arg } from 'veloce-ts';import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), name: z.string(), email: z.string().email(), role: z.enum(['admin', 'user', 'guest']), createdAt: z.date()});
const CreateUserInput = z.object({ name: z.string().min(1), email: z.string().email(), role: z.enum(['admin', 'user', 'guest']).default('user')});
const UpdateUserInput = CreateUserInput.partial();
@Resolver()export class UserResolver {
@GQLQuery() async users(): Promise<User[]> { return await userService.findAll(); }
@GQLQuery() async user(@Arg('id', z.string()) id: string): Promise<User | null> { return await userService.findById(id); }
@GQLMutation() async createUser(@Arg('input', CreateUserInput) input: z.infer<typeof CreateUserInput>): Promise<User> { return await userService.create(input); }
@GQLMutation() async updateUser( @Arg('id', z.string()) id: string, @Arg('input', UpdateUserInput) input: z.infer<typeof UpdateUserInput> ): Promise<User> { return await userService.update(id, input); }
@GQLMutation() async deleteUser(@Arg('id', z.string()) id: string): Promise<boolean> { await userService.delete(id); return true; }}GraphQL Query Examples
Section titled “GraphQL Query Examples”# Query all usersquery GetUsers { users { id name email role createdAt }}
# Query specific userquery GetUser($id: String!) { user(id: $id) { id name email role }}
# Create user mutationmutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name email role createdAt }}
# Update user mutationmutation UpdateUser($id: String!, $input: UpdateUserInput!) { updateUser(id: $id, input: $input) { id name email role }}
# Delete user mutationmutation DeleteUser($id: String!) { deleteUser(id: $id)}Variables Example
Section titled “Variables Example”{ "id": "user-123", "input": { "name": "John Doe", "email": "john@example.com", "role": "user" }}Best Practices
Section titled “Best Practices”1. Use Zod for Validation
Section titled “1. Use Zod for Validation”// Good: Strong typing with Zodconst CreateUserSchema = z.object({ name: z.string().min(1).max(100), email: z.string().email(), age: z.number().int().min(0).max(150)});
@GQLMutation()async createUser(@Arg('input', CreateUserSchema) input: z.infer<typeof CreateUserSchema>) { // input is fully typed and validated}2. Handle Errors Gracefully
Section titled “2. Handle Errors Gracefully”@GQLQuery()async user(@Arg('id', z.string()) id: string): Promise<User> { try { const user = await userService.findById(id); if (!user) { throw new NotFoundException('User not found'); } return user; } catch (error) { // Log error for debugging console.error('Error fetching user:', error); throw error; }}3. Use DataLoaders for Performance
Section titled “3. Use DataLoaders for Performance”// Avoid N+1 queries@Resolver()export class PostResolver { private userLoader = new DataLoader(async (userIds: string[]) => { return await userService.findByIds(userIds); });
@GQLQuery() async posts(): Promise<Post[]> { const posts = await postService.findAll();
// Load users efficiently const users = await this.userLoader.loadMany( posts.map(post => post.authorId) );
return posts.map(post => ({ ...post, author: users.find(user => user?.id === post.authorId) })); }}Next Steps
Section titled “Next Steps”- TaskMaster Example - See GraphQL in action
- Authentication Guide - Secure your GraphQL API
- WebSocket Guide - Real-time subscriptions
- Testing Guide - Test your GraphQL resolvers