Zodot is a new data validation library for Godot, with an API that was inspired by the popular Typescript/Javascript npm package Zod.

Data validation is an important concept in many programming environments, including Godot. It is the process of taking unknown data and confirming that it matches the shape that is expected. If the data does not match the expected shape, then gracefully handle the error.

Features

Some of Zodot’s convenient features include:

  • Typed dictionaries in GDScript
  • Typed Unions
  • Support for nullables
  • Automatic data coercing with str_to_var()

More details on these features below.

When to use Zodot

Oftentimes in game development, or any development - developers will store or retrieve data externally from their codebase, either from a database, an API rest endpoint, a file, or just an external package. Such data can be things like a player’s inventory system, their statistics, lobby information, friends lists, or more.

No matter how you retrieve some external data, the important step is to validate that the data you received matches the shape that you are expecting, otherwise you may start accessing the data when assuming its shape, but results in a runtime crash when the data has changed unexpectedly.

How to use Zodot

Using Zodot is super easy. The first thing to do is define a schema, and then use the schema to parse the data that you want to validate.

Example of defining a player schema with 3 fields:

var PlayerSchema = Z.schema({
  "user_id": Z.string(),
  "avatar_color": Z.color(),
  "position": Z.vector3()
})

This schema defined an object with 3 typed fields. Now we can take some data and pass it to the schema to be validated against:

func parse_player(data: Variant):
  var result = PlayerSchema.parse(data)

  if not result.ok():
    print("The data does not match the expected shape")
    print(result.error)
    return

  # Do things with the valid data now
  print(result.data)

In the above example, we see the data comes in as a Variant type, which just means it could be anything, then we pass that to our schema. If our result from the parse is not ok, then we have access to a detailed error message as result.error. If it does match the schema, then we have access to the typed data in result.data.

Data Coercing

One of the best features of Zodot is its ability to corece data before validating. This is popular when complex data is stored as strings and then needs to be deserialized back into its real type after retrieval. Let’s look at an example:

var schema = Z.schema({
  "color": Z.color().coerce(),
  "position": Z.vector2().coerce()
})

We use the .coerce() extension for each field we want to deserialize because we are expecting to receive that field as a string that was previously serialized using Godot’s var_to_str method.

Now if we have serialized data such as:

# notice the values are wrapped in strings
var data = {
  "color": "Color(1,2,3,.5)",
  "position": "Vector3(4,2,5)"
}

We can parse it and receive the actual types after successful validation:

var result = schema.parse(data)
print(result.data.color) # Color(1,2,3,.5) - real type, not wrapped in strings

Typed Dictionaries

Zodot provides some powerful features that are not even available in GDScript, such as typed dictionaries. Take a look at a dictionary example:

var schema = Z.schema({
  "other_field": Z.float(),
  "my_dict": Z.dictionary(Z.color())
})

Since GDScript can’t define the type for the dictionary values, we can use Zodot’s dictionary validator to ensure that all the values in the dictionary are of a certain type - like this example, all values in the dictionary must be a Color type.

Typed Unions

Another powerful validator from Zodot is called z.union() which is another feature that GDScript does not provide. This validator allows fields to be more than one type.

Z.schema({
  "user_id": Z.union([Z.string(), Z.integer()])
})

In this example, the user_id is defined to accept a string or even an integer as a valid type! This is similar to other languages like Typescript which would be defined as: type UserId = string | number

Nullables

One last feature of Zodot is its ability to allow any field to be null just by adding .nullable() to the end of the field validator.

var nullable_color_field = z.color().nullable()

This makes it much more clear than the basic GDScript type definitions since GDScript has no real way of defining that relationship.

Try it out

Zodot is available on the Godot Asset Store here, and it can also just be downloaded directly from Github here.

Follow me on Twitter to hear more about interesting Godot projects!