# @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 `/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` 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` | 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` | 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` | Builds the resolver context per WS connection. | | `schema` | `GraphQLSchema` | Required. Executable schema. | | `...` | `Partial` | 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 ``. 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