Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .changeset/nice-deers-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
'@graphql-yoga/common': minor
---

**Improve DX for Cloudflare Workers and other environments like Bun that needs a default export with a fetch method**

- You no longer need to export fetch specifically in a different object. Instead, you can export Yoga instance directly.

Before in CF Workers Modules you had to do;

```ts
import { createServer } from '@graphql-yoga/common'

const server = createServer()

export default {
fetch: server.fetch,
}
```

Now you can export Yoga instance as-is like below;

```ts
import { createServer } from '@graphql-yoga/common'

export default createServer()
```

- Environment object is now passed as `ServerContext` to the execution. So you can access KV Namespaces and other `Env` variables in the context.

```ts
import { createServer } from '@graphql-yoga/common'

interface Env {
MY_NAMESPACE: KVNamespace
SOME_TOKEN: String // An example environment variable
}

export default createServer<Env>({
typeDefs: /* GraphQL */ `
type Query {
todo(id: ID!): String
todos: [String]
}
type Mutation {
createTodo(id: ID!, text: String!): String
deleteTodo(id: ID!): String
}
`,
resolvers: {
Query: {
todo: (_, { id }, { MY_NAMESPACE }) => MY_NAMESPACE.get(id),
todos: (_, __, { MY_NAMESPACE }) => MY_NAMESPACE.list(),
},
Mutation: {
// MY_NAMESPACE is available as a GraphQL context
createTodo(_, { id, text }, context) {
return context.MY_NAMESPACE.put(id, text)
},
deleteTodo(_, { id }, context) {
return context.MY_NAMESPACE.delete(id)
},
},
},
})
```
4 changes: 1 addition & 3 deletions examples/cloudflare-modules/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// src/index.mjs
import { createServer } from '@graphql-yoga/common'

const yoga = createServer()

export default { fetch: yoga.fetch }
export default createServer()
20 changes: 14 additions & 6 deletions packages/common/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,13 +653,21 @@ export function createServer<
options,
)
// TODO: Will be removed once we get rid of classes
const fnHandler = (input: any) => {
const fnHandler = (input: any, ctx: any) => {
// Is input a container object over Request?
if (input.request) {
return server.handleRequest(input.request, input as any)
// In this input is also the context
return server.handleRequest(input.request, input)
}
return server.handleRequest(input, undefined as any)
// Or is it Request itself?
// Then ctx is present and it is the context
return server.handleRequest(input, ctx)
}
return new Proxy(fnHandler as any, {
return new Proxy(server as any, {
// It should have all the attributes of the handler function and the server instance
has: (_, prop) => {
return prop in fnHandler || prop in server
},
get: (_, prop) => {
if (server[prop]) {
if (server[prop].bind) {
Expand All @@ -674,8 +682,8 @@ export function createServer<
return fnHandler[prop]
}
},
apply(_, __, [input]: Parameters<typeof fnHandler>) {
return fnHandler(input)
apply(_, __, [input, ctx]: Parameters<typeof fnHandler>) {
return fnHandler(input, ctx)
},
})
}
51 changes: 43 additions & 8 deletions website/docs/integrations/integration-with-cloudflare-workers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,50 @@ server.start()
```ts
import { createServer } from '@graphql-yoga/common'

const yoga = createServer()
export default createServer()
```

export default {
fetch: yoga.fetch,
### Access to environmental values (KV Namespaces etc.)

You can access your KV namespaces etc through the context.

```ts
import { createServer } from '@graphql-yoga/common'

interface Env {
MY_NAMESPACE: KVNamespace
}

export createServer<Env>({
typeDefs: /* GraphQL */`
type Query {
todo(id: ID!): String
todos: [String]
}
type Mutation {
createTodo(id: ID!, text: String!): String
deleteTodo(id: ID!): String
}
`,
resolvers: {
Query: {
todo: (_, { id }, { MY_NAMESPACE }) => MY_NAMESPACE.get(id),
todos: (_, __, { MY_NAMESPACE }) => MY_NAMESPACE.list(),
},
Mutation: {
// MY_NAMESPACE is available as a GraphQL context
createTodo(_, { id, text }, context) {
return context.MY_NAMESPACE.put(id, text)
},
deleteTodo(_, { id }, context) {
return context.MY_NAMESPACE.delete(id)
}
}
}
})
```

If you need `env`, or `ctx` inside your resolvers, you can set the `context` on your GraphQL server similar to what you see here:
If you need `ExecutionContext` as well inside your resolvers, you can set the `context` on your GraphQL server similar to what you see here:

```ts
import { createServer } from '@graphql-yoga/common'
Expand All @@ -68,11 +104,10 @@ interface Env {

const yoga = createServer<{ env: Env; ctx: ExecutionContext }>()

const fetch = async (req: Request, env: Env, ctx: ExecutionContext) =>
await server.handleRequest(req, { env, ctx })

export default {
fetch,
fetch(request: Request, env: Env, ctx: ExecutionContext) {
return yoga.handleRequest(request, { env, ctx })
},
}
```

Expand Down