# Is this possible to do with dry-validation?

**URL:** <https://discourse.dry-rb.org/t/is-this-possible-to-do-with-dry-validation/650>\
**Category:** dry-validation\
**Created:** [November 8, 2018, 9:16pm UTC](https://discourse.dry-rb.org/t/is-this-possible-to-do-with-dry-validation/650 "2018-11-08T21:16:49Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [November 8, 2018, 9:16pm UTC](https://discourse.dry-rb.org/t/is-this-possible-to-do-with-dry-validation/650/1 "2018-11-08T21:16:49Z")

</div>

Hi there, I’m trying to use `Dry::Validation` to validate a [JSON-API](https://jsonapi.org/format/) service and I’m running into a roadblock with the `data` member.

[According to the spec](https://jsonapi.org/format/#document-top-level), `data` must either be a single resource object, or an array of resource objects.

I have a `Resource` validation that looks like

```
Resource = Dry::Validation.JSON do
  configure { config.type_specs = true }

  required(:type, "strict.string").filled
  required(:id, "coercible.integer").filled
  optional(:attributes, Types::Hash)
  optional(:relationships, Types::Hash)
  optional(:links, Types::Links)
  optional(:meta, Types::Hash)
end

```

And now I’m trying to embed this into a larger `Document` validation but I have no idea how to express an object or array validation.

```
Document = Dry::Validation.JSON do
  configure { config.type_specs = true }

  required(:data).each { schema Resource }

  optional(:included).each { schema Resource }

  optional(:links, Types::Links)
  optional(:meta, Types::Hash)

  optional(:jsonapi, Types::Hash)
end

```

I’ve looked at a lot of example validations, and I have never seen one like I need. Is this even possible?

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [November 12, 2018, 9:20am UTC](https://discourse.dry-rb.org/t/is-this-possible-to-do-with-dry-validation/650/2 "2018-11-12T09:20:57Z")

</div>

Something like this:

```ruby
required(:data) { each(schema(Resource)) | schema(Resource) }

```

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [November 12, 2018, 5:14pm UTC](https://discourse.dry-rb.org/t/is-this-possible-to-do-with-dry-validation/650/3 "2018-11-12T17:14:13Z")

</div>

No luck 🙁

Doing what you suggest gives me:

```
Failure/Error: each(schema(Resource)) | schema(Resource)

ArgumentError:
  is_a? predicate arity is invalid

```

I suspect that `each` doesn’t support passing an argument like that… so I tried:

```
required(:data) do
  schema(Resource) | each { schema(Resource) }
end

```

This compiles, however now it fails no matter what I pass in:

```
subject.(data: { id: "123", type: "foo" })
=> Failure({:data=>["data is missing"]})

subject.(data: [{ id: "123", type: "foo" }])
=> Failure({:data=>["data is missing"]})
```

---

<div class="post-metadata">

**Author:** ![alassek](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/alassek/32/314_2.png) [@alassek](https://discourse.dry-rb.org/u/alassek)\
**Post date:** [October 21, 2019, 7:21pm UTC](https://discourse.dry-rb.org/t/is-this-possible-to-do-with-dry-validation/650/4 "2019-10-21T19:21:51Z")

</div>

Followup for any unlucky soul who got here via search engine:

[`dry-schema` 1.0](https://dry-rb.org/gems/dry-schema/1.0/) is _mostly_ able to handle this use-case 🎉

```ruby
class Identity < Dry::Schema::JSON
  define do
    required(:id).filled { str? | int? }
    required(:type).filled(:string)
  end
end

class Resource < Identity
  define do
    optional(:attributes).hash
    optional(:relationships).hash
    optional(:links).hash
    optional(:meta).hash
  end
end

class Document < Dry::Schema::JSON
  define do
    required(:data) do
      array { schema(Resource.new) } | schema(Resource.new)
    end

    optional(:included).array { schema(Resource.new) }
    optional(:links).hash
    optional(:meta).hash
    optional(:jsonapi).hash
  end
end

```

There are two places where `Dry::Schema` still falls short: `relationships` and `links` both can contain arbitrarily-named keys. In `Dry::Types` you would be tempted to express these as `Map` types, but that is currently not supported.

The ability to express dynamic key names is [currently slated for 1.4](https://github.com/dry-rb/dry-schema/issues/37).
