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.