The database structure is defined in a schema.json file containing one or more collections which each have on
or more fields. Based on the schema.json file the user interface and constraints to manage the database is created.
{
"name": "My Database",
"collections": [
{...},
{...}
],
"config": {...}
}
name: {string} - The name of the database.collections: {object[]} - One or more collection objects defining the database structure.config: {object} [optional] - Additional configuration of the db.config.out: {object} [optional] - Configuration of collection output files.config.out.indentSize: {number} [optional] - The indentation size to use for formatting the json output. Set to null for
faster saves and smaller file-sizes (default=2).config.ui: {object} [optional] - Global configuration of the ui.config.ui.manage: {object} [optional] - Global configuration of the manager for collections.config.ui.manage.pageSize: {number} [optional] - The number of the entries to show in the manager.A schema definition must contain one or more collection mappings, where each collection mapping represents the prototype for the entries of the collection. This could e.g. be persons, books, games, cars etc.
{
"name": {
"singular": "person",
"plural": "persons"
},
"fields": [
{...},
{...}
],
"ui": {
"manage": {
"fields": ["fullName", "age", "address.street"]
}
}
}
name: {object} - The singular and plural names of the collection.name.singular: {string} - The singular name of the collection.name.plural: {string} - The plural name of the collection.fields: {object[]} - One or more field objects defining the collection structure.ui: {object} [optional] - Configuration of the editor ui for the collection.ui.manage: {object} [optional] - Configuration of manager for the collection.ui.manage.fields: {string[]} [optional] - The fields to show in the manager for the collection. If undefined all direct non-array
fields will be shown.A collection can include one or more field mappings defining the fields of the collection.
All fields must have a type, propName and a title property.
Each field can describe a single value or an array of values using the array property.
Additionally each field can have a set of options tied to the specific type of field, where some are required and some are optional.
text fieldsText fields are used for storing textual data such as names, adresses and descriptions.
A text field is stored as a JSON string.
{
"type": "text",
"propName": "fullName",
"title": "Full Name",
"array": false,
"required": false,
"textRows": 1,
"defaultValue": "some default value"
}
type: {string} - The type of the field.propName: {string} - The name of the property the value should be stored in.title: {string} - The title of the field to show in the editor.array: {boolean} [optional] - Defines the field to be an array of values if true which makes adding multiple values
possible (default=false).required: {boolean} [optional] - Sets the field to be required if true (default=false).textRows: {number} [optional] - The number of text rows to display in the create/edit form (default=1).defaultValue: {string|string[]} [optional] - A default value or an array of values if array=true (default=undefined).ui: {object} [optional] - Configuration of the editor ui for the field.number fieldsNumber fields are used for storing numeric data such as ages, counts and scores.
A number field is stored as a JSON number.
{
"type": "number",
"propName": "age",
"title": "Age",
"array": false,
"required": false,
"defaultValue": 0
}
type: {string} - The type of the field.propName: {string} - The name of the property the value should be stored in.title: {string} - The title of the field to show in the editor.array: {boolean} [optional] - Defines the field to be an array of values if true which makes adding multiple values
possible (default=false).required: {boolean} [optional] - Sets the field to be required if true (default=false).defaultValue: {number|number[]} [optional] - A default value or an array of values if array=true (default=undefined).ui: {object} [optional] - Configuration of the editor ui for the field.enum fieldsEnum fields are used for storing any valid JSON data but restricted to a predefined set of the given type. The value would typically be a string for e.g. a persons civil status or a number for a fixed range rating, but objects and arrays are possible values as well.
An enum field is stored as the value defined in its options value property.
{
"type": "enum",
"propName": "civilStatus",
"title": "Civil Status",
"options": [
{ "value": "single", "title": "Single" },
{ "value": "relationship", "title": "In a relationship" },
{ "value": "married", "title": "Married" },
{ "value": "complicated", "title": "It's complicated" }
],
"array": false,
"required": false,
"defaultValue": "single"
}
type: {string} - The type of the field.propName: {string} - The name of the property the value should be stored in.title: {string} - The title of the field to show in the editor.options: {object[]} - The options available to choose from.options: {object[].value} - The value to set if this option is selected.options: {object[].title} - The title to display for this option.array: {boolean} [optional] - Defines the field to be an array of values if true which makes adding multiple values
possible (default=false).required: {boolean} [optional] - Sets the field to be required if true (default=false).defaultValue: {*} [optional] - The value of the option which should be default selected (default=undefined).inline: {boolean} [optional] - Show selected options inline instead of blocks when array=true (default=false).ui: {object} [optional] - Configuration of the editor ui for the field.object fieldsObject fields are used for storing related fields grouped together as an object such as adresses with street, number, city and zip-code, books with title, author and description or links with label, url and type.
An object field is stored as a JSON object with properties of the types defined in the fields section of the object field.
Object fields can themselves contain fields of the object type making nested hierarchies of objects possible.
In most cases an object can as an alternative be modelled as relation if e.g. normalization of the collection's data is required.
{
"type": "object",
"propName": "address",
"title": "Address",
"fields": [
{
"type": "text",
"propName": "street",
"title": "Street"
},
{
"type": "text",
"propName": "city",
"title": "City"
}
],
"array": false,
"required": false
}
type: {string} - The type of the field.propName: {string} - The name of the property the value should be stored in.title: {string} - The title of the field to show in the editor.fields: {object[]} - The fields for this object - all field types are allowed and should be mapped in the same way as
fields defined in a collection.array: {boolean} [optional] - Defines the field to be an array of values if true which makes adding multiple objects
possible (default=false).required: {boolean} [optional] - Sets the field to be required if true (default=false).ui: {object} [optional] - Configuration of the editor ui for the field.relation fieldsRelation fields are used for mapping relations like in a relational database where primary and foreign keys represents a connection between two entries.
A relation field is stored as an integer or an array of integers referring to the primary key of the entry which the relation points to.
A relation is often a good alternative to nested objects if the object is a part of multiple relationships and the fields of the object might change over time. This is also referred to as normalization of the database, where the goal is to make sure that every piece of information only exists in one place which makes it easier to keep all data up to date when data is modified.
{
"type": "relation",
"propName": "bornIn",
"title": "Country of Origin",
"targetCollection": {
"name": "countries",
"titleField": "name"
}
"readOnly": false,
"array": false,
"required": false
}
type: {string} - The type of the field.propName: {string} - The name of the property the value should be stored in.title: {string} - The title of the field to show in the editor.targetCollection: {object} - The mapping of the relationship.targetCollection.name: {string} - The name of the target collection the relation points to.targetCollection.titleField: {string|string[]} - The field or an array of fields from the targetCollection which should be shown
in the editor. Nested paths, which does not include arrays, are allowed.targetCollection.inversedByPropName: {string} [optional] - The property name of the target collection which points back to this
collection. Use this for creating bidirectional relationships where updating the relationship on one side
will automatically update the relationship of the other side and vice-versa.readOnly: {boolean} [optional] - Set to true if the field should be read-only from this side of the relationship
(default=false).array: {boolean} [optional] - Defines the field to be an array of values if true which makes adding multiple relations
possible (default=false).required: {boolean} [optional] - Sets the field to be required if true (default=false).ui: {object} [optional] - Configuration of the editor ui for the field.For bidirectional relationships consider using readOnly on one of the sides. Often the reason for choosing a bidirectional relationship
is because the data model becomes easier to use afterwards and not because it necessarily makes sense to edit the relationship from both sides - one of the
sides is typically the primary initiator/owner of the relationship. When using readOnly on a relation you are still able to see the related
entries but can only edit them from the other side.
{
"type": "relation",
"propName": "owner",
"title": "Owner",
"targetCollection": {
"name": "persons",
"titleField": "name"
}
"required": true
}
{
"type": "relation",
"propName": "books",
"title": "Books",
"targetCollection": {
"name": "books",
"titleField": "title",
"inversedByPropName": "owner"
}
"readOnly": true,
"array": true
}
bidirectional relations with inversedByPropName cannot have required set to true without
having one of the sides readOnly set to true as the synchronization of the inverse side, when a relation can
be edited from both sides, can result in violations of the required restriction.
E.g. in a person owns many books relationship where a book only can be owned by a single person and the ownership
can be changed from both sides the required restriction will be violated if:
required owner on the book siderequired books on the person sidefile fieldsFile fields are used attaching files, e.g., images, text-documents, csv-files, to an entry.
A file field is stored as an JSON object with details about the file (see example below).
{
"type": "file",
"propName": "profileImage",
"title": "Profile Image",
"array": false,
"required": false
}
type: {string} - The type of the field.propName: {string} - The name of the property the value should be stored in.title: {string} - The title of the field to show in the editor.required: {boolean} [optional] - Sets the field to be required if true (default=false).ui: {object} [optional] - Configuration of the editor ui for the field.The stored file object has the following properties:
name: {string} - The internal unique name of the file.originalName: {string} - The original name of the file.ext: {string} - The file extension.mimeType: string - The files mime type.size: number - The size of the file in bytes.lastModified: number - The time in milliseconds since Epoch the file was created in the database.{
"name": "23dj3.txt",
"originalName": "example.txt",
"ext": "txt",
"mimeType": "text/plain",
"size": 123,
"lastModified": 1754643507643
}
The actual file is copied to the database directory /resources and renamed to a unique internal name.
See the Books Demo Database for an example.
Optionally fields can configure how they should be displayed in the editor with the following options:
The sorting options will be applied to array fields in the editor in the following views:
readOnly arrays in the edit entry form.Furthermore, editable arrays will have a "sort" button () in the create- and edit entry form, that will apply the sorting to the array. The sorted order will be saved when the entry is saved.
{
"type": "object",
"propName": "authors",
"title": "Authors",
"array": true,
"ui": {
"sorting": [
{"field": "lastName", "order": "asc"]},
{"field": "firstName", "order": "asc"]}
]
}
}
ui: {object} [optional] - The root of the ui configuration for the field.ui.sorting: {array} [optional] - An array of sorting options. The first element is the primary sorting.ui.sorting[].field: {string} [required] - The field to sort on. The path is relative to the owning field.
If the owning field has one the the types text, number or enum the sorting field must be "" (empty
string).ui.sorting[].order {'asc'|'desc'} [required]The order to sort by.file FieldsFields of type file, supports sorting on the file object properties: originalName, ext and mimeType.
{
"type": "file",
"propName": "attachments",
"title": "Attachments",
"array": true,
"ui": {
"sorting": [
{ "field": "originalName", "order": "asc" }
]
}
}
Json-db can be used to edit any json document as long as it defines a root object with an entries array and each entry has an unique integer id
> 0.
{
"entries": [
{ "id": 1, "prop1": "value1", "prop2": {...} },
{ "id": 4, "prop1": "value2", "prop2": {...} },
{ "id": 9, "prop1": "value3", "prop2": {...} },
]
}
Only the fields which needs to be edited is required to be specified in the schema definition. Properties not specified will be kept as is as long as they are not a sub-path of one of the specified fields.
The following shows a full schema.json for a movie database. The full database with data can be downloaded from the demo databases section.
{
"name": "Movie DB",
"collections": [
{
"name": {
"singular": "country",
"plural": "countries"
},
"fields": [
{
"propName": "name",
"title": "Name",
"type": "text",
"textRows": 1,
"required": true
},
{
"propName": "iso3166_1",
"title": "ISO 3166-1 Country Code",
"type": "text",
"textRows": 1,
"required": true
},
{
"propName": "position",
"title": "Position",
"type": "object",
"required": true,
"fields": [
{
"propName": "lon",
"title": "Longitude",
"type": "number",
"required": true
},
{
"propName": "lat",
"title": "Latitude",
"type": "number",
"required": true
}
]
}
]
},
{
"name": {
"singular": "person",
"plural": "persons"
},
"fields": [
{
"propName": "name",
"title": "Name",
"type": "text",
"textRows": 1,
"required": true
},
{
"propName": "gender",
"title": "Gender",
"type": "enum",
"required": true,
"options": [
{
"value": "female",
"title": "Female"
},
[more entries ...]
]
},
{
"propName": "popularity",
"title": "Popularity",
"type": "number",
"required": true
},
{
"propName": "knownForDepartment",
"title": "Known For Department",
"type": "enum",
"required": true,
"options": [
{
"value": "Acting",
"title": "Acting"
},
[... more entries ...]
]
},
{
"propName": "profilePath",
"title": "Profile Url",
"type": "text",
"textRows": 1
}
]
},
{
"name": {
"singular": "movie",
"plural": "movies"
},
"fields": [
{
"propName": "title",
"title": "Title",
"type": "text",
"required": true
},
{
"propName": "description",
"title": "Description",
"type": "text",
"required": true
},
{
"propName": "tagline",
"title": "Tagline",
"type": "text"
},
{
"propName": "language",
"title": "Language",
"type": "enum",
"options": [
{
"value": "en",
"title": "en"
},
[... more entries ...]
],
"required": true
},
{
"propName": "runtime",
"title": "Runtime",
"type": "number",
"required": true
},
{
"propName": "genres",
"title": "Genres",
"type": "enum",
"options": [
{
"value": "Action",
"title": "Action"
},
[... more entries ...]
],
"required": true
},
{
"propName": "voteCount",
"title": "Vote Count",
"type": "number",
"required": true,
"defaultValue": 0
},
{
"propName": "voteAverage",
"title": "Vote Average",
"type": "number",
"required": true,
"defaultValue": 0
},
{
"propName": "releaseDate",
"title": "Release Date",
"type": "text",
"required": true
},
{
"propName": "posterPath",
"title": "Poster Path URL",
"type": "text"
},
{
"propName": "productionCountry",
"title": "Production Country",
"type": "relation",
"targetCollection": {
"name": "countries",
"titleField": "name"
}
},
{
"propName": "directors",
"title": "Directors",
"type": "relation",
"array": true,
"required": true,
"targetCollection": {
"name": "persons",
"titleField": "name"
}
},
{
"propName": "cast",
"title": "Cast",
"type": "object",
"array": "true",
"fields": [
{
"propName": "character",
"title": "Character",
"type": "string",
"required": true
},
{
"propName": "actor",
"title": "Actor",
"type": "relation",
"required": true,
"targetCollection": {
"name": "persons",
"titleField": "name"
}
}
]
}
]
}
]
}