Skip to content

Commit 59638b4

Browse files
committed
docs: Add more doc
1 parent b404a60 commit 59638b4

File tree

5 files changed

+196
-47
lines changed

5 files changed

+196
-47
lines changed

.DS_Store

0 Bytes
Binary file not shown.

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
node_modules/
2+
*.DS_Store

.npmignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
img/
2+
.DS_Store
3+
test/

README.md

+192-47
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# GraphQL For Serverless · [![NPM](https://img.shields.io/npm/v/graphql-serverless.svg?style=flat)](https://www.npmjs.com/package/graphql-serverless) [![Tests](https://travis-ci.org/nicolasdao/graphql-serverless.svg?branch=master)](https://travis-ci.org/nicolasdao/graphql-serverless) [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Neap](https://neap.co/img/made_by_neap.svg)](#this-is-what-we-re-up-to)
22

3-
__*graphql-serverless*__ is a middleware for [_**webfunc**_](https://github.com/nicolasdao/webfunc), that allows to deploy [GraphQL](http://graphql.org/learn/) apis (including an optional [GraphiQL interface](https://github.com/graphql/graphiql)) to the most popular serverless platforms:
3+
__*graphql-serverless*__ is a middleware for [_**webfunc**_](https://github.com/nicolasdao/webfunc), that allows to deploy [GraphQL](http://graphql.org/learn/) apis (including an optional [GraphiQL interface](https://github.com/graphql/graphiql)) to the most popular serverless platforms. _**GraphQl Subscriptions**_ over websocket are also supported out-of-the-box (also supported in GraphiQL). Without changing a single line of code, seamlessly deploy to:
44
- [Zeit Now](https://zeit.co/now) (using express under the hood)
55
- [Google Cloud Functions](https://cloud.google.com/functions/) (incl. Firebase Function)
6-
- [AWS Lambdas](https://aws.amazon.com/lambda) (COMING SOON...)
6+
- [AWS Lambdas](https://aws.amazon.com/lambda)
77
- [Azure Functions](https://azure.microsoft.com/en-us/services/functions/) (COMING SOON...)
88

99
Copy/paste the following in your terminal if you want to run your first GraphQL api ([http://localhost:4000](http://localhost:4000)) including a GraphiQL interface ([http://localhost:4000/graphiql](http://localhost:4000/graphiql)) on your local machine in less than 30 seconds:
@@ -36,81 +36,226 @@ _If you're already logged in, then simply run this:_
3636
npm run deploy:prod
3737
```
3838

39+
# Table Of Contents
40+
> * [Install](#install)
41+
> * [How To Use It](#how-to-use-it)
42+
> - [Basics](#basics)
43+
> - [GraphQl Subscriptions](#graphql-subscriptions)
44+
> - [Customizing GraphiQL](#customizing-graphiql)
45+
> - [Managing GraphQl Errors](#managing-graphql-errors)
46+
> - [Creating Custom Middleware](#creating-custom-middleware)
47+
3948
# Install
4049
```
4150
npm install webfunc graphql-serverless --save
4251
```
4352

4453
# How To Use It
45-
Using the template above (i.e. [graphql-universal-server](https://github.com/nicolasdao/graphql-universal-server.git)) is the easiest way to start a new GraphQL project from scratch. However, if you really want to start on a blank page, simply create an index.js as follow:
54+
## Basics
55+
Using the template above (i.e. [graphql-universal-server](https://github.com/nicolasdao/graphql-universal-server.git)) is the easiest way to start a new GraphQL project from scratch.
56+
57+
However, if you really want to start on a blank page:
58+
59+
1. Create a new npm project: `npm init`
60+
2. Install the following: `npm install graphql-serverless webfunc --save`
61+
3. Create an index.js as follow:
62+
63+
```js
64+
const { graphqlHandler, graphqlError } = require('graphql-serverless')
65+
const { makeExecutableSchema } = require('graphql-tools') // this dependency is automatically included in 'graphql-serverless'
66+
const { app } = require('webfunc')
67+
68+
// STEP 1. Mock some data for this demo.
69+
const productMocks = [
70+
{ id: 1, name: 'Product A', shortDescription: 'First product.' },
71+
{ id: 2, name: 'Product B', shortDescription: 'Second product.' }]
72+
73+
// STEP 2. Creating a basic GraphQl Schema.
74+
const schema = `
75+
type Product {
76+
id: ID!
77+
name: String!
78+
shortDescription: String
79+
}
4680
47-
```js
48-
const { graphqlHandler } = require('graphql-serverless')
49-
const { app } = require('webfunc')
50-
const { makeExecutableSchema } = require('graphql-tools') // this dependency is automatically included in 'graphql-serverless'
81+
type Query {
82+
products(id: Int): [Product]
83+
}
84+
85+
schema {
86+
query: Query
87+
}`
88+
89+
const productResolver = {
90+
Query: {
91+
products(root, { id }, context) {
92+
const results = id ? productMocks.filter(p => p.id == id) : productMocks
93+
if (results.length > 0)
94+
return results
95+
else
96+
throw graphqlError(404, `Product with id ${id} does not exist.`)
97+
}
98+
}
99+
}
100+
101+
const executableSchema = makeExecutableSchema({
102+
typeDefs: schema,
103+
resolvers: productResolver
104+
})
105+
106+
// STEP 3. Creating a GraphQL and a GraphiQl endpoint
107+
const graphqlOptions = {
108+
schema: executableSchema,
109+
graphiql: { // If you do not want to host any GraphiQl web interface, leave this property undefined.
110+
endpoint: '/graphiql'
111+
}
112+
}
113+
114+
// Host a GraphQl API on 2 endpoints: '/' and '/graphiql'. '/graphiql' is used to host the GraphiQL web interface.
115+
// If you're not interested in the GraphiQl web interface, leave the above 'graphqlOptions.graphiql' undefined and
116+
// replace the path following ['/', '/graphiql'] with '/'.
117+
app.all(['/', '/graphiql'], graphqlHandler(graphqlOptions))
118+
119+
// STEP 4. Starting the server
120+
eval(app.listen('app', 4000))
121+
```
122+
123+
4. Then simply run:
124+
```
125+
node index.js
126+
```
127+
128+
This will serve 2 endpoints:
129+
130+
- [http://localhost:4000](http://localhost:4000): This is the GraphQL endpoint that your client can start querying.
131+
- [http://localhost:4000/graphiql](http://localhost:4000/graphiql): This is the GraphiQL Web UI that you can use to test and query your GraphQL server.
132+
133+
>If you need best practices on how to structure your GraphQL project, clone the [graphql-universal-server](https://github.com/nicolasdao/graphql-universal-server.git) project and see by yourself.
134+
135+
## GraphQl Subscriptions
136+
137+
> __WARNING: This feature is only available on [Zeit Now serverless](https://zeit.co/now) or on localhost.__
138+
> Even though _graphql-serverless_ relies on [_webfunc_](https://github.com/nicolasdao/webfunc) to deploy on FaaS solutions like AWS Lambdas or Google Functions, because those hosting platforms do not natively support websocket, GraphQl Subscriptions can't be deployed there.
139+
140+
_graphql-serverless_ exposes a helper method `setupSubscriptions` that can host a websocket endpoint for GraphQl Subscriptions. In the following example, we will slightly modify the code above to:
141+
- (MODIFICATION A) Configure a new websocket endpoint for all subscriptions.
142+
- (MODIFICATION B) Add a PubSub queue so that publisher can publish messages onto topics and subscribers can listen to certain topics so that clients using websocket can receive updates.
143+
- (MODIFICATION C) Add a new GraphQl Mutation to insert a new product. This insert will act as a publisher. It will add a message to the PubSub topic once the product has been successfully inserted.
144+
- (MODIFICATION D) Add a new GraphQl Subscription that listen to a specific topic on the PubSub queue and uses websocket to inform the client that a new product has been inserted.
145+
146+
Install _graphql-subscriptions_: `npm install graphql-subscriptions --save`
51147

52-
const schema = `
53-
type Product {
54-
id: ID!
148+
Update the example above as follow:
149+
```js
150+
// MODIFICATION A - Import the 'setupSubscriptions' helper
151+
const { graphqlHandler, graphqlError, setupSubscriptions } = require('graphql-serverless')
152+
153+
...
154+
155+
// MODIFICATION B - Create a simple local pub/sub (not scalable option, but good enough for a demo)
156+
const { PubSub } = require('graphql-subscriptions')
157+
const pubsub = new PubSub()
158+
159+
...
160+
161+
// MODIFICATION C/D - Add an 'insert product' MUTATION and a 'product inserted' SUBSCRIPTION in the GraphQl schema
162+
const schema = `
163+
input NewProductInput {
55164
name: String!
56165
shortDescription: String
57166
}
58167
59-
type Query {
60-
# ### GET products
61-
#
62-
# _Arguments_
63-
# - **id**: Product's id (optional)
64-
products(id: Int): [Product]
168+
type Mutation {
169+
productInsert(product: NewProductInput!): Product
170+
}
171+
172+
type Subscription {
173+
productInserted: Product
65174
}
66175
67176
schema {
68177
query: Query
178+
mutation: Mutation
179+
subscription: Subscription
69180
}`
70181

71-
const productMocks = [{ id: 1, name: 'Product A', shortDescription: 'First product.' }, { id: 2, name: 'Product B', shortDescription: 'Second product.' }]
72-
const productResolver = {
73-
Query: {
74-
products(root, { id }, context) {
75-
const results = id ? productMocks.filter(p => p.id == id) : productMocks
76-
if (results)
77-
return results
78-
else
79-
throw httpError(404, `Product with id ${id} does not exist.`)
182+
...
183+
184+
// MODIFICATION C/D - Add an 'insert product' MUTATION and a 'product inserted' SUBSCRIPTION in the GraphQl product resolver
185+
const productResolver = {
186+
Query: {...},
187+
188+
Mutation: {
189+
productInsert(root, { product }, context) {
190+
if (!product || !product.name)
191+
throw context.graphqlError('Missing required argument \'product.name\'.')
192+
193+
const newId = productMocks.sort((a,b) => a.id < b.id)[0].id + 1
194+
const newProduct = Object.assign({ id: newId }, product)
195+
productMocks.push(newProduct)
196+
pubsub.publish('productInserted', { productInserted: newProduct })
197+
return newProduct
198+
}
199+
},
200+
201+
Subscription: {
202+
productInserted: {
203+
subscribe: () => pubsub.asyncIterator('productInserted')
204+
}
80205
}
81206
}
82-
}
83207

84-
const executableSchema = makeExecutableSchema({
85-
typeDefs: schema,
86-
resolvers: productResolver
87-
})
208+
...
88209

89-
const graphqlOptions = {
90-
schema: executableSchema,
91-
graphiql: true,
92-
endpointURL: '/graphiql',
93-
context: {} // add whatever global context is relevant to you app
94-
}
210+
// MODIFICATION A - Define the location of the subscriptions endpoint
211+
const graphqlOptions = {
212+
schema: executableSchema,
213+
graphiql: {
214+
endpoint: '/graphiql'
215+
},
216+
subscriptionsEndpoint: '/subscriptions' // this means that the subscription endpoint is 'ws://localhost:4000/subscriptions' if you're deploying locally
217+
}
95218

96-
app.all(['/', '/graphiql'], graphqlHandler(graphqlOptions), () => null)
219+
...
97220

98-
eval(app.listen('app', 4000))
99-
```
221+
// MODIFICATION A - Start the websocket endpoint after the server as started.
222+
// WARNING: This only works for localhost, serverless Zeit Now, but not
223+
// for FaaS like AWS Lambdas, Google Functions, ...
224+
eval(app.listen('app', 4000, () => setupSubscriptions(app.server, graphqlOptions)))
225+
```
100226

101-
Then simply run:
227+
Execute `node index.js` and then browse to [http://localhost:4000/graphiql](http://localhost:4000/graphiql). Start a subscription as follow:
228+
```js
229+
subscription {
230+
productInserted {
231+
id
232+
name
233+
}
234+
}
102235
```
103-
node index.js
236+
237+
At that point, the client is simply listening to any new messages on the 'productInserted' topic. Time to publish a new messages on that topic. Open a new tab and browse again to [http://localhost:4000/graphiql](http://localhost:4000/graphiql). There insert a new product as follow:
238+
```js
239+
mutation {
240+
productInsert(product: {
241+
name: "Product C"
242+
}) {
243+
id
244+
name
245+
}
246+
}
104247
```
105248

106-
This will serve 2 endpoints:
249+
Once the product has been inserted, you should be able to observe that your subscription client has noticed it.
107250

108-
- [http://localhost:4000](http://localhost:4000): This is the GraphQL endpoint that your client can start querying.
109-
- [http://localhost:4000/graphiql](http://localhost:4000/graphiql): This is the GraphiQL Web UI that you can use to test and query your GraphQL server.
251+
## Customizing GraphiQL
110252

111-
>If you need best practices on how to structure your GraphQL project, clone the [graphql-universal-server](https://github.com/nicolasdao/graphql-universal-server.git) project and see by yourself.
253+
## Managing GraphQl Errors
112254

113-
## Contributing
255+
## Creating Custom Middleware
256+
257+
258+
# Contributing
114259
```
115260
npm test
116261
```
@@ -127,6 +272,7 @@ Our other open-sourced projects:
127272
* [__*graphql-serverless*__](https://github.com/nicolasdao/graphql-serverless): GraphQL (incl. a GraphiQL interface) middleware for [webfunc](https://github.com/nicolasdao/webfunc).
128273
* [__*schemaglue*__](https://github.com/nicolasdao/schemaglue): Naturally breaks down your monolithic graphql schema into bits and pieces and then glue them back together.
129274
* [__*graphql-s2s*__](https://github.com/nicolasdao/graphql-s2s): Add GraphQL Schema support for type inheritance, generic typing, metadata decoration. Transpile the enriched GraphQL string schema into the standard string schema understood by graphql.js and the Apollo server client.
275+
* [__*graphql-authorize*__](https://github.com/nicolasdao/graphql-authorize.git): Authorization middleware for [graphql-serverless](https://github.com/nicolasdao/graphql-serverless). Add inline authorization straight into your GraphQl schema to restrict access to certain fields based on your user's rights.
130276

131277
#### React & React Native
132278
* [__*react-native-game-engine*__](https://github.com/bberak/react-native-game-engine): A lightweight game engine for react native.
@@ -135,7 +281,6 @@ Our other open-sourced projects:
135281
#### Tools
136282
* [__*aws-cloudwatch-logger*__](https://github.com/nicolasdao/aws-cloudwatch-logger): Promise based logger for AWS CloudWatch LogStream.
137283

138-
139284
# License
140285
Copyright (c) 2018, Neap Pty Ltd.
141286
All rights reserved.

img/graphql_subscription.gif

344 KB
Loading

0 commit comments

Comments
 (0)