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
).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
).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
).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
).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
).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 sideJson-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"
}
}
]
}
]
}
]
}