Skip to main content

Chapter 8: Integrations

GraphQL Yoga can be integrated with various tools and frameworks to enhance your development experience. In this chapter, we'll explore common integrations and how to use them effectively.

Prisma Integration

Basic Setup

First, let's set up Prisma with GraphQL Yoga:

import { PrismaClient } from '@prisma/client'
import { createYoga } from '@graphql-yoga/node'

const prisma = new PrismaClient()

const yoga = createYoga({
schema,
context: async ({ request }) => {
return {
prisma
}
}
})

Database Access Patterns

Use Prisma in your resolvers:

const resolvers = {
Query: {
users: async (_, { first, after }, context) => {
return context.prisma.user.findMany({
take: first,
skip: after ? 1 : 0,
cursor: after ? { id: after } : undefined
})
},

user: async (_, { id }, context) => {
return context.prisma.user.findUnique({
where: { id }
})
}
},

Mutation: {
createUser: async (_, { input }, context) => {
return context.prisma.user.create({
data: input
})
},

updateUser: async (_, { id, input }, context) => {
return context.prisma.user.update({
where: { id },
data: input
})
}
}
}

Advanced Prisma Features

Use Prisma's advanced features:

const resolvers = {
Query: {
searchUsers: async (_, { query }, context) => {
return context.prisma.user.findMany({
where: {
OR: [
{ name: { contains: query } },
{ email: { contains: query } }
]
}
})
},

usersWithPosts: async (_, { userId }, context) => {
return context.prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
include: {
comments: true
}
}
}
})
}
}
}

TypeScript Enhancements

Code Generation

Use GraphQL Code Generator for type safety:

npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers

Configure code generation:

# codegen.yml
schema: './schema/**/*.graphql'
generates:
./src/generated/graphql.ts:
plugins:
- typescript
- typescript-resolvers
config:
contextType: ../types#Context
mappers:
User: ../prisma#User
Post: ../prisma#Post

Use generated types:

import { Resolvers } from '../generated/graphql'

const resolvers: Resolvers = {
Query: {
user: async (_, { id }, context) => {
return context.prisma.user.findUnique({
where: { id }
})
}
}
}

Type-Safe Context

Define type-safe context:

// types.ts
import { PrismaClient } from '@prisma/client'
import { User } from '@prisma/client'

export interface Context {
prisma: PrismaClient
user: User | null
request: Request
}

// server.ts
const yoga = createYoga({
schema,
context: async ({ request }): Promise<Context> => {
const token = request.headers.get('authorization')?.replace('Bearer ', '')
let user = null

if (token) {
user = await prisma.user.findUnique({
where: { id: verifyToken(token) }
})
}

return {
prisma,
user,
request
}
}
})

Envelop Plugin Ecosystem

Response Cache

Use the response cache plugin:

import { useResponseCache } from '@envelop/response-cache'

const yoga = createYoga({
schema,
plugins: [
useResponseCache({
// Cache responses for 60 seconds
ttl: 60 * 1000,
// Custom cache key
session: (context) => context.user?.id,
// Custom cache validation
shouldCacheResult: (result) => {
return !result.errors
}
})
]
})

Query Validation

Use the validation plugin:

import { useValidation } from '@envelop/validation'

const yoga = createYoga({
schema,
plugins: [
useValidation({
// Custom validation rules
rules: [
// Limit query depth
maxDepth(3),
// Limit query complexity
maxComplexity(1000),
// Disable introspection in production
process.env.NODE_ENV === 'production' ? noIntrospection() : null
].filter(Boolean)
})
]
})

Error Handling

Use the error handling plugin:

import { useErrorHandler } from '@envelop/error-handler'

const yoga = createYoga({
schema,
plugins: [
useErrorHandler({
// Custom error formatter
formatError: (error) => {
// Log error
console.error(error)

// Return formatted error
return {
message: error.message,
code: error.extensions?.code || 'INTERNAL_SERVER_ERROR'
}
}
})
]
})

Framework Integrations

Next.js Integration

Integrate with Next.js:

// pages/api/graphql.ts
import { createYoga } from '@graphql-yoga/node'
import { schema } from '../../schema'

export default createYoga({
schema,
graphqlEndpoint: '/api/graphql'
})

// Configure Next.js
export const config = {
api: {
bodyParser: false
}
}

Express Integration

Integrate with Express:

import express from 'express'
import { createYoga } from '@graphql-yoga/node'

const app = express()
const yoga = createYoga({
schema
})

app.use('/graphql', yoga)

app.listen(4000, () => {
console.log('Server is running on http://localhost:4000/graphql')
})

Fastify Integration

Integrate with Fastify:

import Fastify from 'fastify'
import { createYoga } from '@graphql-yoga/node'

const app = Fastify()
const yoga = createYoga({
schema
})

app.route({
url: '/graphql',
method: ['GET', 'POST', 'OPTIONS'],
handler: async (req, reply) => {
const response = await yoga.handleNodeRequest(req, {
req,
reply
})

response.headers.forEach((value, key) => {
reply.header(key, value)
})

reply.status(response.status)
reply.send(response.body)
}
})

app.listen(4000, () => {
console.log('Server is running on http://localhost:4000/graphql')
})

Testing Tools

Jest Integration

Test your GraphQL API with Jest:

import { createTestClient } from 'apollo-server-testing'
import { schema } from '../schema'

describe('User Queries', () => {
const { query } = createTestClient({
schema,
context: {
prisma: mockPrisma
}
})

it('fetches user by id', async () => {
const res = await query({
query: gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
`,
variables: { id: '1' }
})

expect(res.data.user).toEqual({
id: '1',
name: 'John Doe'
})
})
})

Testing Utilities

Create testing utilities:

// test-utils.ts
import { PrismaClient } from '@prisma/client'
import { createYoga } from '@graphql-yoga/node'

export function createTestContext() {
const prisma = new PrismaClient()

return {
prisma,
yoga: createYoga({
schema,
context: {
prisma
}
})
}
}

export async function executeOperation(operation: any, context: any) {
const { yoga } = context

const response = await yoga.fetch('http://localhost:4000/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(operation)
})

return response.json()
}

Example: Complete Integration Setup

Here's a complete example of integrating various tools:

// server.ts
import { createYoga } from '@graphql-yoga/node'
import { PrismaClient } from '@prisma/client'
import { useResponseCache } from '@envelop/response-cache'
import { useValidation } from '@envelop/validation'
import { useErrorHandler } from '@envelop/error-handler'

const prisma = new PrismaClient()

const yoga = createYoga({
schema,
plugins: [
useResponseCache({
ttl: 60 * 1000
}),
useValidation({
rules: [
maxDepth(3),
maxComplexity(1000)
]
}),
useErrorHandler({
formatError: (error) => {
console.error(error)
return {
message: error.message,
code: error.extensions?.code || 'INTERNAL_SERVER_ERROR'
}
}
})
],
context: async ({ request }) => {
const token = request.headers.get('authorization')?.replace('Bearer ', '')
let user = null

if (token) {
user = await prisma.user.findUnique({
where: { id: verifyToken(token) }
})
}

return {
prisma,
user,
request
}
}
})

// resolvers.ts
import { Resolvers } from '../generated/graphql'
import { Context } from '../types'

const resolvers: Resolvers<Context> = {
Query: {
users: async (_, { first, after }, context) => {
return context.prisma.user.findMany({
take: first,
skip: after ? 1 : 0,
cursor: after ? { id: after } : undefined
})
}
},

Mutation: {
createUser: async (_, { input }, context) => {
return context.prisma.user.create({
data: input
})
}
}
}

Summary

In this chapter, we've covered:

  • Prisma integration for database access
  • TypeScript enhancements with code generation
  • Envelop plugin ecosystem
  • Framework integrations
  • Testing tools and utilities

These integrations will help you build robust and maintainable GraphQL APIs. In the next chapter, we'll explore testing and tooling in detail.