Git Product home page Git Product logo

plug_graphql's Introduction

GraphQL Plug

Build Status Public Slack Discussion

plug_graphql is a Plug integration for the GraphQL Elixir implementation of Facebook's GraphQL.

This Plug allows you to easily mount a GraphQL endpoint in Phoenix. This example project shows you how:

Installation

  1. Make a new Phoenix app, or add it to your existing app.
```sh
mix phoenix.new hello_graphql
cd hello_graphql
```

```sh
git clone https://github.com/graphql-elixir/hello_graphql_phoenix
```
  1. Add plug_graphql to your list of dependencies and applications in mix.exs and install the package with mix deps.get.
```elixir
def application do
  # Add the application to your list of applications.
  # This will ensure that it will be included in a release.
  [applications: [:logger, :plug_graphql]]
end

def deps do
  [{:plug_graphql, "~> 0.3.1"}]
end
```

Usage

  1. Define a simple schema in web/graphql/test_schema.ex:
```elixir
defmodule TestSchema do
  def schema do
    %GraphQL.Schema{
      query: %GraphQL.Type.ObjectType{
        name: "Hello",
        fields: %{
          greeting: %{
            type: %GraphQL.Type.String{},
            args: %{
              name: %{
                type: %GraphQL.Type.String{}
              }
            },
            resolve: {TestSchema, :greeting}
          }
        }
      }
    }
  end

  def greeting(_, %{name: name}, _), do: "Hello, #{name}!"
  def greeting(_, _, _), do: "Hello, world!"
end
```
  1. Your api pipeline should have this as a minimum:
```elixir
pipeline :api do
  plug :accepts, ["json"]
end
```
  1. Mount the GraphQL endpoint as follows:
```elixir
scope "/api" do
  pipe_through :api

  forward "/", GraphQL.Plug, schema: {TestSchema, :schema}
end
```
  1. Start Phoenix
```sh
mix phoenix.server
```
  1. Open your browser to http://localhost:4000/api?query={greeting} and you should see something like this:
```json
{
  "data": {
    "greeting": "Hello, world!"
  }
}
```

Contributions

This is pretty early days, the GraphQL Elixir ecosystem needs a lot more work to be useful.

However we can't get there without your help, so any questions, bug reports, feedback, feature requests and/or PRs are most welcome!

Acknowledgements

Thanks and appreciation goes to the following contributors for PRs, discussions, answering many questions and providing helpful feedback:

Thanks also to everyone who has submitted PRs, logged issues, given feedback or asked questions.

plug_graphql's People

Contributors

adambrodzinski avatar aweiker avatar jamesroseman avatar plasticine avatar sircharleswatson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plug_graphql's Issues

Integration with Auth (Guardian)

I'm attempting to access the currently logged in user during GraphQL resolves. Looking at the pull-request history (specifically #9, it seems I should be able to inject whatever data I need via the root_value so I've defined a module like so:

defmodule MyApp.GraphQLSession do
  def root_eval(_conn) do
    %{conn: _conn}
  end
end

and amended my api pipeline to:

pipeline :api do
    plug :accepts, ["json"]

    plug Guardian.Plug.VerifyHeader
    plug Guardian.Plug.LoadResource

    plug GraphQL.Plug.Endpoint, [schema: {GraphQL.Schema.EctoWorld, :schema}, root_value: &MyApp.GraphQLSession.root_eval/1]
end

scope "/graphql" do
    pipe_through :api

    get  "/ecto", GraphQL.Plug, schema: {GraphQL.Schema.EctoWorld, :schema}
    post "/ecto", GraphQL.Plug, schema: {GraphQL.Schema.EctoWorld, :schema}
end

scope "/api", MyApp do
    pipe_through :api

    ....
    post "/registrations", RegistrationController, :create

    post "/sessions", SessionController, :create
    delete "/sessions", SessionController, :delete

    get "/current_user", CurrentUserController, :show
    ....
end

Note: I'm using Guardian for authentication.

However, whenever I try and hit any endpoint (either via GraphiQL or curl) an exception is raised:

(Plug.Conn.AlreadySentError) the response was already sent

I'm sure I'm missing something fairly simple as I've just started learning Elixir/Phoenix. I was hoping to get any insight into this or perhaps a working example of integrating GraphQL with Guardian.

Please let me know if there is any more information I can provide.

Thanks!

Workaround double `forward` error

Phoenix errors when a second forward is declared to the same Plug. This can be worked around by wrapping the GraphQL.Plug.Endpoint with configuration in a new module Plug.

forward "/hello", GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.HelloWorld, :schema}
forward "/blog",  GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.SimpleBlog, :schema}

Gives this error:

== Compilation error on file web/router.ex ==
** (ArgumentError) `GraphQL.Plug.Endpoint` has already been forwarded to. A module can only be forwarded a single time.
    (phoenix) lib/phoenix/router/route.ex:171: Phoenix.Router.Route.forward_path_segments/3
    web/router.ex:29: (module)
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

Arguably this may be a bug in Phoenix, as this seems like a reasonable thing to do...

There are 2 workarounds:

  1. The plug wrapper workaround (suggested by @chrismccord) which hides the identity of the underlying plug

    # Hello World wrapper
    defmodule HelloWorld do
      plug GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.HelloWorld, :schema}
    end
    
    # Simple Blog wrapper
    defmodule SimpleBlog do
      plug GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.SimpleBlog, :schema}
    end
    
    forward "/hello", HelloWorld
    forward "/blog",  SimpleBlog
  2. This is the get/post workaround which avoids forward altogether, losing ability to handle other HTTP verbs.

    # Hello World example
    get  "/hello", GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.HelloWorld, :schema}
    post "/hello", GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.HelloWorld, :schema}
    
    # Simple Blog example
    get  "/blog",  GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.SimpleBlog, :schema}
    post "/blog",  GraphQL.Plug.Endpoint, schema: {GraphQL.Schema.SimpleBlog, :schema}

The error is fired here and perhaps could account for differing configuration?
https://github.com/phoenixframework/phoenix/blob/master/lib/phoenix/router/route.ex#L170-L172

Test Schema is broken in example

In step 1 of the usage example, the Test Schema is lacking the args property in order for the name to work with the first greeting function.

it should be

  def schema do
    %GraphQL.Schema{
      query: %GraphQL.Type.ObjectType{
        name: "Hello",
        fields: %{
          greeting: %{
            type: %GraphQL.Type.String{},
            args: %{
              name: %{
                type: %GraphQL.Type.String{}
              }
            },
            resolve: {TestSchema, :greeting}
          }
        }
      }
    }
  end

Support GraphQL subscriptions

Still fairly new to Elixir and Phoenix/Plug. Is this the appropriate repo on which to request support for GraphQL subscriptions?

There seems to be lots of work happening in this space recently:

I don't have an opinion/preference as to which approach is better. Maybe they're compatible. But Apollo at least seems to require Redis for multi-node deployments, a need which I imagine Elixir completely removes.

Use conn as context for query execution

It's very important to know who the current user is when you resolve queries. It's pretty easy to assign this information to the conn using plugs before the GraphQL plug, like so:

plug :authenticate_user
plug GraphQL.Endpoint, ...

However, since the conn is not passed into the query execution function, the resolvers have no way to know who the current user is, and therefore authentication seems impossible.

I think the conn should be added as the third argument to this function:

https://github.com/joshprice/plug_graphql/blob/21e42b00cdd9d689f5f048859e66f253d1c8d3e7/lib/graphql/plug/endpoint.ex#L44

The core GraphQL Elixir library may need to be extended to pass the context to all the resolvers.

Am I missing something here?

plug_graphql after `Guardian.Plug.VerifyHeader` not working as expected...

Great library! Really digging GraphQL in Elixir/Phoenix with Ecto, but seem to have a problem with the plug in front of Guardian.Plug.VerifyHeader

Here is a snippet of the router that pipes graphql requests through an API endpoint which should include the guardian plugs...

 pipeline :api do
   plug :accepts, ["json"]
   plug Guardian.Plug.VerifyHeader, realm: "Bearer"
   plug Guardian.Plug.LoadResource
 end

 scope "/api" do
   pipe_through :api
   forward "/", GraphQL.Plug.Endpoint, schema: {MyApp.GraphQL.Schema, :schema}
 end

The GraphQL endpoint seems to respond to the requests even if there is no Authorization header.

Help is much appreciated, thanks!

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.