TypeScript

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

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 from database schema to API response.

Here's how to set it up from scratch.

Project setup

Bash
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:

Bash
npx tsc --init

Define your data model

Prisma uses a schema file to define your database structure. Create schema.prisma:

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:

Bash
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:

Typescript
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.