Can I easily port a Rails API to use GrahQL? - ruby-on-rails

I'm building a project where there's an already implemented REST API running in Rails but we want to start using GraphQL. I'm using the 'graphql' gem but everywhere I look seems to imply that I should implement everything all over again in GraphQL, is that so or is there a way to tell GraphQL to use the Rails controllers?
I have queries working but by implementing them straight in GraphQL, I have looked a lot of places but some of the workarounds don't seem to work.
For example:
module Types
class QueryType < Types::BaseObject
include GraphQL::Types::Relay::HasNodeField
include GraphQL::Types::Relay::HasNodesField
# Add root-level fields here.
# They will be entry points for queries on your schema.
field :users, [Types::UserType]
def users
User.all
end
end
end
Is there a way to use something like users#index for example?

Related

Using Postgres Schema with Ruby on Rails

I have a separate server that contains a Ruby on Rails API ( DB is postgres) that will used by multiple, different, applications. I was thinking of using schemas to sort the tables that each application will require and have a "common" schema that contains the tables that the applications will share ( Users, Employees, etc). Each application schema would have roughly 10 tables and the common schema would contain about 15. The current plan is to have about 3-5 apps using this API all using the same common tables.
My question is, is it worth implementing Postgres schemas with Ruby on Rails? I've been looking for some resources on the topic, but there doesn't seem to be much information on it. There are a couple articles written in 2011/2012 but nothing closer to 2018. One of the main things I've been looking for is how to create rails migrations with postgres schemas properly.
I also read a comment from a user stating that they have used postgres and rails separately, but would never use them in conjunction.
It feels like a good idea to use schemas to organize the DB but don't want to go down that road if it will require a lot of manual DB work/maintenance.
Thanks,
This can be easily implemented with Rails, you will have to override the default table name expected by Rails to point to a specific Schema though:
class YourSchemaRecord < ApplicationRecord
self.table_name_prefix = 'name_of_your_schema.'
end
class SomeRecord < ApplicationRecord
end
class YourCommonSchemaRecord < ApplicationRecord
self.table_name_prefix = 'public.'
end
class SomeCommonRecord < YourCommonSchemaRecord
end
About Rails migrations, you can either use plain SQL (my favorite option) or use Rails' built-in method but give the "full path" of the table you want to update the structure:
add_column 'name_of_your_schema.some_records', :some_column, :string

Can ActiveModel::Serializers have namespaced names (nested) like Rails controllers can?

QUESTION:
I do this with my controller:
class Api::Product::V1::LicenseController < ApplicationController
Why can't I do this with my serializer? (or can I?)
class Api::Product::V1::LicenseSerializer < ActiveModel::Serializer
CONTEXT:
I have multiple controllers/routes that correspond to a single model.
And I need to have multiple serializers per model that correspond 1-to-1 with my controllers.
ActiveModel::Serializers allow you to specify a serializer from a controller like this:
render :json => #license_token, :serializer => LicenseSerializer
So why can't I also do this?
render :json => #license_token, :serializer => Api::Product::V1::LicenseSerializer
I am trying to avoid the ugliness of compound names like these, even though I know they will work:
ProductAlphaLicenseSerializer
ProductBravoLicenseSerializer
ProductCharlieLicenseSerializer
Each of my models supports multiple APIs, which is why I want to namespace the serializers. Each model is used differently by each corresponding serializer.
It might help if you explain the error (if any) that you get. I had a similar concern regarding Active Model Serializers, and according to the documentation it appears AMS will only perform automatic serializer lookup in the app/serializers path based on the model class, so namespaced controllers have no bearing on the serializer lookup.
There doesn't appear to be anything that prevents you from specifying any serializer class you want manually, in fact from within your namespaced controller the use of LicenseSerializer should be looking for the namespaced class in the module Api::Product::V1 by default. Have you tried organising your serializers under a proper namespace so that rails class loading will resolve them automatically? eg, put Api::Product::V1::LicenceSerializer in app/serializers/api/product/v1/license_serializer.rb ?
You may also want to look at roar-rails gem which integrates with rails and uses the ruby web framework agnostic ROAR gem which supports two way JSON/XML/JSON+HAL handling using a representer pattern. Be aware that you won't get jbuilder/jsonify like control over the serialization, but if you're looking at AMS I'm guessing you want to be elevated from the detail somewhat. Using ROAR you will get a uniform API based on the representer format you choose and be much closer to a true hypermedia API.
Some of the rationale for the representer/ROAR approach here, here and here.
EDIT: You may also want to consider my to_json implementation. Performance and flexibility with all the current JSON serializer libraries was a significant issue in my project. After experimenting with all the alternatives I ended up developing a clean JSON DSL and collaborating with the Oj author to develop a highly performant string-buffer/stream marshalling API. My to_json gem easily serializes 18,000 complex objects per second on budget hosting servers and has no limitations on the JSON structures that can be generated.

Rails ActiveRecord override default find

I'm building an API wrapper that will query objects from a third-party API and build them into objects to be used in my Rails environment. To do that, I'm building a set of models that use ActiveRecord (for some of its functionality) but are not database backed. I would like to be able to make a call like this:
obj = MyModel.find(1)
And have the code be something like this:
def MyModel.find id
# check for object in cache
# check for object in db
# grab object from API
# return object
end
Am I going to do something horribly wrong if I override the default find method? Am I approaching this in totally the wrong way?
If you are not using a database, then you do not need ActiveRecord. The entire purpose of ActiveRecord is to give you a mapping to a relational database.
I think what you want is for a class to implement certain pieces of what ActiveRecord provides, and Rails 3 has made those pieces into classes that you can include into regular 'ol classes on an as-needed basis. Look at this article for more details: http://www.rubyinside.com/rails-3-0s-activemodel-how-to-give-ruby-classes-some-activerecord-magic-2937.html
For instance, if you only want validations on a class, you can use include ActiveModel::Validations and then you'll get all of the nice error handling and .valid? and validates presence: true kind of behavior you're used to.
I would also suggest the railscast by Ryan Bates: http://railscasts.com/episodes/219-active-model which goes into more detail.

Using ActiveRecord interface for Models backed by external API in Ruby on Rails

I'm trying to use Models in my Rails application that retrieve information from an external API. What I would like to do is access my data models (which may consist of information resulting from multiple API calls) in a way similar to what an ActiveRecord model would provide (specifically associations, and the same style of chain-able query methods).
My initial instinct was to recreate the parts of ActiveRecord that I wanted and incorporate this API. Not wanting to 'reinvent the wheel' and seeing exactly how much work would be required to add more functionality have made me take a step back and reevaluate how to approach this.
I have found ways to use ActiveRecord without a table (see: Railscast #193 Tableless Model and the blog post here) and looked into ActiveRecord. Because ActiveModel only seems to include Validations I'm not sure that's very helpful in this situation. The workaround to using ActiveRecord without a table seems like the best option, but I suspect there's a cleaner way of doing this that I'm just not seeing.
Here is a gist containing some of the code written when I was trying to recreate the ActiveRecord functionality, borrowing heavily from the ActiveRecord source itself.
My question boils down to: I can get the functionality I want (chaining query methods, relations) by either implementing the workaround to ActiveRecord specified above or recreating the functionality myself, but are these really ideal solutions?
Remember that Rails is still just Ruby underneath.
You could represent the external API as instantiated classes within your application.
class Event
def self.find(id)
#...External http call to get some JSON...#
new(json_from_api)
end
def initialize(json)
#...set up your object here...#
end
def attendees
#...external http call to get some JSON and then assemble it
#...into an array of other objects
end
end
So you end up writing local abstractions to create ruby objects from api calls, you can probably also mix in ActiveModel, or Virtus into it, so you can use hash assignment of attributes, and validations for forms etc.
Take a look at an API abstraction I did for the TfL feed for the tube. service_disruption

How to extend models in rails to always include a certain scope

In our multi-tenant, shared db application, I am looking for a way to extend models to always include the account scope as part of their SQL conditions when running a query. Ideally, i'd extend ActiveRecord so i can include something like scoped_by_account in all relevant models.
I've searched around the internet and found one plugin that claims to do just this. Unfortunately, the scoping searches part of the plugin is not working as far as I can tell. The plugin (which can be found here: https://github.com/mconnell/multi_tenant) extends ActiveRecord and uses the following code to 'inject' the additional search condition in every search:
def self.unscoped
...
super.apply_finder_options(:conditions => { account_id => Account.current.id })
end
From what I can tell, this does not succeed in applying the condition to all searches. If I query Project.all it will return all results, ignoring the current account. Only if I use Project.unscoped it will work correctly.
My question:
How can I extend ActiveRecord to include my additional condition in every query it runs for that model?
Thx for your time,
Erwin
Are you looking for default_scope?

Resources