Defining Item Types
Stately schemas are TypeScript code that’s checked in to your code repository alongside your service code. You write your schema in TypeScript even if you’ll be using a Go, Ruby, or Python client. You write your schema using helper methods imported from the @stately-cloud/schema
package. We recommend opening your schema directory in VSCode since it has built-in support for TypeScript.
Make sure to follow the Define a Schema to get set up with the tools and configuration to build schemas. This document is a reference for how the schema builder API works, and what options you have for defining schema.
Declaring Types
Section titled “Declaring Types”Your schema will consist of as many different type declarations as you want. Each of these is constructed through one of the type builders in the @stately-cloud/schema
package, such as itemType
, objectType
, enumType
, or type
. Since your schema is defined using regular TypeScript, you can use variables, shared constants, functions, even loops (please be reasonable). This also means you can break up your schema into multiple files and import them all together using JavaScript modules however you like. The only thing to keep in mind is that the CLI will load a single file to find all your types, so you should have a top level schema.ts
or index.ts
that imports all your other files. For example, if you’ve made a user.ts
, address.ts
, and orders.ts
that each have some types in them, you could have an index.ts
that looks like:
import './user.js';import './address.js';import './orders.js';
Notice that the file extensions are .js
, not .ts
. That’s because TypeScript gets translated to JavaScript before being run. It’s weird, we know!
Here’s an example type declaration (the ”…” is a lot of omitted code):
import { itemType } from "@stately-cloud/schema";
itemType("User", { ... });
The type builder function itemType
is configuring a new type. The name for the new type is the first argument to the itemType
function.
Assigning Types to Variables
Section titled “Assigning Types to Variables”Unlike itemType
s, custom types like objectType
, enumType
, or type
are used to declare reusable types that you can use as fields of other types. This means we need to assign them to a variable so we can reference them in fields.
import { itemType, objectType } from "@stately-cloud/schema";
const Address = objectType("Address", { ... });
// Now we can use Address as a field of UseritemType("User", { fields: { ... address: { type: Address }, ... }})
Here we assign a type to the variable const Address
- the name of that variable doesn’t actually matter, but it’s less confusing if you name it the same as the name of the type you’re declaring.
There’s an alternate way to declare types, using a function (not an arrow function):
import { objectType } from "@stately-cloud/schema";
export function Address() { return objectType("Address", { ... });}
This has the same result, but there’s one big advantage - types declared as functions are resolved lazily, which means you can use a type before it’s declared. This is very helpful when making circular data structures—imagine a User
has an Address
but the Address
also has a list of Users
—which one needs to be declared first? If you declare at least one of them as a function, it doesn’t matter.
Item Types
Section titled “Item Types”Each Item in your Store belongs to an Item Type which defines its shape and how it is stored. Item Types are declared using the itemType
function:
import { itemType, uuid } from "@stately-cloud/schema";
itemType("User", { keyPath: ["/user-:id"], fields: { id: { type: uuid, initialValue: "uuid", }, // more fields... },});
Each Item Type has the following required properties:
- A name, which is the first argument to the
itemType
function. Item Type names must be unique within the schema, and by convention are CamelCase. - At least one
keyPath
which provides an address to store the Item at. fields
that define all the fields of the item type. This is an object where each property is the name of a field, and the value configures that field. Field names are by convention camelCase.
Along with some optional properties:
ttl
(Time To Live) - A way to define how long an Item should exist before being automatically deleted from the store.indexes
- A list of group-local indexes that allow for different ways to list items in the same Group.
Documenting Schema
Section titled “Documenting Schema”You can document your types and their fields using regular JSDoc comments (/** */
). We analyze your source code to automatically bring these comments along in your generated code. Normal comments (//
or /* */
) are ignored. Note that if you stray from the patterns above and generate your types dynamically, your documentation might not make it through the process.
import { itemType, uuid } from "@stately-cloud/schema";
/** * This type represents a user in my system. */itemType("User", { keyPath: ["/user-:id"], fields: { /** This field is documented */ id: { type: uuid, initialValue: "uuid", }, // more fields... },});