# JSON schema and parsing

> What is JSON schema and how does Windmill use it to define script inputs, flow parameters, and resource types?

Windmill leverages the JSON Schema to define the structure and validation rules for JSON data, adhering to the [JSON Schema standard (version 2020-12)](https://json-schema.org/draft/2020-12/schema) for schema definition.

## JSON Schema specification

A JSON Schema defines the properties, types, and constraints of a JSON object. It consists of the following components:

- `$schema`: The URL that points to the JSON Schema specification.
- `type`: The type of the JSON object (e.g., object, string, number).
- `properties`: A dictionary that defines the properties of the JSON object along with their descriptions and types.
- `required`: A list of the mandatory properties.
- Additional features and constraints can be added to the JSON Schema, such as validation against regular expressions or other custom formats.

## JSON Schema in Windmill

In Windmill, the JSON Schema is used in various contexts, such as defining the input specification for scripts and flows, and specifying resource types.

Below is a simplified spec of a JSON Schema. See [here for its full spec](https://json-schema.org/). Windmill is compatible with the [2020-12 version](https://json-schema.org/draft/2020-12/schema). It is not compatible with its most advanced features yet.

```json
{
	"$schema": "https://json-schema.org/draft/2020-12/schema",
	"type": "object",
	"properties": {
		"your_name": {
			"description": "The name to hello world to",
			"type": "string"
		},
		"your_nickname": {
			"description": "If you prefer a nickname, that's fine too",
			"type": "string"
		}
	},
	"required": []
}
```

Where the `properties` field contains a dictionary of arguments, and `required` is the list of all the mandatory arguments.

The property names need to match the arguments declared by the main function, in our example `your_name` and `your_nickname`. There is a lot you can do with arguments, types, and validation, but to keep it short:

- Arguments can specify a type `integer`, `number`, `string`, `boolean`, `object`, `array` or `any`. The user's input will be validated against that type.
- One can further constraint the type by having the string following a RegEx or pattern, or the object to be of a specific [Resource Type](../3_resources_and_types/index.mdx).
- Arguments can be made mandatory by adding them to the `required` list. In that case, the generated UI will check that user input provides required arguments.
- Each argument can have a description and default fields, that will appear in the generated UI.
- Some types have advanced settings.

### Script parameters to JSON Schema

Scripts in Windmill have input parameters defined by a JSON Schema, where each parameter in the main function of a script corresponds to a field in the JSON Schema. This one-to-one correspondence ensures that the name of the argument becomes the name of the property, and most primitive types in Python and TypeScript have a corresponding primitive type in JSON and JSON Schema. During script execution, the parameters and their types are validated against the JSON Schema, ensuring that the input adheres to the expected format.

In [Python](../../getting_started/0_scripts_quickstart/2_python_quickstart/index.mdx):

| Python                           | JSON Schema                      |
| -------------------------------- | -------------------------------- |
| `str`                            | `string`                         |
| `float`                          | `number`                         |
| `Literal["a", "b"]`              | `string` with enums: "a", "b"    |
| `int`                            | `integer`                        |
| `bool`                           | `boolean`                        |
| `dict`                           | `object`                         |
| `list`                           | `any[]`                          |
| `List[str]`                      | `string[]`                       |
| `bytes`                          | `string, encodingFormat: base64` |
| `datetime`                       | `str, format: date-time`         |
| `_`                              | `any`                            |
| [DynSelect_foo](#dynamic-select) | `dynselect-<name>`               |

In [Deno, Bun](../../getting_started/0_scripts_quickstart/1_typescript_quickstart/index.mdx), [REST](../../getting_started/0_scripts_quickstart/6_rest_grapqhql_quickstart/index.mdx):

| TypeScript                       | JSON Schema                   |
| -------------------------------- | ----------------------------- |
| `string`                         | `string`                      |
| `"a" \| "b"`                     | `string` with enums: "a", "b" |
| `object`                         | `object`                      |
| `boolean`                        | `boolean`                     |
| `bigint`                         | `int`                         |
| `number`                         | `number`                      |
| `string[]`                       | `string[]`                    |
| `("foo" \| "bar")[]`             | `enum[]`                      |
| [oneOf](#oneof)                  | `object`                      |
| [DynSelect_foo](#dynamic-select) | `dynselect-<name>`            |

However in TypeScript there also some special types that are specific to Windmill.
They are as follows:

| Windmill         | JSON Schema                                  |
| ---------------- | -------------------------------------------- |
| `wmill.Base64`   | `string`, encoding$$Format: `base64`         |
| `wmill.Email`    | `string`, format: `email`                    |
| `wmill.Sql`      | `string`, format: `sql`                      |
| `<ResourceType>` | `object`, format: `resource-{resource_type}` |

The `<ResourceType>` is any type that has a matching resource_type in the workspace (more details [here](../3_resources_and_types/index.mdx#using-resources)). Note that the CamelCase of the type is converted to the snake_case.
`Base64` and `Email` are actually a type alias for `string`, and `Resource` is a
type alias for an `object`. They are purely type hints for the Windmill parser.

The `sql` format is specific to Windmill and replaces the normal text field with
a monaco editor with SQL support.

:::info

The equivalent of the type `Postgresql` in Python is the
following:

```python
my_resource_type = dict

def main(x: my_resource_type):
  ...
```

:::

The JSON Schema of a script's arguments is visible and can be modified from the [`Generated UI`](../6_auto_generated_uis/index.mdx) menu.

### Flows parameters and JSON Schema

Flows in Windmill have input parameters defined by a JSON Schema. Each argument of the [`Flow Input`](../../flows/3_editor_components.mdx#flow-inputs) section corresponds to a field in the JSON Schema. The parameters and their types are validated against the JSON Schema during flow execution.

The JSON Schema of a script's arguments can be modified in the `Flow Input` menu. The schema is visible from the `Export / OpenFlow` section, in particular the "Input Schema" tab.

Inline scripts of flows & apps use an autogenerated JSON Schema that is implicitly used by the frontend.

### Resource types and JSON Schema

Resource types in Windmill are associated with JSON Schemas. A resource type defines the structure and constraints of a resource object. JSON Schema is used to validate the properties and values of a resource object against the specified resource type.

## Advanced settings

Main function's arguments can be given advanced settings that will affect the inputs' [auto-generated UI](../6_auto_generated_uis/index.mdx) and JSON Schema.

Here is an example on how to define a [Python](../../getting_started/0_scripts_quickstart/2_python_quickstart/index.mdx) list as an enum of strings using the `Generated UI` menu.

Each argument has the following settings:

- **Name**: the name of the argument (defined in the main Function).
- **Type**: the type of the argument (defined in the main Function): Integer, Number, String, Boolean, Array, Object, or Any.
- **Description**: the description of the argument.
- **Custom Title**: will be displayed in the UI instead of the field name.
- **Placeholder**: will be displayed in the input field when the field is empty. If not set, the default value (directly set from the script code) will be used. The placeholder is disabled depending on the field type, format, etc.
- **Field settings**: advanced settings depending on the type of the field.

Below is the list of advanced settings for each type of field:

| Type     | Advanced Configuration                                                                                                                                                                                                                                                         |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Integer  | Min and Max. Currency. Currency locale.                                                                                                                                                                                                                                        |
| Number   | Min and Max. Currency. Currency locale.                                                                                                                                                                                                                                        |
| String   | Min textarea rows. Disable variable picker. Is Password (will create a [variable](../2_variables_and_secrets/index.mdx) when filled). Field settings: - File (base64) &#124; Enum &#124; Format: email, hostname, uri, uuid, ipv4, yaml, sql, date-time &#124; Pattern (Regex) |
| Boolean  | No advanced configuration for this type.                                                                                                                                                                                                                                       |
| Resource | [Resource type](../3_resources_and_types/index.mdx).                                                                                                                                                                                                                           |
| Object   | Object properties, or a Template (path to a [`json_schema` resource](../3_resources_and_types/index.mdx#json-schema-resources)) that contains a JSON schema with the properties.                                                                                                                                                   |
| Array    | - Items are strings &#124; Items are strings from an enum &#124; Items are objects (JSON) &#124; Items are numbers &#124; Items are bytes                                                                                                                                      |
| Any      | No advanced configuration for this type.                                                                                                                                                                                                                                       |

## oneOf

`oneOf` is a type that can be used to have the user pick between two objects through [auto-generated UI](../6_auto_generated_uis/index.mdx). Within a [TypeScript](../../getting_started/0_scripts_quickstart/1_typescript_quickstart/index.mdx), it can be used with the following syntax:

```ts
example_oneof: { label: "Option 1", attribute: string } | { label: "Option 2", other_attribute: string }
```

And the auto-generated UI will render:

![oneOf Script](./one_of_script.png 'oneOf Script')

For flows, `oneOf` can be added as a [flow input](../../flows/3_editor_components.mdx#flow-inputs).

![oneOf Flow](./one_of_flow.png 'oneOf Flow')

The result of the user's selection will be the selected object. With the example above if user picks Option 2 and enters a custom value:

```
{
    "label": "Option 2",
    "other_attribute": "Value that the user entered"
}
```

`oneOf` input can also be filled with [CLI](../../advanced/3_cli/index.mdx) or [webhooks](../4_webhooks/index.mdx), for example with `wmill` CLI:

```bash
wmill script run u/user/path -d '{"example_oneof":{"label":"Option 2","attribute":"Value that the user entered"}}'
```

## Dynamic select

Dynamic select is a helper function that allows you to create a select field with dynamic options that recompute based on other input values. It's available in both [scripts](../../script_editor/index.mdx) and [flows](../../flows/1_flow_editor.mdx), but with different implementation requirements.

### Dynamic select in scripts

For scripts, you must export both the string type as `DynSelect_<name>` and the function as `<name>`:

```ts

	}
	if (x === 'bar') {
		return [{ value: 'barbar', label: 'barbarbar' }];
	}
	return [
		{ value: '1', label: 'Foo' + x + y },
		{ value: '2', label: 'Bar' },
		{ value: '3', label: 'Foobar' }
	];
}

	return xy;
}
```

```python
DynSelect_foo = str

def foo(x: str, y: int, text):
  if text == "42":
    return [{"value": "42", "label": "The answer to the universe"}]
  if x == "bar":
    return [{"value": "barbar", "label": "barbarbar"}]
  return [
    { "value": '1', "label": 'Foo' + x + str(y) },
    { "value": '2', "label": 'Bar' },
    { "value": '3', "label": 'Foobar' }
  ]

def main(x: str, y: int, xy: DynSelect_foo):
	print(xy)
	return xy
```

### Dynamic select in flows

In flows, dynamic select is only available for flow input steps. You must define one function for each flow input field that is of dynamic select type. The function name must exactly match the name of the flow input field. No export type is required.

```ts
// Function for flow input field named "foo"
async function foo(x: string, y: number, text: string) {
	if (text === '42') {
		return [{ value: '42', label: 'The answer to the universe' }];
	}
	if (x === 'bar') {
		return [{ value: 'barbar', label: 'barbarbar' }];
	}
	return [
		{ value: '1', label: 'Foo' + x + y },
		{ value: '2', label: 'Bar' },
		{ value: '3', label: 'Foobar' }
	];
}

// If you have another dynamic select input named "category"
async function category(department: string) {
	if (department === 'engineering') {
		return [
			{ value: 'frontend', label: 'Frontend' },
			{ value: 'backend', label: 'Backend' }
		];
	}
	return [{ value: 'general', label: 'General' }];
}
```

```python
# Function for flow input field named "foo"
def foo(x: str, y: int, text):
  if text == "42":
    return [{"value": "42", "label": "The answer to the universe"}]
  if x == "bar":
    return [{"value": "barbar", "label": "barbarbar"}]
  return [
    { "value": '1', "label": 'Foo' + x + str(y) },
    { "value": '2', "label": 'Bar' },
    { "value": '3', "label": 'Foobar' }
  ]

# If you have another dynamic select input named "category"
def category(department: str):
  if department == "engineering":
    return [
      {"value": "frontend", "label": "Frontend"},
      {"value": "backend", "label": "Backend"}
    ]
  return [
    {"value": "general", "label": "General"}
  ]
```

### How dynamic select works

The select options recompute dynamically based on other input arguments.

You can implement custom filtering and sorting logic within your function. In the examples above in the foo function, when `x` equals "bar" or `text` equals "42", the options are filtered to show only one option.

#### Dynamic select in scripts

#### Dynamic select in flows

## Backend schema validation

By default, the schema is not explicitly checked by Windmill. For example, when triggering a script via [webhook](../4_webhooks/index.mdx), it is possible to pass an arbitrary JSON payload for the arguments, and Windmill [workers](../9_worker_groups/index.mdx) will just try to execute the script with it.

In some cases, you might want the job to fail if the payload does not follow the defined schema. For this, just add the `schema_validation` annotation as a comment to the top of your script. The logs should tell you if schema validation is taking place.

For example in [TypeScript](../../getting_started/0_scripts_quickstart/1_typescript_quickstart/index.mdx):

```ts
// schema_validation

				foo: string;
		  }
		| {
				label: 'Variant 2';
				bar: number;
		  }
) {
	return { foo: a };
}
```

Here, if we were to pass a string to `a` instead of a number, or pass `"something else"` to `b` instead of `"my"` or `"enum"`, or even if the shape of `g` does not correspond to one of the OneOf variants, the job will fail.

This was an example in TypeScript but backend schema validation is available on all [languages](../../getting_started/0_scripts_quickstart/index.mdx), in particular for [SQL safe interpolated arguments](../../getting_started/0_scripts_quickstart/5_sql_quickstart/index.mdx#safe-interpolated-arguments).

Note that this validation is not a fully JSON schema compliant. The checks you can expect are type and shape, required fields, strict enums. One thing that is not supported yet for instance is matching regex patterns on strings. When in doubt, it's best to test it out or provide your own checks.
