Skip to main content
Version: 0.3.36

Creating NodeTypes

A NodeType is the schema for a kind of node. It names the properties a node of that type carries, which of them are required or unique, how they are indexed, which child types the node may contain, and a few behaviour flags such as versionable and publishable.

NodeType names take the form namespace:Name: a lowercase namespace, a colon, and a PascalCase name (blog:Article, shop:Product). Digits and underscores are not accepted in the name.

There are three ways to create one. All three produce the same stored object.

Via SQL

The CREATE NODETYPE statement is the quickest way to add a type. Run it through the SQL endpoint or through psql:

curl -s -X POST http://localhost:8090/api/sql/myrepo \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d @- <<'EOF'
{"sql": "CREATE NODETYPE 'blog:Article' (
title String REQUIRED FULLTEXT,
slug String REQUIRED UNIQUE,
body String FULLTEXT,
published_on Date,
tags Array OF String,
author Reference
) DESCRIPTION 'A blog article' ALLOWED_CHILDREN ('raisin:Asset') VERSIONABLE PUBLISHABLE"}
EOF
{"columns":["result","success"],"rows":[{"result":"NodeType 'blog:Article' created","success":true}],"row_count":1,"execution_time_ms":1}

The property list can also follow a PROPERTIES keyword, which is the form to use when the type has an EXTENDS or MIXINS clause:

CREATE NODETYPE 'blog:Guide' EXTENDS 'blog:Article'
MIXINS ('myapp:Seo')
PROPERTIES (difficulty String)
ICON 'book';

Property types accepted by the DDL are String, Number, Boolean, Date, URL, Reference, Resource, Element, Composite, NodeType, Array OF <type> and Object { name Type, ... }. A bare Array without OF is a parse error.

Per-property modifiers: REQUIRED, UNIQUE, FULLTEXT, VECTOR, PROPERTY_INDEX, TRANSLATABLE, DEFAULT <value>, LABEL '...', DESCRIPTION '...', ORDER <n>, ALLOW_ADDITIONAL_PROPERTIES.

Type-level clauses: EXTENDS '...', MIXINS ('a', 'b'), DESCRIPTION '...', ICON '...', ALLOWED_CHILDREN ('...'), REQUIRED_NODES ('...'), COMPOUND_INDEX 'name' ON (col, col DESC), and the flags VERSIONABLE, PUBLISHABLE, AUDITABLE, INDEXABLE, STRICT.

Via HTTP

POST /api/management/{repo}/{branch}/nodetypes takes the NodeType wrapped in a node_type key. An optional commit object records a message and actor for the schema revision.

curl -s -X POST http://localhost:8090/api/management/myrepo/main/nodetypes \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"node_type": {
"name": "blog:Article",
"description": "A blog article",
"properties": [
{"name": "title", "type": "String", "required": true, "index": ["Fulltext"]},
{"name": "tags", "type": "Array", "items": {"type": "String"}},
{"name": "published_on", "type": "Date"},
{"name": "rating", "type": "Number", "constraints": {"min": 0, "max": 5}}
],
"allowed_children": ["raisin:Asset"],
"versionable": true,
"publishable": true
},
"commit": {"message": "Add blog:Article"}
}'

The response is the stored NodeType, with a generated id and version: 1:

{"id":"lfnRlKa3eBv_caFu","strict":null,"name":"blog:Article","extends":null,"overrides":null,"description":"A blog article","icon":null,"version":1,"properties":[{"name":"title","type":"String","required":true,"index":["Fulltext"]},{"name":"tags","type":"Array","items":{"type":"String"}},{"name":"published_on","type":"Date"},{"name":"rating","type":"Number","constraints":{"max":5,"min":0}}],"allowed_children":["raisin:Asset"],"initial_structure":null,"versionable":true,"publishable":true,"auditable":null,"indexable":null,"created_at":"2026-09-06T18:33:16.473456Z","updated_at":"2026-09-06T18:33:16.473456Z","published_at":null,"published_by":null,"previous_version":null}

PUT .../nodetypes/{name} replaces the definition with the same body shape, and DELETE .../nodetypes/{name} removes it. The full route list is in the NodeTypes API reference.

Via a package (YAML)

In a package folder, each NodeType is one YAML file under nodetypes/, listed in the manifest under provides.nodetypes. The YAML keys are the same as the JSON fields above.

# nodetypes/article.yaml
name: blog:Article
description: A blog article
icon: file-text
version: 1

properties:
- name: title
type: String
required: true
index: [Fulltext]
- name: slug
type: String
required: true
unique: true
- name: tags
type: Array
items:
type: String
- name: author
type: Reference

allowed_children:
- raisin:Asset
versionable: true
publishable: true
auditable: true
indexable: true
# manifest.yaml
name: blog
version: 1.0.0
provides:
nodetypes:
- blog:Article
workspaces:
- blog

Deploy the folder with the CLI. --install installs it into the target repository after upload:

raisindb deploy ./blog --repo myrepo --install

Keys the server does not know are dropped without an error. Editor hints such as a display label belong in the free-form meta map on a property, not as top-level keys.

Property schema fields

Each entry in properties accepts:

KeyMeaning
nameProperty name
typeString, Number, Boolean, Date, URL, Reference, Resource, Element, Composite, Geometry, NodeType, Array, Object
requiredThe property must be present on every node
uniqueNo two nodes in the workspace may share the value
defaultDefault value (stored on the schema; see the note below)
itemsSchema of each element, for Array
structureMap of nested property schemas, for Object
constraintsFree-form map, for example {"min": 0, "max": 5}
indexAny of Fulltext, Vector, Property, Spatial
is_translatableThe value may be overlaid per locale
encryptedThe value is moved to the secret store on write
metaFree-form map for editor hints

What the server validates

When a node is written, the server resolves the NodeType (including parents and mixins) and checks:

  • every required property is present,
  • every unique property has no duplicate in the workspace,
  • with strict: true, no property outside the schema is present,
  • element and archetype content matches its element types.

Property values are stored as sent. Type mismatches, constraints and default are recorded on the schema for tooling but are not enforced or applied by the write path today. A missing required property is rejected:

{"code":"VALIDATION_FAILED","message":"Missing required property 'title' for NodeType 'blog:Article'","details":"Missing required property 'title' for NodeType 'blog:Article'","timestamp":"2026-09-06T18:33:16.568668+00:00"}

Allowing the type in a workspace

A node can only be created where its type is allowed. Add the type to the workspace's allowed_node_types (and allowed_root_node_types for top-level nodes):

curl -s -X PUT http://localhost:8090/api/workspaces/myrepo/blog \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"blog","allowed_node_types":["raisin:Folder","blog:Article"],"allowed_root_node_types":["raisin:Folder","blog:Article"]}'

Then create a node:

curl -s -X POST http://localhost:8090/api/repository/myrepo/main/head/blog/ \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"name":"hello","node_type":"blog:Article","properties":{"title":"Hello","tags":["a","b"]}}'

Publishing

Publishing stamps a NodeType as a stable version: it increments version, sets published_at and published_by, and makes the type appear in the /nodetypes/published listing.

curl -s -X POST http://localhost:8090/api/management/myrepo/main/nodetypes/blog:Article/publish \
-H "Authorization: Bearer $TOKEN"

Publishing is not a precondition for creating nodes: a type can be used as soon as it exists and is allowed in the workspace. Use publish to mark the definition you consider final, and .../unpublish to withdraw that mark.

Changing a NodeType

ALTER NODETYPE applies one or more alterations in a single statement:

ALTER NODETYPE 'blog:Article'
ADD PROPERTY subtitle String FULLTEXT
DROP PROPERTY body
SET DESCRIPTION = 'A post';

Other alterations: MODIFY PROPERTY name Type [modifiers], SET ICON = '...', SET EXTENDS = 'parent' or SET EXTENDS = NULL, SET ALLOWED_CHILDREN = ('a', 'b'), SET REQUIRED_NODES = (...), ADD MIXIN 'x', DROP MIXIN 'x', and SET VERSIONABLE|PUBLISHABLE|AUDITABLE = true|false.

Every change creates a new revision of the NodeType; version increases and previous_version points at the prior record. Remove a type with DROP NODETYPE 'blog:Article' (add CASCADE to remove dependants).

Naming and design

  • Namespace every type (blog:Article, not Article). The namespace keeps packages from colliding and is required by the name pattern.
  • Use the singular (shop:Product).
  • Keep a type focused. Share cross-cutting fields with mixins rather than growing one large type.

Next steps