File: scoping.md

package info (click to toggle)
ruby-graphql 2.2.17-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,584 kB
  • sloc: ruby: 67,505; ansic: 1,753; yacc: 831; javascript: 331; makefile: 6
file content (67 lines) | stat: -rw-r--r-- 2,229 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
---
layout: guide
search: true
section: Authorization
title: Scoping
desc: Filter lists to match the current viewer and context
index: 4
---


_Scoping_ is a complementary consideration to authorization. Rather than checking "can this user see this thing?", scoping takes a list of items filters it to the subset which is appropriate for the current viewer and context.

For similar features, see [Pundit scopes](https://github.com/varvet/pundit#scopes) and [Cancan's `.accessible_by`](https://github.com/cancancommunity/cancancan/wiki/Fetching-Records).

## `scope:` option

Fields accept a `scope:` option to enable (or disable) scoping, for example:

```ruby
field :products, [Types::Product], scope: true
# Or
field :all_products, [Types::Product], scope: false
```

For __list__ and __connection__ fields, `scope: true` is the default. For all other fields, `scope: false` is the default. You can override this by using the `scope:` option.

## `.scope_items(items, ctx)` method

Type classes may implement `.scope_items(items, ctx)`. This method is called when a field has `scope: true`. For example,

```ruby
field :products, [Types::Product] # has `scope: true` by default
```

Will call:

```ruby
class Types::Product < Types::BaseObject
  def self.scope_items(items, context)
    # filter items here
  end
end
```

The method should return a new list with only the appropriate items for the current `context`.

## Bypassing object-level authorization

If you know that any items returned from `.scope_items` should be visible to the current client, you can skip the normal `.authorized?(obj, ctx)` checks by configuring `reauthorize_scoped_objects(false)` in your type definition. For example:

```ruby
class Types::Product < Types::BaseObject
  # Check that singly-loaded objects are visible to the current viewer
  def self.authorized?(object, context)
    object.visible_to?(context[:viewer])
  end

  # Filter any list to only include objects that are visible to the current viewer
  def self.scope_items(items, context)
    items.visible_for(context[:viewer])
  end

  # If an object of this type was returned from `.scope_items`,
  # don't call `.authorized?` with it.
  reauthorize_scoped_objects(false)
end
```