Building a GraphQL Server with Apollo Server, Prisma, and TypeScript
Apollo Server handles GraphQL. Prisma handles the database. TypeScript ties them together with type safety. This stack gives you a fully typed pipeline fr...
12 Apr 2024

Apollo Server handles GraphQL. Prisma handles the database. TypeScript ties them together with type safety. This stack gives you a fully typed pipeline from database schema to API response.
Here's how to set it up from scratch.
Project setup
mkdir apollo-prisma-server
cd apollo-prisma-server
npm init -y
npm install apollo-server graphql prisma @prisma/client typescript ts-node @types/node
Initialize TypeScript:
npx tsc --init
Define your data model
Prisma uses a schema file to define your database structure. Create 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
}
Generate the Prisma client:
npx prisma generate
This creates a type-safe database client. Every query is autocompleted and type-checked by TypeScript.
Wire up Apollo Server
Create index.ts:
import { ApolloServer, gql } from 'apollo-server';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const typeDefs = gql`
type User {
id: Int!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: Int!
title: String!
content: String!
author: User!
}
type Query {
users: [User!]!
user(id: Int!): User
}
`;
const resolvers = {
Query: {
users: () => prisma.user.findMany({ include: { posts: true } }),
user: (_: any, { id }: { id: number }) =>
prisma.user.findUnique({ where: { id }, include: { posts: true } }),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});
The trade-off
This stack gives you end-to-end type safety. Prisma generates types from your database schema. Apollo Server validates queries against your GraphQL schema. TypeScript catches mismatches at compile time.
The cost: three layers of schema. You maintain a Prisma schema, a GraphQL schema, and TypeScript types. They overlap but don't auto-sync. Tools like typegraphql-prisma or code generators can reduce the duplication, but they add their own complexity.
For small projects, this stack can feel like overkill. For production APIs with multiple clients, it pays for itself in fewer runtime bugs and faster refactoring.