graphql-http 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Denis Badurina
3
+ Copyright (c) GraphQL Contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -5,10 +5,12 @@
5
5
 
6
6
  <h6>Simple, pluggable, zero-dependency, <a href="https://graphql.github.io/graphql-over-http">GraphQL over HTTP Protocol</a> compliant server and client.</h6>
7
7
 
8
- [![Continuous integration](https://github.com/enisdenjo/graphql-http/workflows/Continuous%20integration/badge.svg)](https://github.com/enisdenjo/graphql-http/actions?query=workflow%3A%22Continuous+integration%22) [![graphql-http](https://img.shields.io/npm/v/graphql-http.svg?label=graphql-http&logo=npm)](https://www.npmjs.com/package/graphql-http)
8
+ [![Continuous integration](https://github.com/graphql/graphql-http/workflows/Continuous%20integration/badge.svg)](https://github.com/graphql/graphql-http/actions?query=workflow%3A%22Continuous+integration%22) [![graphql-http](https://img.shields.io/npm/v/graphql-http.svg?label=graphql-http&logo=npm)](https://www.npmjs.com/package/graphql-http)
9
9
 
10
10
  <i>Need subscriptions? Try <b>[graphql-ws](https://github.com/enisdenjo/graphql-ws)</b> or <b>[graphql-sse](https://github.com/enisdenjo/graphql-sse)</b> instead!</i>
11
11
 
12
+ <i>Want a full-featured server? See the <b>[servers section](#servers)</b></i>!
13
+
12
14
  <br />
13
15
  </div>
14
16
 
@@ -51,34 +53,18 @@ const schema = new GraphQLSchema({
51
53
 
52
54
  ```js
53
55
  import http from 'http';
54
- import { createHandler } from 'graphql-http';
56
+ import { createHandler } from 'graphql-http/lib/use/node';
55
57
  import { schema } from './previous-step';
56
58
 
57
- // Create the GraphQL over HTTP handler
59
+ // Create the GraphQL over HTTP Node request handler
58
60
  const handler = createHandler({ schema });
59
61
 
60
- // Create a HTTP server using the handler on `/graphql`
61
- const server = http.createServer(async (req, res) => {
62
- if (!req.url.startsWith('/graphql')) {
63
- return res.writeHead(404).end();
64
- }
65
-
66
- try {
67
- const [body, init] = await handler({
68
- url: req.url,
69
- method: req.method,
70
- headers: req.headers,
71
- body: () =>
72
- new Promise((resolve) => {
73
- let body = '';
74
- req.on('data', (chunk) => (body += chunk));
75
- req.on('end', () => resolve(body));
76
- }),
77
- raw: req,
78
- });
79
- res.writeHead(init.status, init.statusText, init.headers).end(body);
80
- } catch (err) {
81
- res.writeHead(500).end(err.message);
62
+ // Create a HTTP server using the listner on `/graphql`
63
+ const server = http.createServer((req, res) => {
64
+ if (req.url.startsWith('/graphql')) {
65
+ handler(req, res);
66
+ } else {
67
+ res.writeHead(404).end();
82
68
  }
83
69
  });
84
70
 
@@ -98,10 +84,10 @@ $ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \
98
84
  ```js
99
85
  import fs from 'fs';
100
86
  import http2 from 'http2';
101
- import { createHandler } from 'graphql-http';
87
+ import { createHandler } from 'graphql-http/lib/use/node';
102
88
  import { schema } from './previous-step';
103
89
 
104
- // Create the GraphQL over HTTP handler
90
+ // Create the GraphQL over HTTP Node request handler
105
91
  const handler = createHandler({ schema });
106
92
 
107
93
  // Create a HTTP/2 server using the handler on `/graphql`
@@ -110,27 +96,11 @@ const server = http2.createSecureServer(
110
96
  key: fs.readFileSync('localhost-privkey.pem'),
111
97
  cert: fs.readFileSync('localhost-cert.pem'),
112
98
  },
113
- async (req, res) => {
114
- if (!req.url.startsWith('/graphql')) {
115
- return res.writeHead(404).end();
116
- }
117
-
118
- try {
119
- const [body, init] = await handler({
120
- url: req.url,
121
- method: req.method,
122
- headers: req.headers,
123
- body: () =>
124
- new Promise((resolve) => {
125
- let body = '';
126
- req.on('data', (chunk) => (body += chunk));
127
- req.on('end', () => resolve(body));
128
- }),
129
- raw: req,
130
- });
131
- res.writeHead(init.status, init.statusText, init.headers).end(body);
132
- } catch (err) {
133
- res.writeHead(500).end(err.message);
99
+ (req, res) => {
100
+ if (req.url.startsWith('/graphql')) {
101
+ handler(req, res);
102
+ } else {
103
+ res.writeHead(404).end();
134
104
  }
135
105
  },
136
106
  );
@@ -143,35 +113,15 @@ console.log('Listening to port 4000');
143
113
 
144
114
  ```js
145
115
  import express from 'express'; // yarn add express
146
- import { createHandler } from 'graphql-http';
116
+ import { createHandler } from 'graphql-http/lib/use/express';
147
117
  import { schema } from './previous-step';
148
118
 
149
- // Create the GraphQL over HTTP handler
150
- const handler = createHandler({ schema });
151
-
152
- // Create an express app serving all methods on `/graphql`
119
+ // Create a express instance serving all methods on `/graphql`
120
+ // where the GraphQL over HTTP express request handler is
153
121
  const app = express();
154
- app.use('/graphql', async (req, res) => {
155
- try {
156
- const [body, init] = await handler({
157
- url: req.url,
158
- method: req.method,
159
- headers: req.headers,
160
- body: () =>
161
- new Promise((resolve) => {
162
- let body = '';
163
- req.on('data', (chunk) => (body += chunk));
164
- req.on('end', () => resolve(body));
165
- }),
166
- raw: req,
167
- });
168
- res.writeHead(init.status, init.statusText, init.headers).end(body);
169
- } catch (err) {
170
- res.writeHead(500).end(err.message);
171
- }
172
- });
122
+ app.all('/graphql', createHandler({ schema }));
173
123
 
174
- app.listen(4000);
124
+ app.listen({ port: 4000 });
175
125
  console.log('Listening to port 4000');
176
126
  ```
177
127
 
@@ -179,30 +129,15 @@ console.log('Listening to port 4000');
179
129
 
180
130
  ```js
181
131
  import Fastify from 'fastify'; // yarn add fastify
182
- import { createHandler } from 'graphql-http';
132
+ import { createHandler } from 'graphql-http/lib/use/fastify';
183
133
  import { schema } from './previous-step';
184
134
 
185
- // Create the GraphQL over HTTP handler
186
- const handler = createHandler({ schema });
187
-
188
135
  // Create a fastify instance serving all methods on `/graphql`
136
+ // where the GraphQL over HTTP fastify request handler is
189
137
  const fastify = Fastify();
190
- fastify.all('/graphql', async (req, res) => {
191
- try {
192
- const [body, init] = await handler({
193
- url: req.url,
194
- method: req.method,
195
- headers: req.headers,
196
- body: req.body, // fastify reads the body for you
197
- raw: req,
198
- });
199
- res.writeHead(init.status, init.statusText, init.headers).end(body);
200
- } catch (err) {
201
- res.writeHead(500).end(err.message);
202
- }
203
- });
138
+ fastify.all('/graphql', createHandler({ schema }));
204
139
 
205
- fastify.listen(4000);
140
+ fastify.listen({ port: 4000 });
206
141
  console.log('Listening to port 4000');
207
142
  ```
208
143
 
@@ -210,37 +145,49 @@ console.log('Listening to port 4000');
210
145
 
211
146
  ```ts
212
147
  import { serve } from 'https://deno.land/std@0.151.0/http/server.ts';
213
- import { createHandler } from 'https://esm.sh/graphql-http';
148
+ import { createHandler } from 'https://esm.sh/graphql-http/lib/use/fetch';
214
149
  import { schema } from './previous-step';
215
150
 
216
- // Create the GraphQL over HTTP handler
217
- const handler = createHandler<Request>({ schema });
151
+ // Create the GraphQL over HTTP native fetch handler
152
+ const handler = createHandler({ schema });
218
153
 
219
154
  // Start serving on `/graphql` using the handler
220
155
  await serve(
221
- async (req: Request) => {
156
+ (req: Request) => {
222
157
  const [path, _search] = req.url.split('?');
223
- if (!path.endsWith('/graphql')) {
224
- return new Response(null, { status: 404, statusText: 'Not Found' });
158
+ if (path.endsWith('/graphql')) {
159
+ return handler(req);
160
+ } else {
161
+ return new Response(null, { status: 404 });
225
162
  }
226
-
227
- const headers: Record<string, string> = {};
228
- req.headers.forEach((value, key) => (headers[key] = value));
229
- const [body, init] = await handler({
230
- url: req.url,
231
- method: req.method,
232
- headers,
233
- body: () => req.text(),
234
- raw: req,
235
- });
236
- return new Response(body, init);
237
163
  },
238
164
  {
239
- port: 4000,
165
+ port: 4000, // Listening to port 4000
240
166
  },
241
167
  );
168
+ ```
169
+
170
+ ##### With [`Bun`](https://bun.sh/)
242
171
 
243
- // Listening to port 4000
172
+ ```js
173
+ import { createHandler } from 'graphql-http/lib/use/fetch'; // bun install graphql-http
174
+ import { schema } from './previous-step';
175
+
176
+ // Create the GraphQL over HTTP native fetch handler
177
+ const handler = createHandler({ schema });
178
+
179
+ // Start serving on `/graphql` using the handler
180
+ export default {
181
+ port: 4000, // Listening to port 4000
182
+ fetch(req) {
183
+ const [path, _search] = req.url.split('?');
184
+ if (path.endsWith('/graphql')) {
185
+ return handler(req);
186
+ } else {
187
+ return new Response(null, { status: 404 });
188
+ }
189
+ },
190
+ };
244
191
  ```
245
192
 
246
193
  #### Use the client
@@ -555,7 +502,7 @@ const client = createClient({
555
502
  <summary><a href="#deno-client">🔗</a> Client usage in Deno</summary>
556
503
 
557
504
  ```js
558
- import { createClient } from 'graphql-http';
505
+ import { createClient } from 'https://esm.sh/graphql-http';
559
506
 
560
507
  const client = createClient({
561
508
  url: 'http://deno.earth:4000/graphql',
@@ -566,6 +513,43 @@ const client = createClient({
566
513
 
567
514
  </details>
568
515
 
516
+ <details id="bun-client">
517
+ <summary><a href="#bun-client">🔗</a> Client usage in Bun</summary>
518
+
519
+ ```js
520
+ import { createClient } from 'graphql-http'; // bun install graphql-http
521
+
522
+ const client = createClient({
523
+ url: 'http://bun.bread:4000/graphql',
524
+ });
525
+
526
+ // consider other recipes for usage inspiration
527
+ ```
528
+
529
+ </details>
530
+
531
+ <details id="migrating-express-grpahql">
532
+ <summary><a href="#migrating-express-grpahql">🔗</a> Server handler migration from <a href="https://github.com/graphql/express-graphql">express-graphql</a></summary>
533
+
534
+ ```diff
535
+ import express from 'express';
536
+ import { schema } from './my-graphql-schema';
537
+
538
+ -import { graphqlHTTP } from 'express-graphql';
539
+ +import { createHandler } from 'graphql-http/lib/use/express';
540
+
541
+ const app = express();
542
+
543
+ app.use(
544
+ '/graphql',
545
+ - graphqlHTTP({ schema }),
546
+ + createHandler({ schema }),
547
+ );
548
+
549
+ app.listen(4000);
550
+ ```
551
+
552
+ </details>
569
553
  <details id="auth">
570
554
  <summary><a href="#auth">🔗</a> Server handler usage with authentication</summary>
571
555
 
@@ -735,10 +719,39 @@ for (const audit of serverAudits({
735
719
 
736
720
  </details>
737
721
 
722
+ ## Only [GraphQL over HTTP](https://graphql.github.io/graphql-over-http/)
723
+
724
+ This is the official [GraphQL over HTTP spec](https://graphql.github.io/graphql-over-http/) reference implementation and as such follows the specification strictly without any additional features (like file uploads, @stream/@defer directives and subscriptions).
725
+
726
+ Having said this, graphql-http is mostly aimed for library authors and simple server setups, where the requirements are exact to what the aforementioned spec offers.
727
+
728
+ ## [Servers](/implementations)
729
+
730
+ If you want a feature-full server with bleeding edge technologies, you're recommended to use one of the following.
731
+
732
+ | Name | Audit |
733
+ | -------------------------------------------------------------- | ------------------------------------------------------------------ |
734
+ | [graphql-yoga](https://www.the-guild.dev/graphql/yoga-server) | [✅ Fully compliant](/implementations/graphql-yoga/README.md) |
735
+ | [apollo-server](https://www.the-guild.dev/graphql/yoga-server) | [⚠️ Partially compliant](/implementations/apollo-server/README.md) |
736
+ | [mercurius](https://mercurius.dev) | [⚠️ Partially compliant](/implementations/mercurius/README.md) |
737
+ | [graphql-helix](https://www.graphql-helix.com/) | [⚠️ Partially compliant](/implementations/graphql-helix/README.md) |
738
+
738
739
  ## [Documentation](docs/)
739
740
 
740
741
  Check the [docs folder](docs/) out for [TypeDoc](https://typedoc.org) generated documentation.
741
742
 
742
- ## [Want to help?](CONTRIBUTING.md)
743
+ ## [Audits](implementations/)
744
+
745
+ Inspect audits of other implementations in the [implementations folder](implementations/). Adding your implementation is very welcome!
746
+
747
+ ## Want to help?
748
+
749
+ File a bug, contribute with code, or improve documentation? Read up on our guidelines below and drive development with `yarn test --watch` away!
750
+
751
+ This repository is managed by EasyCLA. Project participants must sign the free [GraphQL Specification Membership agreement](https://preview-spec-membership.graphql.org) before making a contribution. You only need to do this one time, and it can be signed by [individual contributors](http://individual-spec-membership.graphql.org/) or their [employers](http://corporate-spec-membership.graphql.org/).
752
+
753
+ To initiate the signature process please open a PR against this repo. The EasyCLA bot will block the merge if we still need a membership agreement from you.
754
+
755
+ You can find [detailed information here](https://github.com/graphql/graphql-wg/tree/main/membership). If you have issues, please email [operations@graphql.org](mailto:operations@graphql.org).
743
756
 
744
- File a bug, contribute with code, or improve documentation? Read up on our guidelines for [contributing](CONTRIBUTING.md) and drive development with `yarn test --watch` away!
757
+ If your company benefits from GraphQL and you would like to provide essential financial support for the systems and people that power our community, please also consider membership in the [GraphQL Foundation](https://foundation.graphql.org/join).
@@ -0,0 +1,58 @@
1
+ /**
2
+ *
3
+ * audit/common
4
+ *
5
+ */
6
+ /**
7
+ * Audit requirement levels as per [RFC2119](https://www.rfc-editor.org/rfc/rfc2119).
8
+ *
9
+ * @category Audits
10
+ */
11
+ export declare type AuditRequirement = 'MUST' | 'SHOULD' | 'MAY';
12
+ /**
13
+ * Audit name starting with the audit requirement level.
14
+ *
15
+ * @category Audits
16
+ */
17
+ export declare type AuditName = `${AuditRequirement} ${string}`;
18
+ /**
19
+ * Actual audit test returning an result.
20
+ *
21
+ * The test function will throw only if the error is fatal.
22
+ *
23
+ * @category Audits
24
+ */
25
+ export interface Audit {
26
+ name: AuditName;
27
+ fn: () => Promise<AuditResult>;
28
+ }
29
+ /**
30
+ * Indicates that the audit was successful.
31
+ *
32
+ * @category Audits
33
+ */
34
+ export interface AuditOk {
35
+ name: AuditName;
36
+ status: 'ok';
37
+ }
38
+ /**
39
+ * Indicates that the audit failed.
40
+ *
41
+ * If the status is `warn`, the audit is not a requirement but rather a recommendation.
42
+ *
43
+ * On the other hand, if the status is `error`, the audit is a requirement and the source
44
+ * is therefore not compliant.
45
+ *
46
+ * @category Audits
47
+ */
48
+ export interface AuditFail {
49
+ name: AuditName;
50
+ status: 'warn' | 'error';
51
+ reason: string;
52
+ }
53
+ /**
54
+ * Result of the performed audit. See `AuditOk` and `AuditFail` for more information.
55
+ *
56
+ * @category Audits
57
+ */
58
+ export declare type AuditResult = AuditOk | AuditFail;
@@ -0,0 +1,2 @@
1
+ export * from './common';
2
+ export * from './server';
@@ -0,0 +1,39 @@
1
+ /**
2
+ *
3
+ * audit/server
4
+ *
5
+ */
6
+ import { Audit, AuditResult } from './common';
7
+ /**
8
+ * Options for server audits required to check GraphQL over HTTP spec conformance.
9
+ *
10
+ * @category Audits
11
+ */
12
+ export interface ServerAuditOptions {
13
+ /**
14
+ * The URL of the GraphQL server for the audit.
15
+ */
16
+ url: string;
17
+ /**
18
+ * The Fetch function to use.
19
+ *
20
+ * For NodeJS environments consider using [`@whatwg-node/fetch`](https://github.com/ardatan/whatwg-node/tree/master/packages/fetch).
21
+ *
22
+ * @default global.fetch
23
+ */
24
+ fetchFn?: unknown;
25
+ }
26
+ /**
27
+ * List of server audits required to check GraphQL over HTTP spec conformance.
28
+ *
29
+ * @category Audits
30
+ */
31
+ export declare function serverAudits(opts: ServerAuditOptions): Audit[];
32
+ /**
33
+ * Performs the full list of server audits required for GraphQL over HTTP spec conformance.
34
+ *
35
+ * Please consult the `AuditResult` for more information.
36
+ *
37
+ * @category Audits
38
+ */
39
+ export declare function auditServer(opts: ServerAuditOptions): Promise<AuditResult[]>;