1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
# @eol/gq
A batteries-included GraphQL toolkit for Deno, wrapping `graphql-js` and `@graphql-tools/schema` with the bits most APIs end up reaching for anyway: a Fetch-compatible HTTP handler, a cached `gql` tag, a `.graphql` file loader, and an embedded GraphiQL UI powered by [`@eeeooolll/graphiql`](https://www.npmjs.com/package/@eeeooolll/graphiql).
## Install
Published on JSR as [`@eol/gq`](https://jsr.io/@eol/gq).
```sh
deno add jsr:@eol/gq
```
Or import directly:
```ts
import { executeSchema, gql, GraphQLHTTP } from "jsr:@eol/gq";
```
## Quick start
```ts
import { executeSchema, gql, GraphQLHTTP } from "@eol/gq";
const schema = executeSchema({
resolvers: {
Query: {
hello: (_, { name }) => `hello, ${name ?? "world"}`
}
},
typeDefs: gql`
type Query {
hello(name: String): String
}
`
});
Deno.serve(
{ port: 8000 },
GraphQLHTTP({ graphiql: true, schema })
);
```
Visit `http://localhost:8000` in a browser for GraphiQL, or `POST` a query to the same URL.
## Loading schemas from `.graphql` files
Use `importQL` to read an entry file and resolve its imports into a single SDL string.
### Explicit imports
```graphql
# schema/schema.graphql
# import "./post.graphql"
# import "./user.graphql"
type Query {
me: User
posts: [Post!]!
}
```
```ts
import { executeSchema, gql, importQL } from "@eol/gq";
const typeDefs = gql(await importQL("schema/schema.graphql"));
const schema = executeSchema({ resolvers, typeDefs });
```
### Dynamic imports
Drop `# DYNAMIC_IMPORTS` in your entry file and every `.graphql` file found one level below `<cwd>/schema/` gets inlined at that marker:
```
schema/
schema.graphql # contains: # DYNAMIC_IMPORTS
post/
post.graphql
user/
user.graphql
```
## API
All symbols are re-exported from the package root (`@eol/gq`).
### `GraphQLHTTP(options)`
Returns a `(request) => Promise<Response>` handler. Pluggable into `Deno.serve`, [oak](https://jsr.io/@oak/oak), [hono](https://jsr.io/@hono/hono), or anything else Fetch-shaped.
Options:
| Option | Type | Description |
| ------------------- | ------------------------------------- | ---------------------------------------------- |
| `schema` | `GraphQLSchema` | Required. Executable schema. |
| `context` | `(req) => Ctx \| Promise<Ctx>` | Builds the resolver context per request. |
| `graphiql` | `boolean` | Serve GraphiQL on `GET` + `Accept: text/html`. |
| `headers` | `HeadersInit` | Extra headers merged into every response. |
| `playgroundOptions` | `Omit<RenderPageOptions, "endpoint">` | Passthrough options for the GraphiQL renderer. |
### `executeSchema(config)`
Re-export of `@graphql-tools/schema`’s `makeExecutableSchema`, renamed for brevity.
### `gql` *(tagged template)*
Parses a GraphQL string into a `DocumentNode`. Results are cached by normalized source, and embedded `DocumentNode` interpolations are inlined from their original source.
Companion knobs:
- `disableExperimentalFragmentVariables()`
- `disableFragmentWarnings()`
- `enableExperimentalFragmentVariables()`
- `resetCaches()`
### `GraphQLWS(options)`
Returns a `(request: Request) => Response` handler that upgrades incoming requests to a WebSocket and speaks the [`graphql-transport-ws`](https://github.com/enisdenjo/graphql-ws) protocol. Pass it as `subscriptions` to `GraphQLHTTP` so HTTP and WS share one endpoint.
Options:
| Option | Type | Description |
| --------- | ------------------------------ | ------------------------------------------------------------------- |
| `context` | `(ctx) => Ctx \| Promise<Ctx>` | Builds the resolver context per WS connection. |
| `schema` | `GraphQLSchema` | Required. Executable schema. |
| `...` | `Partial<ServerOptions>` | Anything else `graphql-ws` accepts (`onConnect`, `onSubscribe`, …). |
### `PubSub`
Re-export of `graphql-subscriptions`'s in-memory pub/sub. Resolvers call `pubsub.asyncIterator(["EVENT"])` for `subscribe`; mutations call `pubsub.publish("EVENT", payload)`. For multi-process deployments swap in `graphql-redis-subscriptions` — same `PubSubEngine` interface.
### `importQL(path)`
Reads a `.graphql` file and resolves its imports. See [Loading schemas from `.graphql` files](#loading-schemas-from-graphql-files).
### `runHttpQuery(params, options, request)`
Low-level executor that `GraphQLHTTP` delegates to. Use it if you’re rolling your own transport but still want the context wiring.
### Types
`GQLOptions`, `GQLRequest`, `GraphQLParams`, `GraphQLHandler`, plus `RenderPageOptions` for the GraphiQL shell.
We also export commonly imported GraphQL types: `DocumentNode`, `ExecutionResult`, `GraphQLArgs`, `GraphQLFieldResolver`, `GraphQLResolveInfo`, `GraphQLScalarType`, `GraphQLSchema`, `GraphQLTypeResolver`, and `Source`.
## Features
- Import `*.graphql` files — explicit and dynamic — via `importQL`.
- Embedded GraphiQL via [`@eeeooolll/graphiql`](https://www.npmjs.com/package/@eeeooolll/graphiql) — Svelte 5, CodeMirror 6, served as a prebuilt IIFE bundle from jsDelivr.
- Ships typed; passes `deno check entry.ts` with no fuss.
- Zero build step — it’s Deno, you just import it.
## GraphiQL renderer options
`playgroundOptions` is forwarded to the HTML shell builder. All fields are optional:
| Field | Type | Description |
| ------------ | ---------------- | ------------------------------------------------------------------------ |
| `cdnUrl` | `string` | CDN base. Defaults to `//cdn.jsdelivr.net/npm`. |
| `faviconUrl` | `string \| null` | `null` skips the favicon; `undefined` uses the bundle's default. |
| `title` | `string` | Document `<title>`. Defaults to `"GraphiQL"`. |
| `version` | `string` | Pin `@eeeooolll/graphiql` to a specific version on the CDN. Recommended. |
Pinning `version` is recommended — without it the shell hits jsDelivr's `@latest` cache, which can lag behind new releases by ~12h.
## Subscriptions
Pair `GraphQLWS` with a `PubSub` instance from `graphql-subscriptions` to serve subscriptions over WebSockets on the same endpoint as HTTP:
```ts
import {
executeSchema,
gql,
GraphQLHTTP,
GraphQLWS,
PubSub
} from "@eol/gq";
const pubsub = new PubSub();
const schema = executeSchema({
resolvers: {
Mutation: {
ping: (_, { msg }) => {
pubsub.publish("PING", { pinged: msg });
return msg;
}
},
Subscription: {
pinged: { subscribe: () => pubsub.asyncIterator(["PING"]) }
}
},
typeDefs: gql`
type Mutation { ping(msg: String!): String }
type Subscription { pinged: String }
`
});
const subscriptions = GraphQLWS({ schema });
Deno.serve(
{ port: 4000 },
GraphQLHTTP({
graphiql: true,
schema,
subscriptions
})
);
```
Clients connect WebSockets to the same URL using the `graphql-transport-ws` subprotocol. The bundled GraphiQL detects subscription operations and switches transports automatically. See `subscription-example.ts` for a runnable demo.
## License
MIT
|