APIQL
Implementation of the API language similar to GraphQL for Ruby on Rails.
It compiles requests into Hashes for faster rendering.
It automatically detects nested entities and eager-loads them for faster DB access!
Define your responder (requested methods):
class UserAPIQL < ::APIQL
def me
:show, ::User
current_user
end
def authenticate(email, password)
user = ::User.find_by(email)
user.authenticate(password)
user
end
def logout
current_user&.logout
:ok
end
end
In controller or Grape API endpoint, handler of POST /apiql method (or any other, see below APIQL.endpoint):
def apiql
schema = APIQL.cache(params)
UserAPIQL.new(self, :session, :current_user, :params).render(schema)
end
variables session
, current_user
and params
(you can list any you need in your presenters/responders) will be stored into context you can use in presenters and handlers
Define presenters for your models:
class User < ApplicationRecord
class Entity < ::APIQL::Entity
attributes :full_name, :email, :token, :role, :roles # any attributes, methods or associations
def token # if defined, method will be called instead of attribute
object.token if object == current_user # directly access to current_user from context
end
end
has_many :roles
...
end
JS:
assets/javascripts/application.js:
//= require apiql
APIQL.endpoint = "/apiql"
// function apiql(schema, params = {}, form = null) -- schema is cached, so entire request is passed only for first time, later - short hashes only
authenticate(email, password) {
apiql(`
logout()
authenticate(email, password) {
token
}
me {
email full_name role token
roles {
id title
}
}
`, {
email: email, // these vars will be passed into methods on the server side
password: password
})
.then(response => { // response contains results of called methods
let user = response.me
})
}
logout() {
apiql(`
logout
`)
.then(response => {
})
}
you can call methods on entities:
apiql(`
authenticate(email, password) {
token
}
user: me.reload {
email full_name role token
roles(filter) {
id title
}
contacts.primary {
full_name
email
}
}
`, {
email: email,
filter: 'all',
password: password
})
.then(response => {
let user = response.user
})
}
You can use initializer like this for authorization using cancancan gem:
config/initializers/apiql.rb:
class APIQL
class Context
def (*args)
ability.(*args)
end
private
def ability
@ability ||= ::Ability::Factory.build_ability_for(current_user)
end
end
end
you can add CRUD methods for your models:
class UserAPIQL < ::APIQL
model ::User
model ::Role
end
or mount methods from external modules:
class UserAPIQL < ::APIQL
mount ::Services::User # all methouds could be called with "user" prefix like "user.logout()"
mount ::Services::Common, as: '' # all methods could be called without prefixes
mount ::Services::Employer, as: 'dashboard" # all methods could be called with specified prefix
end