import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { GraphQLError } from 'graphql';
// Type definitions
const typeDefs = `#graphql
type Book {
id: ID!
title: String!
author: String!
}
type Query {
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, author: String!): Book
}
`;
// Custom error classes
class ValidationError extends GraphQLError {
constructor(message: string) {
super(message, {
extensions: {
code: 'BAD_USER_INPUT',
http: { status: 400 }
}
});
}
}
class NotFoundError extends GraphQLError {
constructor(message: string) {
super(message, {
extensions: {
code: 'NOT_FOUND',
http: { status: 404 }
}
});
}
}
// Data store
interface Book {
id: string;
title: string;
author: string;
}
const books: Book[] = [
{ id: '1', title: 'The Hobbit', author: 'J.R.R. Tolkien' },
{ id: '2', title: 'Harry Potter', author: 'J.K. Rowling' }
];
// Resolvers
const resolvers = {
Query: {
book: (_: unknown, { id }: { id: string }) => {
const book = books.find(book => book.id === id);
if (!book) {
throw new NotFoundError(`Book with ID ${id} not found`);
}
return book;
},
},
Mutation: {
addBook: (_: unknown, { title, author }: { title: string; author: string }) => {
const validationErrors: string[] = [];
if (!title?.trim()) {
validationErrors.push('Title is required');
}
if (!author?.trim()) {
validationErrors.push('Author is required');
}
if (validationErrors.length > 0) {
throw new ValidationError(validationErrors.join(', '));
}
const newBook = {
id: String(books.length + 1),
title: title.trim(),
author: author.trim()
};
books.push(newBook);
return newBook;
},
}
};
// Server setup
const server = new ApolloServer({
typeDefs,
resolvers,
formatError: (formattedError, error) => {
console.error('GraphQL Error:', error);
if (formattedError.extensions?.code === 'INTERNAL_SERVER_ERROR') {
return new GraphQLError('Internal server error', {
extensions: {
code: 'INTERNAL_SERVER_ERROR',
http: { status: 500 }
}
});
}
return formattedError;
},
});
const startServer = async () => {
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 }
});
console.log(`🚀 Server ready at ${url}`);
};
startServer().catch(console.error);