• v3 (latest)
  • Integrations
  • uWebSockets

Integration with uWebSockets

uWebSockets is µWebSockets.js is an HTTP/WebSocket server for Node.js.

Installation

yarn add graphql-yoga graphql

Example

index.ts
import { App, HttpRequest, HttpResponse } from 'uWebSockets.js'
import { createSchema, createYoga } from 'graphql-yoga'
import { Readable } from 'node:stream'
 
interface ServerContext {
  req: HttpRequest
  res: HttpResponse
}
 
const yoga = createYoga<ServerContext>({
  schema: createSchema({
    typeDefs: /* GraphQL */ `
      type Query {
        hello: String!
      }
    `,
    resolvers: {
      Query: {
        hello: () => 'Hello world!'
      }
    }
  })
})
 
App()
  .any('/*', async (res, req) => {
    let body: any
    const method = req.getMethod()
    if (method !== 'get' && method !== 'head') {
      body = new Readable({
        read() {}
      })
      res
        .onData(function (chunk, isLast) {
          body.push(Buffer.from(chunk))
          if (isLast) {
            body.push(null)
          }
        })
        .onAborted(function () {
          body.push(null)
        })
    }
    const headers = {}
    req.forEach((key, value) => {
      headers[key] = value
    })
    const response = await yoga.fetch(
      req.getUrl(),
      {
        method,
        headers,
        body
      },
      {
        req,
        res
      }
    )
    res.writeStatus(`${response.status} ${response.statusText}`)
    response.headers.forEach((value, key) => {
      // content-length causes an error with Node.js's fetch
      if (key === 'content-length') {
        return
      }
      res.writeHeader(key, value)
    })
    if (response.body) {
      if (response.body instanceof Uint8Array) {
        res.end(response.body)
        return
      }
      for await (const chunk of response.body) {
        res.write(chunk)
      }
    }
    res.end()
  })
  .listen(4000, () => {
    console.log(`Server is running on http://localhost:4000`)
  })

Subscriptions with WebSockets

You can also use WebSockets instead of SSE with graphql-ws;

yarn add graphql-ws
index.ts
import { App, HttpRequest, HttpResponse } from 'uWebSockets.js'
import { createSchema, createYoga, Repeater } from 'graphql-yoga'
import { Readable } from 'node:stream'
import { makeBehavior } from 'graphql-ws/lib/use/uWebSockets'
import { ExecutionArgs, execute, subscribe } from 'graphql'
 
interface ServerContext {
  req: HttpRequest
  res: HttpResponse
}
 
export const yoga = createYoga<ServerContext>({
  schema: createSchema({
    typeDefs: /* GraphQL */ `
      type Query {
        hello: String!
      }
 
      type Subscription {
        time: String!
      }
    `,
    resolvers: {
      Query: {
        hello: () => 'Hello world!'
      },
      Subscription: {
        time: {
          subscribe: () =>
            new Repeater((push, stop) => {
              const interval = setInterval(() => {
                push({
                  time: new Date().toISOString()
                })
              }, 1000)
              stop.then(() => clearInterval(interval))
            })
        }
      }
    }
  }),
  graphiql: {
    subscriptionsProtocol: 'WS' // use WebSockets instead of SSE
  }
})
 
const yogaHandler = async (res: HttpResponse, req: HttpRequest) => {
  let body: any
  const method = req.getMethod()
  if (method !== 'get' && method !== 'head') {
    body = new Readable({
      read() {}
    })
    res
      .onData(function (chunk, isLast) {
        body.push(Buffer.from(chunk))
        if (isLast) {
          body.push(null)
        }
      })
      .onAborted(function () {
        body.push(null)
      })
  }
  const headers = {}
  req.forEach((key, value) => {
    headers[key] = value
  })
  const response = await yoga.fetch(
    req.getUrl(),
    {
      method,
      headers,
      body
    },
    {
      req,
      res
    }
  )
  res.writeStatus(`${response.status} ${response.statusText}`)
  response.headers.forEach((value, key) => {
    // content-length causes an error with Node.js's fetch
    if (key === 'content-length') {
      return
    }
    res.writeHeader(key, value)
  })
  if (response.body) {
    if (response.body instanceof Uint8Array) {
      res.end(response.body)
      return
    }
    for await (const chunk of response.body) {
      res.write(chunk)
    }
  }
  res.end()
}
 
// yoga's envelop may augment the `execute` and `subscribe` operations
// so we need to make sure we always use the freshest instance
type EnvelopedExecutionArgs = ExecutionArgs & {
  rootValue: {
    execute: typeof execute
    subscribe: typeof subscribe
  }
}
 
const wsHandler = makeBehavior({
  execute: (args) => (args as EnvelopedExecutionArgs).rootValue.execute(args),
  subscribe: (args) =>
    (args as EnvelopedExecutionArgs).rootValue.subscribe(args),
  onSubscribe: async (ctx, msg) => {
    const { schema, execute, subscribe, contextFactory, parse, validate } =
      yoga.getEnveloped(ctx)
 
    const args: EnvelopedExecutionArgs = {
      schema,
      operationName: msg.payload.operationName,
      document: parse(msg.payload.query),
      variableValues: msg.payload.variables,
      contextValue: await contextFactory(),
      rootValue: {
        execute,
        subscribe
      }
    }
 
    const errors = validate(args.schema, args.document)
    if (errors.length) return errors
    return args
  }
})
 
App()
  .any('/*', yogaHandler)
  .ws('/graphql', wsHandler)
  .listen(() => {
    console.log(`Server is running on http://localhost:4000`)
  })

Also see our full example here

Last updated on January 5, 2023