Building a GraphQL Server with Apollo Server, Prisma, and TypeScript
In recent years, GraphQL has gained immense popularity as a query language for APIs due to its flexibility and efficiency. Apollo Server is a GraphQL server implementation that simplifies the process of building GraphQL APIs, while Prisma offers a powerful ORM (Object-Relational Mapping) for database interactions. When combined with TypeScript, these tools provide a robust foundation for building scalable and type-safe GraphQL servers. In this article, we'll explore how to integrate Apollo Server with Prisma using TypeScript to create a GraphQL API.
mkdir apollo-prisma-server
cd apollo-prisma-server
npm init -y
npm install apollo-server graphql prisma @prisma/client typescript ts-node @types/node
Next, initialize TypeScript in the project:
npx tsc --init
Prisma simplifies database access by generating type-safe database client libraries based on your data model. Let's define our data model in Prisma. Create a new file called schema.prisma in the project root:
// schema.prisma
model User {
id Int @id @default(autoincrement())
name String
email String @unique
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
Now, generate the Prisma client:
npx prisma generate
Setting Up Apollo Server:
Create a new file called index.ts in the project root to define our Apollo Server:
import * as yup from 'yup';
const createUserSchema = yup.object().shape({
name: yup.string().required(),
email: yup.string().email().required(),
});
const updateUserSchema = yup.object().shape({
id: yup.number().required(),
name: yup.string(),
email: yup.string().email(),
});
const createPostSchema = yup.object().shape({
title: yup.string().required(),
content: yup.string().required(),
authorId: yup.number().required(),
});
const updatePostSchema = yup.object().shape({
id: yup.number().required(),
title: yup.string(),
content: yup.string(),
});
Next, let's update our resolver functions to perform input validation before executing mutations:
import { ValidationError } from 'yup';
const resolvers = {
Mutation: {
createUser: async (_, { data }) => {
try {
await createUserSchema.validate(data, { abortEarly: false });
return prisma.user.create({ data });
} catch (error) {
if (error instanceof ValidationError) {
throw new Error(error.errors.join(', '));
}
throw error;
}
},
updateUser: async (_, { data }) => {
try {
await updateUserSchema.validate(data, { abortEarly: false });
const { id, ...rest } = data;
return prisma.user.update({
where: { id },
data: rest,
});
} catch (error) {
if (error instanceof ValidationError) {
throw new Error(error.errors.join(', '));
}
throw error;
}
},
createPost: async (_, { data }) => {
try {
await createPostSchema.validate(data, { abortEarly: false });
return prisma.post.create({ data });
} catch (error) {
if (error instanceof ValidationError) {
throw new Error(error.errors.join(', '));
}
throw error;
}
},
updatePost: async (_, { data }) => {
try {
await updatePostSchema.validate(data, { abortEarly: false });
const { id, ...rest } = data;
return prisma.post.update({
where: { id },
data: rest,
});
} catch (error) {
if (error instanceof ValidationError) {
throw new Error(error.errors.join(', '));
}
throw error;
}
},
},
};
Then you can have the server
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.listen().then(({ url }) => {
console.log(`Server running at ${url}`);
});
This code defines our GraphQL schema, resolvers, and sets up an Apollo Server instance. We're using the Prisma client to interact with the database.
Now, let's start our Apollo Server:
npx ts-node index.ts
Your GraphQL server should now be running at the specified URL (typically http://localhost:4000). You can access the GraphQL Playground to interact with your API and execute queries.
Conclusion:
In this article, we've seen how to build a GraphQL server with Apollo Server, Prisma, and TypeScript. By leveraging the power of GraphQL for API queries, Prisma for database interactions, and TypeScript for type safety, we've created a robust and scalable backend for our applications. This setup allows for efficient development and maintenance of GraphQL APIs with confidence in type safety and data integrity.