Gathering detailed insights and metrics for graphql-http
Gathering detailed insights and metrics for graphql-http
Gathering detailed insights and metrics for graphql-http
Gathering detailed insights and metrics for graphql-http
Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite.
npm install graphql-http
81.4
Supply Chain
100
Quality
85.6
Maintenance
100
Vulnerability
100
License
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
319 Stars
294 Commits
24 Forks
10 Watching
3 Branches
14 Contributors
Updated on 20 Nov 2024
Minified
Minified + Gzipped
TypeScript (93.71%)
HTML (3.25%)
JavaScript (3.03%)
Cumulative downloads
Total Downloads
Last day
3.7%
62,992
Compared to previous day
Last week
9.5%
334,770
Compared to previous week
Last month
30.6%
1,531,895
Compared to previous month
Last year
127.9%
12,184,839
Compared to previous year
1
38
Quickly check for compliance? Visit graphql-http.com!
Want a full-featured server? See the servers section!
Need subscriptions? Try graphql-ws or graphql-sse instead!
1yarn add graphql-http
1import { GraphQLSchema, GraphQLObjectType, GraphQLString } from 'graphql'; 2 3/** 4 * Construct a GraphQL schema and define the necessary resolvers. 5 * 6 * type Query { 7 * hello: String 8 * } 9 */ 10const schema = new GraphQLSchema({ 11 query: new GraphQLObjectType({ 12 name: 'Query', 13 fields: { 14 hello: { 15 type: GraphQLString, 16 resolve: () => 'world', 17 }, 18 }, 19 }), 20});
http
1import http from 'http'; 2import { createHandler } from 'graphql-http/lib/use/http'; 3import { schema } from './previous-step'; 4 5// Create the GraphQL over HTTP Node request handler 6const handler = createHandler({ schema }); 7 8// Create a HTTP server using the listener on `/graphql` 9const server = http.createServer((req, res) => { 10 if (req.url.startsWith('/graphql')) { 11 handler(req, res); 12 } else { 13 res.writeHead(404).end(); 14 } 15}); 16 17server.listen(4000); 18console.log('Listening to port 4000');
http2
Browsers might complain about self-signed SSL/TLS certificates. Help can be found on StackOverflow.
1$ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ 2 -keyout localhost-privkey.pem -out localhost-cert.pem
1import fs from 'fs'; 2import http2 from 'http2'; 3import { createHandler } from 'graphql-http/lib/use/http2'; 4import { schema } from './previous-step'; 5 6// Create the GraphQL over HTTP Node request handler 7const handler = createHandler({ schema }); 8 9// Create a HTTP/2 server using the handler on `/graphql` 10const server = http2.createSecureServer( 11 { 12 key: fs.readFileSync('localhost-privkey.pem'), 13 cert: fs.readFileSync('localhost-cert.pem'), 14 }, 15 (req, res) => { 16 if (req.url.startsWith('/graphql')) { 17 handler(req, res); 18 } else { 19 res.writeHead(404).end(); 20 } 21 }, 22); 23 24server.listen(4000); 25console.log('Listening to port 4000');
express
1import express from 'express'; // yarn add express 2import { createHandler } from 'graphql-http/lib/use/express'; 3import { schema } from './previous-step'; 4 5// Create an express instance serving all methods on `/graphql` 6// where the GraphQL over HTTP express request handler is 7const app = express(); 8app.all('/graphql', createHandler({ schema })); 9 10app.listen({ port: 4000 }); 11console.log('Listening to port 4000');
fastify
1import Fastify from 'fastify'; // yarn add fastify 2import { createHandler } from 'graphql-http/lib/use/fastify'; 3import { schema } from './previous-step'; 4 5// Create a fastify instance serving all methods on `/graphql` 6// where the GraphQL over HTTP fastify request handler is 7const fastify = Fastify(); 8fastify.all('/graphql', createHandler({ schema })); 9 10fastify.listen({ port: 4000 }); 11console.log('Listening to port 4000');
Koa
1import Koa from 'koa'; // yarn add koa 2import mount from 'koa-mount'; // yarn add koa-mount 3import { createHandler } from 'graphql-http/lib/use/koa'; 4import { schema } from './previous-step'; 5 6const app = new Koa(); 7app.use(mount('/graphql', createHandler({ schema }))); 8 9app.listen({ port: 4000 }); 10console.log('Listening to port 4000');
uWebSockets.js
1import uWS from 'uWebSockets.js'; // yarn add uWebSockets.js@uNetworking/uWebSockets.js#<version> 2import { createHandler } from 'graphql-http/lib/use/uWebSockets'; 3import { schema } from './previous-step'; 4 5uWS 6 .App() 7 .any('/graphql', createHandler({ schema })) 8 .listen(4000, () => { 9 console.log('Listening to port 4000'); 10 });
Deno
1import { serve } from 'https://deno.land/std@0.151.0/http/server.ts'; 2import { createHandler } from 'https://esm.sh/graphql-http/lib/use/fetch'; 3import { schema } from './previous-step'; 4 5// Create the GraphQL over HTTP native fetch handler 6const handler = createHandler({ schema }); 7 8// Start serving on `/graphql` using the handler 9await serve( 10 (req: Request) => { 11 const [path, _search] = req.url.split('?'); 12 if (path.endsWith('/graphql')) { 13 return handler(req); 14 } else { 15 return new Response(null, { status: 404 }); 16 } 17 }, 18 { 19 port: 4000, // Listening to port 4000 20 }, 21);
Bun
1import { createHandler } from 'graphql-http/lib/use/fetch'; // bun install graphql-http 2import { schema } from './previous-step'; 3 4// Create the GraphQL over HTTP native fetch handler 5const handler = createHandler({ schema }); 6 7// Start serving on `/graphql` using the handler 8export default { 9 port: 4000, // Listening to port 4000 10 fetch(req) { 11 const [path, _search] = req.url.split('?'); 12 if (path.endsWith('/graphql')) { 13 return handler(req); 14 } else { 15 return new Response(null, { status: 404 }); 16 } 17 }, 18};
Netlify Functions
1import { createHandler } from 'graphql-http/lib/use/@netlify/functions'; // yarn add @netlify/functions 2import { schema } from './previous-step'; 3 4// Create the GraphQL over HTTP native fetch handler 5export const handler = createHandler({ schema });
1import { createClient } from 'graphql-http'; 2 3const client = createClient({ 4 url: 'http://localhost:4000/graphql', 5}); 6 7(async () => { 8 let cancel = () => { 9 /* abort the request if it is in-flight */ 10 }; 11 12 const result = await new Promise((resolve, reject) => { 13 let result; 14 cancel = client.subscribe( 15 { 16 query: '{ hello }', 17 }, 18 { 19 next: (data) => (result = data), 20 error: reject, 21 complete: () => resolve(result), 22 }, 23 ); 24 }); 25 26 expect(result).toEqual({ hello: 'world' }); 27})();
Thanks to ruru
, serving GraphiQL is as easy as running:
1npx ruru -SP -p 4001 -e http://localhost:4000/graphql
Open http://localhost:4001 in the browser to use it.
1import { ExecutionResult } from 'graphql'; 2import { createClient, RequestParams } from 'graphql-http'; 3import { getSession } from './my-auth'; 4 5const client = createClient({ 6 url: 'http://hey.there:4000/graphql', 7 headers: async () => { 8 const session = await getSession(); 9 if (session) { 10 return { 11 Authorization: `Bearer ${session.token}`, 12 }; 13 } 14 }, 15}); 16 17function execute<Data, Extensions>( 18 params: RequestParams, 19): [request: Promise<ExecutionResult<Data, Extensions>>, cancel: () => void] { 20 let cancel!: () => void; 21 const request = new Promise<ExecutionResult<Data, Extensions>>( 22 (resolve, reject) => { 23 let result: ExecutionResult<Data, Extensions>; 24 cancel = client.subscribe<Data, Extensions>(params, { 25 next: (data) => (result = data), 26 error: reject, 27 complete: () => resolve(result), 28 }); 29 }, 30 ); 31 return [request, cancel]; 32} 33 34(async () => { 35 const [request, cancel] = execute({ 36 query: '{ hello }', 37 }); 38 39 // just an example, not a real function 40 onUserLeavePage(() => { 41 cancel(); 42 }); 43 44 const result = await request; 45 46 expect(result).toBe({ data: { hello: 'world' } }); 47})();
1import { Observable } from 'relay-runtime'; 2// or 3import { Observable } from '@apollo/client/core'; 4// or 5import { Observable } from 'rxjs'; 6// or 7import Observable from 'zen-observable'; 8// or any other lib which implements Observables as per the ECMAScript proposal: https://github.com/tc39/proposal-observable 9import { createClient } from 'graphql-http'; 10import { getSession } from './my-auth'; 11 12const client = createClient({ 13 url: 'http://graphql.loves:4000/observables', 14 headers: async () => { 15 const session = await getSession(); 16 if (session) { 17 return { 18 Authorization: `Bearer ${session.token}`, 19 }; 20 } 21 }, 22}); 23 24const observable = new Observable((observer) => 25 client.subscribe({ query: '{ hello }' }, observer), 26); 27 28const subscription = observable.subscribe({ 29 next: (result) => { 30 expect(result).toBe({ data: { hello: 'world' } }); 31 }, 32}); 33 34// unsubscribe will cancel the request if it is pending 35subscription.unsubscribe();
1import { GraphQLError } from 'graphql'; 2import { 3 Network, 4 Observable, 5 RequestParameters, 6 Variables, 7} from 'relay-runtime'; 8import { createClient } from 'graphql-http'; 9import { getSession } from './my-auth'; 10 11const client = createClient({ 12 url: 'http://i.love:4000/graphql', 13 headers: async () => { 14 const session = await getSession(); 15 if (session) { 16 return { 17 Authorization: `Bearer ${session.token}`, 18 }; 19 } 20 }, 21}); 22 23function fetch(operation: RequestParameters, variables: Variables) { 24 return Observable.create((sink) => { 25 if (!operation.text) { 26 return sink.error(new Error('Operation text cannot be empty')); 27 } 28 return client.subscribe( 29 { 30 operationName: operation.name, 31 query: operation.text, 32 variables, 33 }, 34 sink, 35 ); 36 }); 37} 38 39export const network = Network.create(fetch);
1import { 2 ApolloLink, 3 Operation, 4 FetchResult, 5 Observable, 6} from '@apollo/client/core'; 7import { print, GraphQLError } from 'graphql'; 8import { createClient, ClientOptions, Client } from 'graphql-http'; 9import { getSession } from './my-auth'; 10 11class HTTPLink extends ApolloLink { 12 private client: Client; 13 14 constructor(options: ClientOptions) { 15 super(); 16 this.client = createClient(options); 17 } 18 19 public request(operation: Operation): Observable<FetchResult> { 20 return new Observable((sink) => { 21 return this.client.subscribe<FetchResult>( 22 { ...operation, query: print(operation.query) }, 23 { 24 next: sink.next.bind(sink), 25 complete: sink.complete.bind(sink), 26 error: sink.error.bind(sink), 27 }, 28 ); 29 }); 30 } 31} 32 33const link = new HTTPLink({ 34 url: 'http://where.is:4000/graphql', 35 headers: async () => { 36 const session = await getSession(); 37 if (session) { 38 return { 39 Authorization: `Bearer ${session.token}`, 40 }; 41 } 42 }, 43});
1import { createClient, NetworkError } from 'graphql-http'; 2 3const client = createClient({ 4 url: 'http://unstable.service:4000/graphql', 5 shouldRetry: async (err: NetworkError, retries: number) => { 6 if (retries > 3) { 7 // max 3 retries and then report service down 8 return false; 9 } 10 11 // try again when service unavailable, could be temporary 12 if (err.response?.status === 503) { 13 // wait one second (you can alternatively time the promise resolution to your preference) 14 await new Promise((resolve) => setTimeout(resolve, 1000)); 15 return true; 16 } 17 18 // otherwise report error immediately 19 return false; 20 }, 21});
1<!doctype html> 2<html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>GraphQL over HTTP</title> 6 <script 7 type="text/javascript" 8 src="https://unpkg.com/graphql-http/umd/graphql-http.min.js" 9 ></script> 10 </head> 11 <body> 12 <script type="text/javascript"> 13 const client = graphqlHttp.createClient({ 14 url: 'http://umdfor.the:4000/win/graphql', 15 }); 16 17 // consider other recipes for usage inspiration 18 </script> 19 </body> 20</html>
1const fetch = require('node-fetch'); // yarn add node-fetch 2const { AbortController } = require('node-abort-controller'); // (node < v15) yarn add node-abort-controller 3const { createClient } = require('graphql-http'); 4 5const client = createClient({ 6 url: 'http://no.browser:4000/graphql', 7 fetchFn: fetch, 8 abortControllerImpl: AbortController, // node < v15 9}); 10 11// consider other recipes for usage inspiration
1import { createClient } from 'https://esm.sh/graphql-http';
2
3const client = createClient({
4 url: 'http://deno.earth:4000/graphql',
5});
6
7// consider other recipes for usage inspiration
1import { createClient } from 'graphql-http'; // bun install graphql-http
2
3const client = createClient({
4 url: 'http://bun.bread:4000/graphql',
5});
6
7// consider other recipes for usage inspiration
1import express from 'express'; 2import { schema } from './my-graphql-schema'; 3 4-import { graphqlHTTP } from 'express-graphql'; 5+import { createHandler } from 'graphql-http/lib/use/express'; 6 7const app = express(); 8 9app.use( 10 '/graphql', 11- graphqlHTTP({ schema }), 12+ createHandler({ schema }), 13); 14 15app.listen(4000);
Authenticate the user within graphql-http
during GraphQL execution context assembly. This is a approach is less safe compared to early authentication (see early authentication in Node) because some GraphQL preparations or operations are executed even if the user is not unauthorized.
1import { createHandler } from 'graphql-http'; 2import { 3 schema, 4 getUserFromCookies, 5 getUserFromAuthorizationHeader, 6} from './my-graphql'; 7 8const handler = createHandler({ 9 schema, 10 context: async (req) => { 11 // process token, authenticate user and attach it to your graphql context 12 const userId = await getUserFromCookies(req.headers.cookie); 13 // or 14 const userId = await getUserFromAuthorizationHeader( 15 req.headers.authorization, 16 ); 17 18 // respond with 401 if the user was not authenticated 19 if (!userId) { 20 return [null, { status: 401, statusText: 'Unauthorized' }]; 21 } 22 23 // otherwise attach the user to the graphql context 24 return { userId }; 25 }, 26});
1import { createHandler } from 'graphql-http'; 2import { schema, getDynamicContext } from './my-graphql'; 3 4const handler = createHandler({ 5 schema, 6 context: async (req, args) => { 7 return getDynamicContext(req, args); 8 }, 9 // or static context by supplying the value directly 10});
1import { parse } from 'graphql'; 2import { createHandler } from 'graphql-http'; 3import { getSchemaForRequest, myValidationRules } from './my-graphql'; 4 5const handler = createHandler({ 6 onSubscribe: async (req, params) => { 7 const schema = await getSchemaForRequest(req); 8 9 const args = { 10 schema, 11 operationName: params.operationName, 12 document: parse(params.query), 13 variableValues: params.variables, 14 }; 15 16 return args; 17 }, 18});
Authenticate the user early, before reaching graphql-http
. This is the recommended approach because no GraphQL preparations or operations are executed if the user is not authorized.
1import { createHandler } from 'graphql-http';
2import {
3 schema,
4 getUserFromCookies,
5 getUserFromAuthorizationHeader,
6} from './my-graphql';
7
8const handler = createHandler({
9 schema,
10 context: async (req) => {
11 // user is authenticated early (see below), simply attach it to the graphql context
12 return { userId: req.raw.userId };
13 },
14});
15
16const server = http.createServer(async (req, res) => {
17 if (!req.url.startsWith('/graphql')) {
18 return res.writeHead(404).end();
19 }
20
21 try {
22 // process token, authenticate user and attach it to the request
23 req.userId = await getUserFromCookies(req.headers.cookie);
24 // or
25 req.userId = await getUserFromAuthorizationHeader(
26 req.headers.authorization,
27 );
28
29 // respond with 401 if the user was not authenticated
30 if (!req.userId) {
31 return res.writeHead(401, 'Unauthorized').end();
32 }
33
34 const [body, init] = await handler({
35 url: req.url,
36 method: req.method,
37 headers: req.headers,
38 body: () =>
39 new Promise((resolve) => {
40 let body = '';
41 req.on('data', (chunk) => (body += chunk));
42 req.on('end', () => resolve(body));
43 }),
44 raw: req,
45 });
46 res.writeHead(init.status, init.statusText, init.headers).end(body);
47 } catch (err) {
48 res.writeHead(500).end(err.message);
49 }
50});
51
52server.listen(4000);
53console.log('Listening to port 4000');
1import http from 'http'; 2import { createHandler } from 'graphql-http/lib/use/http'; 3import processRequest from 'graphql-upload/processRequest.mjs'; // yarn add graphql-upload 4import { schema } from './my-graphql'; 5 6const handler = createHandler({ 7 schema, 8 async parseRequestParams(req) { 9 const params = await processRequest(req.raw, req.context.res); 10 if (Array.isArray(params)) { 11 throw new Error('Batching is not supported'); 12 } 13 return { 14 ...params, 15 // variables must be an object as per the GraphQL over HTTP spec 16 variables: Object(params.variables), 17 }; 18 }, 19}); 20 21const server = http.createServer((req, res) => { 22 if (req.url.startsWith('/graphql')) { 23 handler(req, res); 24 } else { 25 res.writeHead(404).end(); 26 } 27}); 28 29server.listen(4000); 30console.log('Listening to port 4000');
1import express from 'express'; // yarn add express 2import { createHandler } from 'graphql-http/lib/use/express'; 3import processRequest from 'graphql-upload/processRequest.mjs'; // yarn add graphql-upload 4import { schema } from './my-graphql'; 5 6const app = express(); 7app.all( 8 '/graphql', 9 createHandler({ 10 schema, 11 async parseRequestParams(req) { 12 const params = await processRequest(req.raw, req.context.res); 13 if (Array.isArray(params)) { 14 throw new Error('Batching is not supported'); 15 } 16 return { 17 ...params, 18 // variables must be an object as per the GraphQL over HTTP spec 19 variables: Object(params.variables), 20 }; 21 }, 22 }), 23); 24 25app.listen({ port: 4000 }); 26console.log('Listening to port 4000');
1import { fetch } from '@whatwg-node/fetch'; 2import { serverAudits } from 'graphql-http'; 3 4for (const audit of serverAudits({ 5 url: 'http://localhost:4000/graphql', 6 fetchFn: fetch, 7})) { 8 test(audit.name, async () => { 9 const result = await audit.fn(); 10 if (result.status === 'error') { 11 throw result.reason; 12 } 13 if (result.status === 'warn') { 14 console.warn(result.reason); // or throw if you want full compliance (warnings are not requirements) 15 } 16 // result.status === 'ok' 17 }); 18}
1import { serverAudits } from 'npm:graphql-http'; 2 3for (const audit of serverAudits({ 4 url: 'http://localhost:4000/graphql', 5 fetchFn: fetch, 6})) { 7 Deno.test(audit.name, async () => { 8 const result = await audit.fn(); 9 if (result.status === 'error') { 10 throw result.reason; 11 } 12 if (result.status === 'warn') { 13 console.warn(result.reason); // or throw if you want full compliance (warnings are not requirements) 14 } 15 // Avoid leaking resources 16 if ('body' in result && result.body instanceof ReadableStream) { 17 await result.body.cancel(); 18 } 19 }); 20}
Put the above contents in a file and run it with deno test --allow-net
.
This is the official GraphQL over HTTP spec reference implementation and as such follows the specification strictly without any additional features (like playgrounds or GUIs, file uploads, @stream/@defer directives and subscriptions).
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.
If you want a feature-full server with bleeding edge technologies, you're recommended to use one of the following servers.
Their compliance with the GraphQL over HTTP spec is checked automatically and updated regularly.
Check the docs folder out for TypeDoc generated documentation.
Inspect audits of other implementations in the implementations folder. Adding your implementation is very welcome, see how!
File a bug, contribute with code, or improve documentation? Read more in CONTRIBUTING.md.
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.
No vulnerabilities found.
No security vulnerabilities found.