From 6a1ada0ceab00726f3bcbb3b4728a72b2f7eeec2 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 29 Mar 2022 18:04:39 +0100 Subject: [PATCH] Add example of using url_helpers in resolvers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A problem I’ve been struggling with is generating URLs within the resolvers using the current host and port. A common solution is to set `Rails.application.routes.default_url_options`, but this is static and with my current setup I need to be able to return the host actually used within the request (My development environment runs over both http and https, so I can’t just hardcode localhost:3000). A solution I’ve found is to pass the controller’s request into the context, and use the `ActionController::UrlFor` concern. And likewise for Active Storage, `ActiveStorage::SetCurrent` works similarly. I thought it might be useful to share this with anyone else running into this use case. --- guides/faq.md | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/guides/faq.md b/guides/faq.md index 875c2d3e2c..eab9a19553 100644 --- a/guides/faq.md +++ b/guides/faq.md @@ -10,25 +10,62 @@ desc: How to do common tasks Returning Route URLs ==================== -With GraphQL there is less of a need to include resource URLs to other REST resources, however sometimes you want to use Rails routing to include a URL as one of your fields. A common use case would be to build HTML format URLs to render a link in your React UI. In that case you can add the Rails route helpers to the execution context as shown below. +With GraphQL there is less of a need to include resource URLs to other REST resources, however sometimes you want to use Rails routing to include a URL as one of your fields. A common use case would be to build HTML format URLs to render a link in your React UI. In that case you can pass the request to your context, so that the helpers are able to build full URLs based on the incoming host, port and protocol. Example ------- ```ruby class Types::UserType < Types::BaseObject + include ActionController::UrlFor + include Rails.application.routes.url_helpers + # Needed by ActionController::UrlFor to extract the host, port, protocol etc. from the current request + def request + context[:request] + end + # Needed by Rails.application.routes.url_helpers, it will then use the url_options defined by ActionController::UrlFor + def default_url_options + {} + end + field :profile_url, String, null: false def profile_url - context[:routes].user_url(object) + user_url(object) end end -# Add the url helpers to `context`: +# In your GraphQL controller, add the request to `context`: MySchema.execute( params[:query], variables: params[:variables], context: { - routes: Rails.application.routes.url_helpers, - # ... + request: request }, ) ``` + +Returning ActiveStorage blob URLs +================================= +If you are using ActiveStorage and need to return a URL to an attachment blob, you will find that using `Rails.application.routes.url_helpers.rails_blob_url` alone will throw an exception since Rails won't know what host, port or protocol to use in it. +You can include `ActiveStorage::SetCurrent` in your GraphQL controller to pass on this information into your resolvers. + +Example +======= + +```ruby +class GraphqlController < ApplicationController + include ActiveStorage::SetCurrent + ... +end + +class Types::UserType < Types::BaseObject + field :picture_url, String, null: false + def picture_url + Rails.application.routes.url_helpers.rails_blob_url( + object.picture, + protocol: ActiveStorage::Current.url_options[:protocol], + host: ActiveStorage::Current.url_options[:host], + port: ActiveStorage::Current.url_options[:port] + ) + end +end +```