- Notifications
You must be signed in to change notification settings - Fork29
Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite.
License
graphql/graphql-http
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Simple, pluggable, zero-dependency,GraphQL over HTTP spec compliant server, client and audit suite.
Quickly check for compliance? Visitgraphql-http.com!
Want a full-featured server? See theservers section!
Need subscriptions? Trygraphql-ws orgraphql-sse instead!
yarn add graphql-http
import{GraphQLSchema,GraphQLObjectType,GraphQLString}from'graphql';/** * Construct a GraphQL schema and define the necessary resolvers. * * type Query { * hello: String * } */constschema=newGraphQLSchema({query:newGraphQLObjectType({name:'Query',fields:{hello:{type:GraphQLString,resolve:()=>'world',},},}),});
Withhttp
importhttpfrom'http';import{createHandler}from'graphql-http/lib/use/http';import{schema}from'./previous-step';// Create the GraphQL over HTTP Node request handlerconsthandler=createHandler({ schema});// Create a HTTP server using the listener on `/graphql`constserver=http.createServer((req,res)=>{if(req.url.startsWith('/graphql')){handler(req,res);}else{res.writeHead(404).end();}});server.listen(4000);console.log('Listening to port 4000');
Withhttp2
Browsers might complain about self-signed SSL/TLS certificates.Help can be found on StackOverflow.
$ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj'/CN=localhost' \ -keyout localhost-privkey.pem -out localhost-cert.pem
importfsfrom'fs';importhttp2from'http2';import{createHandler}from'graphql-http/lib/use/http2';import{schema}from'./previous-step';// Create the GraphQL over HTTP Node request handlerconsthandler=createHandler({ schema});// Create a HTTP/2 server using the handler on `/graphql`constserver=http2.createSecureServer({key:fs.readFileSync('localhost-privkey.pem'),cert:fs.readFileSync('localhost-cert.pem'),},(req,res)=>{if(req.url.startsWith('/graphql')){handler(req,res);}else{res.writeHead(404).end();}},);server.listen(4000);console.log('Listening to port 4000');
Withexpress
importexpressfrom'express';// yarn add expressimport{createHandler}from'graphql-http/lib/use/express';import{schema}from'./previous-step';// Create an express instance serving all methods on `/graphql`// where the GraphQL over HTTP express request handler isconstapp=express();app.all('/graphql',createHandler({ schema}));app.listen({port:4000});console.log('Listening to port 4000');
Withfastify
importFastifyfrom'fastify';// yarn add fastifyimport{createHandler}from'graphql-http/lib/use/fastify';import{schema}from'./previous-step';// Create a fastify instance serving all methods on `/graphql`// where the GraphQL over HTTP fastify request handler isconstfastify=Fastify();fastify.all('/graphql',createHandler({ schema}));fastify.listen({port:4000});console.log('Listening to port 4000');
WithKoa
importKoafrom'koa';// yarn add koaimportmountfrom'koa-mount';// yarn add koa-mountimport{createHandler}from'graphql-http/lib/use/koa';import{schema}from'./previous-step';constapp=newKoa();app.use(mount('/graphql',createHandler({ schema})));app.listen({port:4000});console.log('Listening to port 4000');
WithuWebSockets.js
importuWSfrom'uWebSockets.js';// yarn add uWebSockets.js@uNetworking/uWebSockets.js#<version>import{createHandler}from'graphql-http/lib/use/uWebSockets';import{schema}from'./previous-step';uWS.App().any('/graphql',createHandler({ schema})).listen(4000,()=>{console.log('Listening to port 4000');});
WithDeno
import{serve}from'https://deno.land/std@0.151.0/http/server.ts';import{createHandler}from'https://esm.sh/graphql-http/lib/use/fetch';import{schema}from'./previous-step';// Create the GraphQL over HTTP native fetch handlerconsthandler=createHandler({ schema});// Start serving on `/graphql` using the handlerawaitserve((req:Request)=>{const[path,_search]=req.url.split('?');if(path.endsWith('/graphql')){returnhandler(req);}else{returnnewResponse(null,{status:404});}},{port:4000,// Listening to port 4000},);
WithBun
import{createHandler}from'graphql-http/lib/use/fetch';// bun install graphql-httpimport{schema}from'./previous-step';// Create the GraphQL over HTTP native fetch handlerconsthandler=createHandler({ schema});// Start serving on `/graphql` using the handlerexportdefault{port:4000,// Listening to port 4000fetch(req){const[path,_search]=req.url.split('?');if(path.endsWith('/graphql')){returnhandler(req);}else{returnnewResponse(null,{status:404});}},};
import{createHandler}from'graphql-http/lib/use/@netlify/functions';// yarn add@netlify/functionsimport{schema}from'./previous-step';// Create the GraphQL over HTTP native fetch handlerexportconsthandler=createHandler({ schema});
import{createClient}from'graphql-http';constclient=createClient({url:'http://localhost:4000/graphql',});(async()=>{letcancel=()=>{/* abort the request if it is in-flight */};constresult=awaitnewPromise((resolve,reject)=>{letresult;cancel=client.subscribe({query:'{ hello }',},{next:(data)=>(result=data),error:reject,complete:()=>resolve(result),},);});expect(result).toEqual({hello:'world'});})();
ServeGraphiQL
Thanks toruru
, serving GraphiQL is as easy as running:
npx ruru -SP -p 4001 -e http://localhost:4000/graphql
Openhttp://localhost:4001 in the browser to use it.
🔗 Client usage with Promise
import{ExecutionResult}from'graphql';import{createClient,RequestParams}from'graphql-http';import{getSession}from'./my-auth';constclient=createClient({url:'http://hey.there:4000/graphql',headers:async()=>{constsession=awaitgetSession();if(session){return{Authorization:`Bearer${session.token}`,};}},});functionexecute<Data,Extensions>(params:RequestParams,):[request:Promise<ExecutionResult<Data,Extensions>>,cancel:()=>void]{letcancel!:()=>void;constrequest=newPromise<ExecutionResult<Data,Extensions>>((resolve,reject)=>{letresult:ExecutionResult<Data,Extensions>;cancel=client.subscribe<Data,Extensions>(params,{next:(data)=>(result=data),error:reject,complete:()=>resolve(result),});},);return[request,cancel];}(async()=>{const[request,cancel]=execute({query:'{ hello }',});// just an example, not a real functiononUserLeavePage(()=>{cancel();});constresult=awaitrequest;expect(result).toBe({data:{hello:'world'}});})();
🔗 Client usage withObservable
import{Observable}from'relay-runtime';// orimport{Observable}from'@apollo/client/core';// orimport{Observable}from'rxjs';// orimportObservablefrom'zen-observable';// or any other lib which implements Observables as per the ECMAScript proposal: https://github.com/tc39/proposal-observableimport{createClient}from'graphql-http';import{getSession}from'./my-auth';constclient=createClient({url:'http://graphql.loves:4000/observables',headers:async()=>{constsession=awaitgetSession();if(session){return{Authorization:`Bearer${session.token}`,};}},});constobservable=newObservable((observer)=>client.subscribe({query:'{ hello }'},observer),);constsubscription=observable.subscribe({next:(result)=>{expect(result).toBe({data:{hello:'world'}});},});// unsubscribe will cancel the request if it is pendingsubscription.unsubscribe();
🔗 Client usage withRelay
import{GraphQLError}from'graphql';import{Network,Observable,RequestParameters,Variables,}from'relay-runtime';import{createClient}from'graphql-http';import{getSession}from'./my-auth';constclient=createClient({url:'http://i.love:4000/graphql',headers:async()=>{constsession=awaitgetSession();if(session){return{Authorization:`Bearer${session.token}`,};}},});functionfetch(operation:RequestParameters,variables:Variables){returnObservable.create((sink)=>{if(!operation.text){returnsink.error(newError('Operation text cannot be empty'));}returnclient.subscribe({operationName:operation.name,query:operation.text, variables,},sink,);});}exportconstnetwork=Network.create(fetch);
🔗 Client usage withApollo
import{ApolloLink,Operation,FetchResult,Observable,}from'@apollo/client/core';import{print,GraphQLError}from'graphql';import{createClient,ClientOptions,Client}from'graphql-http';import{getSession}from'./my-auth';classHTTPLinkextendsApolloLink{privateclient:Client;constructor(options:ClientOptions){super();this.client=createClient(options);}publicrequest(operation:Operation):Observable<FetchResult>{returnnewObservable((sink)=>{returnthis.client.subscribe<FetchResult>({ ...operation,query:print(operation.query)},{next:sink.next.bind(sink),complete:sink.complete.bind(sink),error:sink.error.bind(sink),},);});}}constlink=newHTTPLink({url:'http://where.is:4000/graphql',headers:async()=>{constsession=awaitgetSession();if(session){return{Authorization:`Bearer${session.token}`,};}},});
🔗 Client usage with request retries
import{createClient,NetworkError}from'graphql-http';constclient=createClient({url:'http://unstable.service:4000/graphql',shouldRetry:async(err:NetworkError,retries:number)=>{if(retries>3){// max 3 retries and then report service downreturnfalse;}// try again when service unavailable, could be temporaryif(err.response?.status===503){// wait one second (you can alternatively time the promise resolution to your preference)awaitnewPromise((resolve)=>setTimeout(resolve,1000));returntrue;}// otherwise report error immediatelyreturnfalse;},});
🔗 Client usage in browser
<!doctype html><html><head><metacharset="utf-8"/><title>GraphQL over HTTP</title><scripttype="text/javascript"src="https://unpkg.com/graphql-http/umd/graphql-http.min.js"></script></head><body><scripttype="text/javascript">constclient=graphqlHttp.createClient({url:'http://umdfor.the:4000/win/graphql',});// consider other recipes for usage inspiration</script></body></html>
🔗 Client usage in Node
constfetch=require('node-fetch');// yarn add node-fetchconst{ AbortController}=require('node-abort-controller');// (node < v15) yarn add node-abort-controllerconst{ createClient}=require('graphql-http');constclient=createClient({url:'http://no.browser:4000/graphql',fetchFn:fetch,abortControllerImpl:AbortController,// node < v15});// consider other recipes for usage inspiration
🔗 Client usage in Deno
import{createClient}from'https://esm.sh/graphql-http';constclient=createClient({url:'http://deno.earth:4000/graphql',});// consider other recipes for usage inspiration
🔗 Client usage in Bun
import{createClient}from'graphql-http';// bun install graphql-httpconstclient=createClient({url:'http://bun.bread:4000/graphql',});// consider other recipes for usage inspiration
🔗 Server handler migration fromexpress-graphql
import express from 'express';import { schema } from './my-graphql-schema';-import { graphqlHTTP } from 'express-graphql';+import { createHandler } from 'graphql-http/lib/use/express';const app = express();app.use( '/graphql',- graphqlHTTP({ schema }),+ createHandler({ schema }),);app.listen(4000);
🔗 Server handler usage with authentication
Authenticate the user withingraphql-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.
import{createHandler}from'graphql-http';import{schema,getUserFromCookies,getUserFromAuthorizationHeader,}from'./my-graphql';consthandler=createHandler({ schema,context:async(req)=>{// process token, authenticate user and attach it to your graphql contextconstuserId=awaitgetUserFromCookies(req.headers.cookie);// orconstuserId=awaitgetUserFromAuthorizationHeader(req.headers.authorization,);// respond with 401 if the user was not authenticatedif(!userId){return[null,{status:401,statusText:'Unauthorized'}];}// otherwise attach the user to the graphql contextreturn{ userId};},});
🔗 Server handler usage with custom context value
import{createHandler}from'graphql-http';import{schema,getDynamicContext}from'./my-graphql';consthandler=createHandler({ schema,context:async(req,args)=>{returngetDynamicContext(req,args);},// or static context by supplying the value directly});
🔗 Server handler usage with custom execution arguments
import{parse}from'graphql';import{createHandler}from'graphql-http';import{getSchemaForRequest,myValidationRules}from'./my-graphql';consthandler=createHandler({onSubscribe:async(req,params)=>{constschema=awaitgetSchemaForRequest(req);constargs={ schema,operationName:params.operationName,document:parse(params.query),variableValues:params.variables,};returnargs;},});
🔗 Server handler usage in Node with early authentication (recommended)
Authenticate the user early, before reachinggraphql-http
. This is the recommended approach because no GraphQL preparations or operations are executed if the user is not authorized.
import{createHandler}from'graphql-http';import{schema,getUserFromCookies,getUserFromAuthorizationHeader,}from'./my-graphql';consthandler=createHandler({ schema,context:async(req)=>{// user is authenticated early (see below), simply attach it to the graphql contextreturn{userId:req.raw.userId};},});constserver=http.createServer(async(req,res)=>{if(!req.url.startsWith('/graphql')){returnres.writeHead(404).end();}try{// process token, authenticate user and attach it to the requestreq.userId=awaitgetUserFromCookies(req.headers.cookie);// orreq.userId=awaitgetUserFromAuthorizationHeader(req.headers.authorization,);// respond with 401 if the user was not authenticatedif(!req.userId){returnres.writeHead(401,'Unauthorized').end();}const[body,init]=awaithandler({url:req.url,method:req.method,headers:req.headers,body:()=>newPromise((resolve)=>{letbody='';req.on('data',(chunk)=>(body+=chunk));req.on('end',()=>resolve(body));}),raw:req,});res.writeHead(init.status,init.statusText,init.headers).end(body);}catch(err){res.writeHead(500).end(err.message);}});server.listen(4000);console.log('Listening to port 4000');
🔗 Server handler usage withgraphql-upload andhttp
importhttpfrom'http';import{createHandler}from'graphql-http/lib/use/http';importprocessRequestfrom'graphql-upload/processRequest.mjs';// yarn add graphql-uploadimport{schema}from'./my-graphql';consthandler=createHandler({ schema,asyncparseRequestParams(req){constparams=awaitprocessRequest(req.raw,req.context.res);if(Array.isArray(params)){thrownewError('Batching is not supported');}return{ ...params,// variables must be an object as per the GraphQL over HTTP specvariables:Object(params.variables),};},});constserver=http.createServer((req,res)=>{if(req.url.startsWith('/graphql')){handler(req,res);}else{res.writeHead(404).end();}});server.listen(4000);console.log('Listening to port 4000');
🔗 Server handler usage withgraphql-upload andexpress
importexpressfrom'express';// yarn add expressimport{createHandler}from'graphql-http/lib/use/express';importprocessRequestfrom'graphql-upload/processRequest.mjs';// yarn add graphql-uploadimport{schema}from'./my-graphql';constapp=express();app.all('/graphql',createHandler({ schema,asyncparseRequestParams(req){constparams=awaitprocessRequest(req.raw,req.context.res);if(Array.isArray(params)){thrownewError('Batching is not supported');}return{ ...params,// variables must be an object as per the GraphQL over HTTP specvariables:Object(params.variables),};},}),);app.listen({port:4000});console.log('Listening to port 4000');
🔗 Audit for servers usage inJest environment
import{fetch}from'@whatwg-node/fetch';import{serverAudits}from'graphql-http';for(constauditofserverAudits({url:'http://localhost:4000/graphql',fetchFn:fetch,})){test(audit.name,async()=>{constresult=awaitaudit.fn();if(result.status==='error'){throwresult.reason;}if(result.status==='warn'){console.warn(result.reason);// or throw if you want full compliance (warnings are not requirements)}// result.status === 'ok'});}
🔗 Audit for servers usage inDeno environment
import{serverAudits}from'npm:graphql-http';for(constauditofserverAudits({url:'http://localhost:4000/graphql',fetchFn:fetch,})){Deno.test(audit.name,async()=>{constresult=awaitaudit.fn();if(result.status==='error'){throwresult.reason;}if(result.status==='warn'){console.warn(result.reason);// or throw if you want full compliance (warnings are not requirements)}// Avoid leaking resourcesif('body'inresult&&result.bodyinstanceofReadableStream){awaitresult.body.cancel();}});}
Put the above contents in a file and run it withdeno test --allow-net
.
This is the officialGraphQL 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 theGraphQL over HTTP spec is checked automatically and updated regularly.
Check thedocs folder out forTypeDoc generated documentation.
Inspect audits of other implementations in theimplementations 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 theGraphQL Foundation.
About
Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite.
Topics
Resources
License
Code of conduct
Security policy
Uh oh!
There was an error while loading.Please reload this page.