Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full integration example #22

Open
rafbgarcia opened this issue Nov 8, 2017 · 10 comments
Open

Full integration example #22

rafbgarcia opened this issue Nov 8, 2017 · 10 comments

Comments

@rafbgarcia
Copy link

Hey there, really excited to use the new subscription feature!

However after few hours still couldn't make it work, I was wondering if there's an example fully integrating absinthe_phoenix with absinthe-socket.

FWIW I've posted my issue here.

Thanks!

@benwilson512
Copy link
Contributor

Hey! This is a perfectly reasonable request :) We're within a day or two of doing the final release, and it's the need for that release that has blocked the development of a full example.

The package you link to literally just went up, you're just slightly ahead of the curve. We'll have examples up soon.

@tlvenn
Copy link
Member

tlvenn commented Nov 8, 2017

A very quick glance at your gist shows that you are trying to connect the socket on the http endpoint ws://localhost:4001/api/graphql instead of using the socket endpoint ws://localhost:4001/socket.

@rafbgarcia
Copy link
Author

@tlvenn, thanks a lot man, you're right! :D

Thanks @benwilson512, yea I've followed the commits this afternoon, I was probably the first one to download the new @absinthe/socket lol.

Should I close this issue or leave it as a reminder for the full example? Feel free to close it if it's not necessary.

@benwilson512
Copy link
Contributor

We'll leave it up until there are full examples just in case anyone else is also wondering.

@brbharath17
Copy link

brbharath17 commented Dec 13, 2017

@benwilson512
Tried a simple thing with users and posts. Query and mutation works. Subscription doesn't work
What should be the WS Url to be specified in Graphiql .
screen shot 2017-12-13 at 6 39 59 pm

Also please share an example of router.ex
Thanks in advance

@benwilson512
Copy link
Contributor

@brbharath17 Hey there. It looks like it's working fine to me, that's what shows up until something publishes to the subscription.

@benwilson512
Copy link
Contributor

As an aside, I think it will be best to handle generic help requests via slack or the forums. This issue is left open for reference until there is a full end to end example up somewhere, but I don't think it's the place to field questions about how to use Absinthe.

@benwilson512
Copy link
Contributor

Hey @brbharath17. Please take this to the elixir-lang slack or the forums. Github issues are not the place we want to support one on one questions or learning.

@brbharath17
Copy link

@benwilson512 . Sure i ll put it on the slack.
Sorry

@vadimshvetsov
Copy link

I'm not sure that my setup would help somebody but I'm happy to share it although it originates from Absinthe guides. I tried to omit unnecessary details for readability sake.

Versions of dependencies:

Frontend:

Deps:

{
  "@absinthe/socket": "^0.2.1",
  "@absinthe/socket-apollo-link": "^0.2.1",
  "@apollo/client": "^3.1.3",
  "graphql": "^15.3.0",
  "phoenix": "^1.5.4",
  "typescript": "^3.9.3
}

TypeDefs Deps:

{
  "@types/absinthe__socket": "^0.2.0",
  "@types/absinthe__socket-apollo-link": "^0.2.0",
  "@types/phoenix": "^1.5.0",
  "@types/react": "^16.9.35"
}

Umbrella Phoenix Web App Deps:

defp deps do
  [
    {:phoenix, "~> 1.5"},
    {:phoenix_pubsub, "~> 2.0"},
    {:phoenix_ecto, "~> 4.0"},
    {:gettext, "~> 0.11"},
    {:my_app, in_umbrella: true}, # My ecto app
    {:plug_cowboy, "~> 2.3"},
    {:corsica, "~> 1.0"},
    {:absinthe, "~> 1.5"},
    {:absinthe_plug, "~> 1.5"},
    {:absinthe_phoenix, "~> 2.0"},
    {:poison, "~> 2.1"},
    {:dataloader, "~> 1.0"},
    {:pow, "~> 1.0.20"},
    {:phoenix_swoosh, "~> 0.3.0"},
    {:gen_smtp, "~> 0.13"}
  ]
end

Frontend modules (Typescript)

src/utils/apiClient/websocketLink.ts

import * as AbsintheSocket from '@absinthe/socket'
import { createAbsintheSocketLink } from '@absinthe/socket-apollo-link'
import { Socket as PhoenixSocket } from 'phoenix'

const phoenixSocket = new PhoenixSocket(
  process.env.REACT_APP_SOCKET_URL as string,
  {
    params: () => {
      const accessToken = localStorage.getItem('accessToken')
      return accessToken ? { access_token: accessToken } : {}
    },
  },
)

const absintheSocket = AbsintheSocket.create(phoenixSocket)

export default createAbsintheSocketLink(absintheSocket)

src/utils/apiClient/authLink.ts

import { ApolloLink } from '@apollo/client'

type Headers = {
  authorization?: string
}

const authLink = new ApolloLink((operation, forward) => {
  const accessToken = localStorage.getItem('accessToken')

  operation.setContext(({ headers }: { headers: Headers }) => ({
    headers: {
      ...headers,
      authorization: accessToken,
    },
  }))

  return forward(operation)
})

export default authLink

src/utils/apiClient/index.ts

import { ApolloClient, from, HttpLink, split, ApolloLink } from '@apollo/client'
import { getMainDefinition } from '@apollo/client/utilities'

import websocketLink from './websocketLink'
import errorLink from './errorLink' // In short here I renew token on expired token error
import authLink from './authLink'
import cache from './cache'

const httpLink = new HttpLink({
  uri: process.env.REACT_APP_API_URL,
})

const transportLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query)
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    )
  },
  (websocketLink as unknown) as ApolloLink, // types doesn't work proper with apollo-client v3 at this moment
  from([errorLink, authLink, httpLink]),
)

const apiClient = new ApolloClient({
  link: transportLink,
  cache,
  credentials: 'include',
})

export default apiClient

Elixir modules (Umbrella Phoenix)

config/config.exs

...
config :my_app_web, MyAppWeb.Endpoint,
  url: [host: "localhost"],
  secret_key_base: "hello there",
  render_errors: [view: MyAppWeb.ErrorView, accepts: ~w(json)],
  pubsub_server: MyAppWeb.PubSub
...

apps/my_app_web/lib/my_app_web/application.ex

defmodule MyAppWeb.Application do
  use Application

  ...
  def start(_type, _args) do
    connect_nodes()

    children = [
      MyAppWeb.Endpoint,
      {Phoenix.PubSub, name: MyAppWeb.PubSub},
      {Absinthe.Subscription, MyAppWeb.Endpoint},
    ]

    opts = [strategy: :one_for_one, name: MyAppWeb.Supervisor]
    Supervisor.start_link(children, opts)
  end
  ...
end

apps/my_app_web/lib/my_app_web/endpoint.ex

defmodule MyAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app_web
  use Absinthe.Phoenix.Endpoint

  socket "/socket", MyAppWeb.UserSocket,
    websocket: true,
    longpoll: false
 ...
end

apps/my_app_web/lib/my_app_web/channels/user_socket.ex

defmodule MyAppWeb.UserSocket do
  use Phoenix.Socket
  use Absinthe.Phoenix.Socket, schema: MyAppWeb.Schema

  def connect(params, socket, _connect_info) do
    current_user = current_user(params)

    socket =
      Absinthe.Phoenix.Socket.put_options(socket,
        context: %{
          current_user: current_user
        }
      )

    {:ok, socket}
  end

  defp current_user(%{"access_token" => access_token}) do
    # Here you should get your user token
  end

  defp current_user(_params), do: nil

  def id(_socket), do: nil
end

I'm open to share more details if it would help someone but this guide looks quite complete

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants