hapijs / hapijs/joi

Standard JSON Schema: bugs and missing conversions

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

Description

### Runtime

node.js

### Runtime version

24.x

### Module version

18.1.0

### Used with

standalone

### Any other relevant information

re #3096 #3102 #3107

These are gaps I found while using the Standard JSON Schema output added in #3102 and improved in #3107. Some are bugs producing incorrect/invalid output, some are missing handlers that have straightforward JSON Schema Draft 2020-12 equivalents.

### What problem are you trying to solve?

Two bugs and several missing conversions in the `~standard.jsonSchema` output:

**Bugs:**

**1. `alternatives.match('all')` produces `anyOf` instead of `allOf`**

```js
const schema = Joi.alternatives().try(Joi.string(), Joi.number()).match('all');
const json = schema['~standard'].jsonSchema.input();
// Actual: { anyOf: [...] }
// Expected: { allOf: [...] }
```

The code in `alternatives.js` only checks for `match('one')` -> `oneOf` and falls through to `anyOf` for everything else:

```js
const matchMode = schema._flags.match ?? 'any';
if (matchMode === 'one') {
res.oneOf = matches;
}
else {
res.anyOf = matches; // 'all' ends up here too
}
```

**2. Ref arguments produce invalid JSON Schema**

When a rule receives `Joi.ref()` instead of a literal value, the Ref object gets assigned directly as the JSON Schema keyword value:

```js
const schema = Joi.number().min(Joi.ref('other'));
const json = schema['~standard'].jsonSchema.input();
// Produces: { type: 'number', minimum: { ref: ... } }
// This is not valid JSON Schema
```

Number, string, and array rule handlers don't check whether the arg is a Ref before assigning it. Date rules partially handle this by checking `date instanceof Date`, but other types don't guard at all.

Since refs resolve against runtime values (not schema definitions), there's no standard JSON Schema equivalent. Draft 2020-12 has no `$data` keyword or cross-field reference mechanism. The fix would be to skip emitting the constraint when the arg is a Ref, similar to how the date rules already skip when the arg isn't a Date instance.

**Missing conversions:**

3. `_invalids` not handled: `Joi.string().invalid('foo', 'bar')` produces no constraint. Could be `not: { enum: ['foo', 'bar'] }`.

4. `presence: 'forbidden'` not handled: `Joi.object({ secret: Joi.forbidden() })` includes `secret` in `properties` as a normal optional field.

5. `result: 'strip'` not handled in output mode: stripped fields still appear in the output JSON Schema despite being removed from the actual output.

6. Object dependency rules (`with`/`without`/`and`/etc.) not emitted. `with`/`without` map cleanly to `dependentRequired` in Draft 2020-12:
```js
Joi.object({ a: Joi.string(), b: Joi.string() }).with('a', 'b')
// Could produce: { ..., dependentRequired: { a: ['b'] } }
```

7. `string.alphanum` missing handler. Could be `pattern: '^[a-zA-Z0-9]+$'`.

8. `number.precision` missing handler. Could approximate with `multipleOf` (e.g., precision 2 -> `multipleOf: 0.01`).

**Question: additional target support?**

The Standard JSON Schema spec [strongly recommends](https://github.com/standard-schema/standard-schema/blob/main/packages/spec/src/index.ts) supporting both `draft-2020-12` and `draft-07`, and lists `openapi-3.0` as a best-effort target:

```typescript
type Target = "draft-2020-12" | "draft-07" | "openapi-3.0" | ({} & string);
```

Currently Joi only supports `draft-2020-12` and throws on everything else. Is there interest in adding `draft-07` support? The main differences would be `prefixItems` -> `items`/`additionalItems`, and `$defs` -> `definitions`. `openapi-3.0` would be a bigger lift since it's based on draft-04 with extensions.

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

Happy to work on PRs for any of these. The bugs (1, and 2) seem like the highest priority since they produce incorrect or invalid output.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.