Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/yuki24/artemis
Ruby GraphQL client on Rails that actually makes you more productive
https://github.com/yuki24/artemis
graphql graphql-client http2 rails ruby
Last synced: 5 days ago
JSON representation
Ruby GraphQL client on Rails that actually makes you more productive
- Host: GitHub
- URL: https://github.com/yuki24/artemis
- Owner: yuki24
- License: mit
- Created: 2018-09-26T04:19:30.000Z (about 6 years ago)
- Default Branch: main
- Last Pushed: 2024-12-04T23:10:21.000Z (21 days ago)
- Last Synced: 2024-12-13T13:04:58.713Z (12 days ago)
- Topics: graphql, graphql-client, http2, rails, ruby
- Language: Ruby
- Homepage:
- Size: 2.45 MB
- Stars: 208
- Watchers: 5
- Forks: 15
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Artemis [![build](https://github.com/yuki24/artemis/actions/workflows/ruby.yml/badge.svg)](https://github.com/yuki24/artemis/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/artemis.svg)](https://rubygems.org/gems/artemis)
Artemis is a GraphQL client that is designed to fit well on Rails.
* **Convention over Configuration**: You'll never have to make trivial decisions or spend time on boring setup. Start
making a GraphQL request in literally 30s.
* **Performant by default**: You can't do wrong when it comes to performance. All GraphQL files are pre-loaded only
once in production and it'll never affect runtime performance. Comes with options that enable persistent connections
and even HTTP/2, the next-gen high-performance protocol.
* **First-class support for testing**: Testing and stubbing GraphQL requests couldn't be simpler. No need to add
external dependencies to test well.Battle-tested at [Artsy](https://www.artsy.net)
![graphql-client vs Artemis](https://raw.githubusercontent.com/yuki24/artemis/master/banner.png "graphql-client vs Artemis")
## Getting started
Add this line to your application's Gemfile:
```ruby
gem 'artemis'
```Once you run `bundle install` on your Rails app, run the install command:
```sh
$ rails g artemis:install artsy https://metaphysics-production.artsy.net/# or if you need to specify the `--authorization` header:
$ rails g artemis:install github https://api.github.com/graphql --authorization 'token ...'
```### Generating your first query
Artemis comes with a query generator. For exmaple, you could use the query generator to generate a query stub for `artist`:
```sh
$ rails g artemis:query artist
```Then this will generate:
```graphql
# app/operations/artist.graphql
query($id: String!) {
artist(id: $id) {
# Add fields here...
}
}
```Then you could call the class method that has the matching name `artist`:
```ruby
Artsy.artist(id: "pablo-picasso")
# => makes a GraphQL query that's in app/operations/artist.graphql
```You can also specify a file name:
```sh
$ rails g artemis:query artist artist_details_on_artwork
# => generates app/operations/artist_details_on_artwork.graphql
```Then you can make a query in `artist_details_on_artwork.graphql` with:
```ruby
Artsy.artist_details_on_artwork(id: "pablo-picasso")
```## The convention
Artemis assumes that the files related to GraphQL are organized in a certain way. For example, a service that talks to Artsy's GraphQL API could have the following structure:
```
├──app/operations
│ ├── artsy
│ │ ├── _artist_fragment.graphql
│ │ ├── artwork.graphql
│ │ ├── artist.graphql
│ │ └── artists.graphql
│ └── artsy.rb
├──config/graphql.yml
├──test/fixtures/graphql
│ └── artsy
│ ├── artwork.yml
│ ├── artist.yml
│ └── artists.yml
└──vendor/graphql/schema/artsy.json
```### Fragments
Fragments are defined in defined in a standard way in a file named `_artwork_fragment.graphql` with the standard convention:```graphql
fragment on Artwork {
id,
name,
artist_id
# other artwork fields here
}
```The way of calling an Artemis fragment on other queries or models is with a **Rails convention**. Let us suppose we have the Artist model and its corresponding artwork. The way of nesting or calling the artwork on the artist model would look like this:
```graphql
fragment on Artist {
id,
name,
artworks {
...Artsy::ArtworkFragment
}
}
```Where `Artsy` is the name of the folder/module.
## Callbacks
You can use the `before_execute` callback to intercept outgoing requests and the `after_execute` callback to observe the
response. A common operation that's done in the `before_execute` hook is assigning a token to the header:```ruby
class Artsy < Artemis::Client
before_execute do |document, operation_name, variables, context|
context[:headers] = {
Authorization: "token ..."
}
end
end
```Here the `:headers` key is a special context type. The hash object assigned to the `context[:headers]` will be sent as
the HTTP headers of the request.Another common thing when receiving a response is to check if there's any error in the response and throw and error
accordingly:```ruby
class Artsy < Artemis::Client
after_execute do |data, errors, extensions|
raise "GraphQL error: #{errors.to_json}" if errors.present?
end
end
```## Multi domain support
Services like Shopify provide
[a different endpoint per customer](https://shopify.dev/api/admin/graphql/reference#graphql-endpoint) (e.g.
`https://{shop}.myshopify.com`). In order to switch the endpoint on a per-request basis, you will have to use the
`:multi_domain` adapter. This is a wrapper adapter that relies on an actual HTTP adapter such as `:net_http` and
`:curb` so that e.g. it can maintain multiple connections for each endpoint if necessary. This could be configured
as shown below:```yaml
default: &default
# Specify the :multi_domain adapter:
adapter: :multi_domain# Other configurations such as `timeout` and `pool_size` are passed down to the underlying adapter:
timeout: 10
pool_size: 25# Additional adapter-specific configurations could be configured as `adapter_options`:
adapter_options:
# Here you can configure the actual adapter to use. By default, it is set to :net_http. Available adapters are
# :net_http, :net_http_persistent, :curb, and :test. You can not nest the use of the `:multi_domain` adapter.
adapter: :net_httpdevelopment:
shopify:
<<: *default...
```Upon making a request you will also have to specify the `url` option:
```ruby
# Makes a request to https://myawesomeshop.myshopify.com:
Shopify.with_context(url: "https://myawesomeshop.myshopify.com").product(id: "...")
```## Configuration
You can configure the GraphQL client using the following options. Those configurations are found in the
`config/graphql.yml`.| Name | Required? | Default | Description |
| ------------- | --------- | ------------| ----------- |
| `adapter` | No | `:net_http` | The underlying client library that actually makes an HTTP request. See Adapters for available options.
| `pool_size` | No | 25 | The number of keep-alive connections. The `:net_http` adapter will ignore this option.
| `schema_path` | No | See above | The path to the GrapQL schema. Setting an empty value to this will force the client to download the schema upon the first request.
| `timeout` | No | 10 | HTTP timeout set for the adapter in seconds. This will be set to both `read_timeout` and `write_timeout` and there is no way to configure them with a different value as of writing (PRs welcome!)
| `url` | Yes | N/A | The URL for the GraphQL endpoint.### Adapters
There are four adapter options available. Choose the adapter that best fits on your use case.
| Adapter | Protocol | Keep-alive | Performance | Dependencies |
| ---------------------- | ------------------------ | ----------- | ----------- | ------------ |
| `:curb` | HTTP/1.1, **HTTP/2** | **Yes** | **Fastest** | [`curb 0.9.6+`][curb]
[`libcurl 7.64.0+`][curl]
[`nghttp2 1.0.0+`][nghttp]
| `:net_http` (default) | HTTP/1.1 only | No | Slow | **None**
| `:net_http_persistent` | HTTP/1.1 only | **Yes** | **Fast** | [`net-http-persistent 3.0.0+`][nhp]
| `:multi_domain` | See [multi domain support](#multi-domain-support)
| `:test` | See [Testing](#testing)#### Third-party adapters
This is a comminuty-maintained adapter. Want to add yours? Send us a pull request!
| Adapter | Description |
| ---------------------- | ------------|
| [`:net_http_hmac`](https://github.com/JanStevens/artemis-api-auth/tree/master) | provides a new Adapter for the Artemis GraphQL ruby client to support HMAC Authentication using [ApiAuth](https://github.com/mgomes/api_auth). |#### Writing your own adapter
When the built-in adapters do not satisfy your needs, you may want to implement your own adapter. You could do so by following the steps below. Let's implement the [`:net_http_hmac`](https://github.com/JanStevens/artemis-api-auth/tree/master) adapter as an example.
1. Define `NetHttpHmacAdapter` under the `Artemis::Adapters` namespace and implement [the `#execute` method](https://github.com/github/graphql-client/blob/master/guides/remote-queries.md):
```ruby
# lib/artemis/adapters/net_http_hmac_adapter.rb
module Artemis::Adapters
class NetHttpHmacAdapter
def execute(document:, operation_name: nil, variables: {}, context: {})
# Makes an HTTP request for GraphQL query.
end
end
end
```2. Load the adapter in `config/initializers/artemis.rb` (or any place that gets loaded before Rails runs initializers):
```ruby
require 'artemis/adapters/net_http_hmac_adapter'
```3. Specify the adapter name in `config/graphql.yml`:
```yml
default: &default
adapter: :net_http_hmac
```## Rake tasks
Artemis also adds a useful `rake graphql:schema:update` rake task that downloads the GraphQL schema using the
`Introspection` query.### `graphql:schema:update`
Downloads and saves the GraphQL schema.
| Option Name | Description |
| ------------------ | ------------|
| `SERVICE` | Service name the schema is downloaded from.|
| `AUTHORIZATION` | HTTP `Authorization` header value used to download the schema with.|#### Examples
```
$ rake graphql:schema:update
# => downloads schema from the service. fails if there are multiple services in config/graphql.yml.$ rake graphql:schema:update SERVICE=github AUTHORIZATION="token ..."
# => downloads schema from the `github` service using the HTTP header "AUTHORIZATION: token ..."
```## Testing
Given that you have `app/operations/artsy/artist.graphql` and fixture file for the `artist.yml`:
```yml
# test/fixtures/graphql/artist.yml:
leonardo_da_vinci:
data:
artist:
name: Leonardo da Vinci
birthday: 1452/04/15yayoi_kusama:
data:
artist:
name: Yayoi Kusama
birthday: 1929/03/22
```Then you can stub the request with the `stub_graphql` DSL:
```ruby
stub_graphql(Artsy, :artist, id: "yayoi-kusama").to_return(:yayoi_kusama)
stub_graphql(Artsy, :artist, id: "leonardo-da-vinci").to_return(:leonardo_da_vinci)yayoi_kusama = Artsy.artist(id: "yayoi-kusama")
yayoi_kusama.data.artist.name # => "Yayoi Kusama"
yayoi_kusama.data.artist.birthday # => "1452/04/15"da_vinci = Artsy.artist(id: "leonardo-da-vinci")
da_vinci.data.artist.name # => "Leonardo da Vinci"
da_vinci.data.artist.birthday # => "1452/04/15"
```You can also use JSON instead of YAML. See [example fixtures](https://github.com/yuki24/artemis/tree/master/spec/fixtures/responses)
and [test cases](https://github.com/yuki24/artemis/blob/master/spec/test_helper_spec.rb#L16-L51).### MiniTest
Setting up the test helper with Artemis is very easy and simple. Just add the following code to the
`test/test_helper.rb` in your app:```ruby
# spec/test_helper.rb
require 'artemis/test_helper'class ActiveSupport::TestCase
setup do
graphql_requests.clear
graphql_responses.clear
end
end
```### RSpec
Artemis also comes with a script that wires up helper methods on Rspec. Because it is more common to use the `spec/`
directory to organize spec files in RSpec, the `config.artemis.fixture_path` config needs to point to
`spec/fixtures/graphql`. Other than that, it is very straightforward to set it up:```ruby
# config/application.rb
config.artemis.fixture_path = 'spec/fixtures/graphql'
``````ruby
# Add this to your spec/rails_helper.rb or spec_helper.rb if you don't have rails_helper.rb
require 'artemis/rspec'
```## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/yuki24/artemis. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
## Code of Conduct
Everyone interacting in the Artemis project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/artemis/blob/master/CODE_OF_CONDUCT.md).
[curb]: https://rubygems.org/gems/curb
[curl]: https://curl.haxx.se/docs/http2.html
[nghttp]: https://nghttp2.org/
[nhp]: https://rubygems.org/gems/net-http-persistent