hapijs / hapijs/joi

Re-instate .lazy(..) to support Mutually Recursive Schemas?

Open
#2,611 6 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
21.2k
Forks
1.5k
Avg merge
4h 57m
Merged PRs (30d)
14

Description

#### Support plan

* *is this issue currently blocking your project?* (yes/no): yes
* *is this issue affecting a production system?* (yes/no): no

#### Context

* *node version*: 14.15.0
* *module version*: 17.4.0
* *environment* (e.g. node, browser, native): node
* *used with* (e.g. hapi application, another framework, standalone, ...):standalone
* *any other relevant information*:

#### What problem are you trying to solve?

I would like to use JOI for the validation logic for a set of structural types which include some mutually recursive references.

A limited example just to demonstrate the point:

```js
const X = Joi.object({ a: Y });
const Y = Joi.object({ b: X });
```

This obviously won't work as `Y` will be undefined in the first line. This also wouldn't be achievable using `.link(..)` as `.link(..)` (as far as I can tell) only works for referencing things within the one schema's boundaries itself. i.e. I initially tried:

```js
const X = Joi.object({ a: Joi.link('#Y') }).id('X');
const Y = Joi.object({ b: Joi.link('#X') }).id('Y');
```

But, upon, trying to validate something, received the error `Error: "a" contains link reference "ref:local:Y" which is outside of schema boundaries`. This agrees with the [docs here](https://joi.dev/api/?v=17.4.0#:~:text=note%20that%20named%20links%20must%20be%20found%20in%20a%20direct%20ancestor%20of%20the%20link.%20the%20names%20are%20searched%20by%20iterating%20over%20the%20chain%20of%20schemas%20from%20the%20current%20schema%20to%20the%20root.%20to%20reach%20an%20uncle%20or%20cousin%2C%20you%20must%20use%20the%20name%20of%20a%20common%20ancestor%20such%20as%20a%20grandparent%20and%20then%20walk%20down%20the%20tree).

In v15, there was a function `.lazy(..)` which supports this approach. It would allow:

```js
const X = Joi.object({ a: Joi.lazy(() => Y) });
const Y = Joi.object({ b: X });
```

`.lazy(..)` was deleted in v16 ([release notes](https://github.com/sideway/joi/issues/2037))

To help give more context, I'm writing something that generates JOI validation schemas out of schema.org. schema.org models have many mutually recursive references e.g. https://schema.org/Enumeration references https://schema.org/Class (in `supersededBy`), which references https://schema.org/Enumeration again, etc etc.

#### Do you have a new or modified API suggestion to solve the problem?

Re-introducing `.lazy(..)` as it was in v15 (https://joi.dev/api/?v=15.1.1#lazyfn-options---inherits-from-any).

Contributor guide

Open the contributing guide

Research direction

Start by comparing the v15 `.lazy(..)` API documentation with the v16 removal discussed in issue #2037, then review the current link and schema-boundary behavior described in the issue. Done means a supported API allows mutually recursive schemas such as X and Y to be defined and validated without out-of-bound link errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.