graphiti-api / graphiti

Stylish Graph APIs
https://www.graphiti.dev/
MIT License
983 stars 140 forks source link

And you can use whichever client or ORM you’d like for a given Backend (ActiveRecord, Sequel, Mongoid, Net::HTTP, etc). #261

Open brentgreeff opened 4 years ago

brentgreeff commented 4 years ago

I have made some big promises to expose a MongoDB using Graphiti. Looking in the code there is no Mongoid adapter. Am I just using the wrong config or do I need to write this adapter? - How much work is involved in that?

richmolj commented 4 years ago

Hey @brentgreeff - no built-in Mongoid adapter, but I know others have used MongoDB . There's surprisingly little work to write an adapter, though really depends on the complexity of your application and how full-featured it needs to be.

I'd recommend starting with this guide to get an idea of how Graphiti works and how to write your own adapter. I'd also recommend taking a look at the AR adapter here.

The best way to get a sense of the work is to write a resource without an adapter first (the cookbook covers this) then move to an adapter once you nail things down. I think persistence is the biggest hurdle, but I'd imagine you get the majority of reads covered quite quickly.

brentgreeff commented 4 years ago

This adapter gets me a surprising amount of progress:

module Graphiti
  module Adapters
    class Mongoid < ::Graphiti::Adapters::Abstract

      def base_scope(model)
        model.all
      end

      def paginate(scope, current_page, per_page)
        scope.page(current_page).per(per_page)
      end

      def resolve(scope)
        scope.to_a
      end
    end
  end
end

I swapped gem 'kaminari' for 'kaminari-mongoid'.

I have nested objects in my primary Collection. I am using field :store, type: Hash in Mongoid, and attribute :store, :hash in my Resource, and that does work, but I would like to be able to see this as an association - maybe has_one and then query it from Vandal.

richmolj commented 4 years ago

I have nested objects in my primary Collection. I am using field :store, type: Hash in Mongoid, and attribute :store, :hash in my Resource, and that does work, but I would like to be able to see this as an association - maybe has_one and then query it from Vandal.

You can get this to work, but has some downsides and generally not considered a good practice. I often get the question "when should something be a nested attribute, and when should it be a relationship?". And the answer is: make it a relationship when you can deal with this entity independently. So a separate DB table makes sense, but a serialized JSON column does not because you cannot deal with it independently. I don't use Mongo, but I think the same applies here.

There are a few reasons for this, including the fact that we want to be able to fetch something from a DB at one level of the graph and a separate data source at a different level of the graph. To do this, graph nodes cannot be coupled. You also get into very tricky graph scenarios/contexts, adjusting your code depending on the entrypoint instead of everything operating independently.

That said, you can use this lower-level customization to pass the parent model to the child resource:

# PostResource
has_one :author do
  scope do |parent_id, post|
    post
  end
end

In this case, the scope object passed to the AuthorResource will be an instance of the Post model.