Skip to main content

JSON Schema and Parsing

Windmill leverages the JSON Schema to define the structure and validation rules for JSON data, adhering to the JSON Schema standard (version 2020-12) 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. Windmill is compatible with the 2020-12 version. It is not compatible with its most advanced features yet.

{
"$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.
  • 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:

PythonJSON Schema
strstring
floatnumber
Literal["a", "b"]string with enums: "a", "b"
intinteger
boolboolean
dictobject
listany[]
List[str]string[]
bytesstring, encodingFormat: base64
datetimestr, format: date-time
_any
DynSelect_foodynselect-<name>

In Deno, Bun, REST:

TypeScriptJSON Schema
stringstring
"a" | "b"string with enums: "a", "b"
objectobject
booleanboolean
bigintint
numbernumber
string[]string[]
("foo" | "bar")[]enum[]
oneOfobject
DynSelect_foodynselect-<name>

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

WindmillJSON Schema
wmill.Base64string, encoding$$Format: base64
wmill.Emailstring, format: email
wmill.Sqlstring, 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). 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:

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 menu.

Flows Parameters and JSON Schema

Flows in Windmill have input parameters defined by a JSON Schema. Each argument of the Flow Input 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 and JSON Schema.

Here is an example on how to define a Python 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:

TypeAdvanced Configuration
IntegerMin and Max. Currency. Currency locale.
NumberMin and Max. Currency. Currency locale.
StringMin textarea rows. Disable variable picker. Is Password (will create a variable when filled). Field settings: - File (base64) | Enum | Format: email, hostname, uri, uuid, ipv4, yaml, sql, date-time | Pattern (Regex)
BooleanNo advanced configuration for this type.
ObjectAdvanced settings are Resource Types.
Array- Items are strings | Items are strings from an enum | Items are objects (JSON) | Items are numbers | Items are bytes
AnyNo advanced configuration for this type.

Special Types

There are special types that lead to a custom behavior in the auto-generated UIs.

oneOf

oneOf is a type that can be used to have the user pick between two objects through auto-generated UI. Within a TypeScript, it can be used with the following syntax:

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

And the auto-generated UI will render:

oneOf Script

For flows, oneOf can be added as a flow input.

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 or webhooks, for example with wmill CLI:

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

Dynamic Select

Dynamic Select is an helper function within scripts that allows you to create a select field with dynamic options.

You must export the string type as DynSelect_<name> and the function as <name> :

export type DynSelect_foo = string

export async function foo(x: string, y: number) {
if (x === "bar") {
return [{ value: "barbar", label: "barbarbar" }];
}
return [
{ value: '1', label: 'Foo' + x + y },
{ value: '2', label: 'Bar' },
{ value: '3', label: 'Foobar' }
]
}

export async function main(y: number, x: string, xy: DynSelect_foo) {
console.log(xy)
return xy
}

The select options recompute dynamically based on the other arguments, in this case x and y.

You can also do your own filtering and sorting of the options. In the example above, if x is "bar", the options will be filtered to only one option.