Giter VIP home page Giter VIP logo

fanout-graphql-tools's Introduction

fanout-graphql-tools

Modules that help make GraphQL servers that work with Fanout Cloud.

See fanout/apollo-serverless-demo for an example project that uses this to power a GraphQL API server with GraphQL Subscriptions on AWS Lambda.

Fanout Cloud can act as a reverse proxy between your users' web browsers and your GraphQL API, holding open long-running WebSocket connections so your server (or function-as-a-service) doesn't have to. Instead, Fanout Cloud makes simple regular HTTP Requests to your application using the WebSocket-Over-HTTP Protocol. The tools in this library allow your GraphQL API server to serve the GraphQL Subscriptions protocol (graphql-ws) over WebSocket-Over-HTTP.

Usage

With apollo-server

Let's say you already have a project that uses apollo-server to make a GraphQL API with subscriptions. Follow these steps to make it work with Fanout.

  1. Make some decisions about your data stores. These tools require a persistent place to store data of two types: GraphQL PubSub Subscriptions as well as WebSocket-Over-HTTP Connections. You must provide storage objects that implement the ISimpleTable interface.

    • You can make your own and store data wherever you want.

    • This interface is a subset of the @pulumi/cloud.Table interface, so you can use those too. Pulumi has implementations for AWS DyanmoDB as well as Azure Table Storage.

    • If you want to use another data store not listed here and need help, file an issue to let us know.

    • When developing, you can use MapSimpleTable, which stores data in-memory in a Map object. But the data won't be very persistent.

      Here's an example of creating these storage objects using MapSimpleTable.

      import { MapSimpleTable, IStoredPubSubSubscription, IStoredConnection } from "fanout-graphql-tools"
      
      const connectionStorage = MapSimpleTable<IStoredConnection>()
      const pubSubSubscriptionStorage = MapSimpleTable<IStoredPubSubSubscription>()
  2. Use WebSocketOverHttpContextFunction when constructing ApolloServer. This adds some properties to your GraphQL Context that can later be used in your GraphQL Resolvers.

    import { WebSocketOverHttpContextFunction } from "fanout-graphql-tools"
    // you may get ApolloServer from elsewhere, e.g. apollo-server-express
    import { ApolloServer } from "apollo-server"
    import { makeExecutableSchema } from "graphql-tools";
    import MyGraphqlApi from "./my-graphql-api"
    
    // these depend on your specific API, e.g. https://github.com/apollographql/apollo-server#installation-standalone
    const { typeDefs, resolvers } = MyGraphqlApi()
    const schema = makeExecutableSchema({ typeDefs, resolvers })
    
    const apolloServer = ApolloServer({
      context: WebSocketOverHttpContextFunction({
        grip: {
          // Get this from your Fanout Cloud console, which looks like https://api.fanout.io/realm/{realm-id}?iss={realm-id}&key=base64:{realm-key}
          // or use this localhost for your own pushpin.org default installation
          url: process.env.GRIP_URL || "http://localhost:5561",
        },
        pubSubSubscriptionStorage,
        schema,
      }),
      schema,
    })

    You can see a full example of this here

  3. In your GraphQL Resolvers, wrap all usages of pubsub with WebSocketOverHttpPubsubMixin(context)(pubsub).

    Every ApolloServer has to be created with some GraphQL Resolvers. To power GraphQL Subscriptions, these resolvers make use of a PubSubEngine. In mutation resolvers, you call pubsub.publish(triggerName, payload). In your subscription resolvers, you call pubsub.asyncIterator(triggerName).

    Here's a before/after example

    • Before (example from the official Apollo docs on subscriptions.)

      const resolvers = {
        Subscription: {
          postAdded: {
            // Additional event labels can be passed to asyncIterator creation
            subscribe: () => pubsub.asyncIterator([POST_ADDED]),
          },
        },
        Mutation: {
          addPost(root: any, args: any, context: any) {
            pubsub.publish(POST_ADDED, { postAdded: args });
            return postController.addPost(args);
          },
        },
      };
    • After wrapping pubsubs with WebSocketOverHttpPubsubMixin(context)(pubsub)

      import { WebSocketOverHttpPubsubMixin } from "fanout-graphql-tools"
      const resolvers = {
        Subscription: {
          postAdded: {
            // Additional event labels can be passed to asyncIterator creation
            subscribe: (source, args, context) => WebSocketOverHttpPubsubMixin(context)(pubsub).asyncIterator([POST_ADDED]),
          },
        },
        Mutation: {
          addPost(root: any, args: any, context: any) {
            WebSocketOverHttpPubsubMixin(context)(pubsub).publish(POST_ADDED, { postAdded: args });
            return postController.addPost(args);
          },
        },
      };

      You can see a full example of this in SimpleGraphqlApi

  4. Add WebSocket-Over-HTTP handling to the http server that serves your GraphQL App. The way to do this depends on how you make an HTTP Server.

    • apollo-server-express

      Many projects use ApolloServer along with the Express web framework. There is an official apollo-server integration called apollo-server-express. You can add WebSocket-Over-HTTP handling to your epxress app with GraphqlWsOverWebSocketOverHttpExpressMiddleware.

      import * as express from "express"
      import { GraphqlWsOverWebSocketOverHttpExpressMiddleware } from "fanout-graphql-tools"
      import { makeExecutableSchema } from "graphql-tools";
      import MyGraphqlApi from "./my-graphql-api"
      
      // these depend on your specific API, e.g. https://github.com/apollographql/apollo-server#installation-standalone
      const { typeDefs, resolvers } = MyGraphqlApi()
      const schema = makeExecutableSchema({ typeDefs, resolvers })
      const app = express()
        .use(GraphqlWsOverWebSocketOverHttpExpressMiddleware({
          // we created these earlier, remember?
          connectionStorage,
          pubSubSubscriptionStorage,
          schema,
        }))
      
      // later, do `ApolloServer(/*...*/).applyMiddleware({ app })

      You can see a full example of this here

    • Other web frameworks

      Not everyone uses express. That's fine. We still want to work with your project. Many node.js web frameworks ultimately end up using the http module from the standard library behind the scenes. If your web framework gives you a reference to an underlying http.Server instance, you can use GraphqlWsOverWebSocketOverHttpSubscriptionHandlerInstaller to install WebSocket-Over-HTTP handling to it.

      import * as http from "http"
      import { GraphqlWsOverWebSocketOverHttpSubscriptionHandlerInstaller } from "fanout-graphql-tools"
      import { makeExecutableSchema } from "graphql-tools";
      import MyGraphqlApi from "./my-graphql-api"
      
      // these depend on your specific API, e.g. https://github.com/apollographql/apollo-server#installation-standalone
      const { typeDefs, resolvers } = MyGraphqlApi()
      const schema = makeExecutableSchema({ typeDefs, resolvers })
      
      // However you get here, e.g. with https://github.com/zeit/micro
      const httpServer: http.Server = http.createServer(requestListener)
      
      GraphqlWsOverWebSocketOverHttpSubscriptionHandlerInstaller({
        connectionStorage,
        pubSubSubscriptionStorage,
        schema,
      })(httpServer);

      Take a look at the micro example for a working example of this with an http.Server created from micro and apollo-server-micro.

    • Have a question about this part? File an issue and we can help out and add to the docs.

Those are the steps for using fanout-graphql-tools. See apollo-serverless-demo for a fully functional app, running in AWS Lambda and storing data in DynamoDB.

Development

Releasing New Versions

Release a new version of this package by pushing a git tag with a name that is a semver version like "v0.0.2". Make sure you also update the package.json to have the same version.

The best way to do this is using npm version <newversion>, which will update package.json, then create a git commit, then create a git tag pointing to that git commit. You should run this in the master branch.

After that you can push the commit and new tags using git push --follow-tags.

npm version minor
git push --follow-tags

Travis is configured to test and publish all git tags to npm. You don't need to run npm publish locally.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.