Document definitions used throughout the Foundry Virtual Tabletop framework.
The Document class represents a discrete entry in the server-side database.
Document Subclasses
Foundry has over 30 distinct Document types, each of which is available in the global namespace. However, the typical system only needs to customize a handful of them (Actor and Item being the most typical), while the rest can be left alone.
Subclassing a document is as simple as extending the class and replacing the reference in CONFIG.
The advantage of subclassing a document is you can adjust the behavior of core functions, such as actor.getRollData(), the function generally used to populate roll paths (e.g. 1d20 + @abilities.str.mod in dnd5e). By default, this function only returns a reference to the system property of the actor, but by subclassing it we can add in more information.
Now, in addition to the system information, users can access anything stored in flags with @flags.key.value! There are many other functions in the core software which are written with subclassing in mind, which you can find through the official API documentation.
Some important limitations of document subclassing:
You can only register one document class per document type; the following section, document subtypes, covers how you can have varied behavior
You cannot modify the database-backed schemas of documents; custom properties must go inside of flags or system (if available).
You cannot define new document types beyond those defined by the core software.
Document Subtypes
While there can only be one registered document class per document type, there can be any number of registered document subtypes. A document subtype is a way to establish categories of documents, such as "character" or "npc". Eleven document types currently support subtypes; that is to say, they have a paired "type" and "system" field (a few others, like RollTable, have a type field but these cannot be customized or extended by community packages and do not have a corresponding system field).
You don’t need to implement your own subtypes for each of these; most systems only need to specify subtypes for Actor and Item. The rest, without registered subtypes, will default to type: "base".
Registering a subtype is done in the "documentTypes" property of system.json or module.json.
This informs the server to expect that Actor will have two subtypes: "character" and "npc", while Items will have "equipment" and "feature". Foundry strictly prohibits creating un-registered document subtypes, however systems are not the only ones that can define a subtype — modules are also able to register subtypes. A module-registered subtype is always of the format module-id.subtype.
The second part of defining a document subtype is registering a data model for each subtype. The Data Models page gets into the details of how to fill in these data models, but the following is a good pattern for defining data models. Systems should always subclass TypeDataModel for these purposes (The exception is ActiveEffect subtypes, which should subclass foundry.data.ActiveEffectTypeDataModel).
The actorConfig object, above, is necessary to associate the names of the subtypes defined in system.json with their data models. That is to say, each Actor with a type of "character" will have a CharacterModel instance for its system property.
Working with Documents
All package developers need to work with documents in some form. Documents are stored in collections, such as a WorldCollection, CompendiumCollection, or an EmbeddedCollection. Document operations often reference parent and foundry.abstract.Document.pack; a document's parent is the document that has the EmbeddedCollection containing it, and a document's pack is the ID of the CompendiumCollection holding it. A document embedded inside of another document that is inside a compendium would have both a parent and a pack.
These relationships are predefined and validated by the server — there is no world collection of active effects and items cannot be embedded inside of each other. Many documents are only valid as embedded documents, e.g. a Token is always contained within a Scene. Individual document class pages have more information about the available collections the document can be included in and what valid embedded documents it has if any.
Accessing Documents
Documents are distinguished within a collection by their _id, a 16-character string that is the only property that is guaranteed unique within a given collection. Collections of all forms provide synchronous access with the get method, which takes the document's _id and returns the document. A basic example of this is game.actors.get, which accesses the Actors collection to return an actor contained within the world. Collections have many other methods for working with their contents, such as being iterable for for loops; check out the various foundry.utils.Collection subclass pages for more information on them.
The uuid property provides the full path of collections and IDs necessary to retrieve a document. For example, an active effect inside of an item on an unlinked token actor might have a UUID of Scene.6ES7pwxLe2XpxS7u.Token.PhV3rSGgTBDwMJzi.Actor.lhsXXokRvqYijRSp.Item.jFoLCNS2BFxZQeVd.ActiveEffect.Q0A91xSgxHvzCrXM; you can see how the UUID alternates between document names and IDs. The fromUuidSync method can retrieve any world document, regardless of how deeply embedded, when provided a valid UUID. The foundry.utils.parseUuid method can be helpful when trying to understand pieces of a UUID programmatically.
Working with compendiums is more complicated. Documents inside of a compendium are not fully loaded, which has the benefit of improving performance but restricts options available to developers. The typical way of accessing a document inside of a compendium is by knowing the UUID and using fromUuid, which will return a Promise that resolves to the document. A typical construction is const doc = await fromUuid(uuid). If you need many documents from a given pack, the pack.getDocuments() method can help. You can find individual compendium collections inside game.packs, which itself is a Collection subclass that uses a concatenation of the package id and pack name, e.g. crucible.affixes. All documents stored inside of a compendium have a UUID that starts with Compendium, e.g. Compendium.crucible.affixes.ActiveEffect.determination000.
If you need to access information about documents inside a compendium at synchronous speed, some data is cached in the pack.index property. This includes each document's name (helpful if you want to go pack.index.getName), img, and type, alongside the _id and a constructed uuid property. Alongside the various methods of iterating on collections like filter, this can allow you to synchronously prepare a list of documents without needing to perform a costly full-fetch. You can expand the cached information with the getIndex method; the ready hook is generally the most appropriate time to expand the indices this way.
Creating Documents
Document creation in Foundry is performed through factory methods like Item.create — normal class construction like new Item does not communicate with the database on the server to properly persist the created document.
Document Data: The first argument to create is the document data. The valid data for each document type is determined by the document's schema, which is validated by the server. You can see a full list of document typedefs on the types page. This document data should be JSON serializable (so no complex objects like classes or sets). Most fields for documents have initial values and are not required, but a few fields like name and type are usually required. This document data is saved to the created document's _source. When Foundry makes changes to core document schemas it provides migrations and shims to help and minimize immediate breakage but it is still important to stay on top of any changes that do happen.
Document Operation Parameters: The second argument to create methods is almost always operations, which informs the operation. The two most important parameters are parent and pack, which help locate where you want to create the document; Item.create defaults to creating a world item, but if you want the item to be inside an Actor you need to pass that actor as the parent, and if you want the item to be created inside of a compendium you need to provide the pack ID for pack. If you want to create an item inside an actor that is inside a compendium you do not need to pass both, just parent is required. There are other optional parameters as listed in the DatabaseCreateOperation typedef, and unlike document data you can define your own parameters — any JSON serializable properties will be kept throughout the full database transaction process.
Batched Operations: If you want to create multiple documents at once in the same place, createDocuments is your friend — instead of a single data object, you provide an array of them. Alternatively, you can use createEmbeddedDocuments, which is an instance method of the parent that doesn't require parent or pack but does require you to specify the document type being created. If you need to operate in multiple locations, use foundry.documents.modifyBatch.
Updating Documents
Updating documents follows many of the same principles as document creation, but instead of a static factory method the update method is called on the document instance to be updated, e.g. item.update. If you're updating a nested property, Foundry accepts using dot-separated strings for the keys, e.g. actor.update({ "system.hp.value": 10 }). Like creation, updates have their own operation parameter which is defined by DatabaseUpdateOperation, but this is much more rarely needed for individual updates — Foundry will automatically fill in the pack and parent for the document being updated.
Special Operators: Document updates default to partial diffing (except for arrays, which are always fully replaced). To help Foundry has made the _del and _replace operators globally available. _del will remove a key from an object (usually useful with TypedObjectField), while _replace(data) indicates that the properties should be fully replaced by the provided data object rather than performing a partial update.
Flags: The setFlag and unsetFlag methods are wrappers for update that can simplify the process of constructing the flag update while validating it's of the proper format. If you have a more complex update to perform it may be preferable to use the general update method.
Batched Operations: To update multiple documents at the same time it is necessary to use methods like updateDocuments. This method takes an array of update data, which in addition to the data that is being updated also requires providing the matching _id for each document so Foundry knows which documents are being updated. Like createDocuments, you must provide parent or pack as needed to specify where these updates are taking place. Alternatively, you can use updateEmbeddedDocuments, which is an instance method of the parent that doesn't require parent or pack but does require you to specify the document type being updated. If these updates are occurring across multiple locations, leverage foundry.documents.modifyBatch.
Deleting Documents
Deleting an individual document is fairly simple; just call the delete method from the document instance. You may want to use deleteDialog instead to obtain confirmation, since deletions are irreversible. Like creation, deletion has its own operation parameter which is defined by DatabaseDeleteOperation, but this is much more rarely needed for individual deletions — Foundry will automatically fill in the pack and parent for the document being deleted.
Batched Operations: To delete multiple documents at the same time it is necessary to use methods like deleteDocuments. This method takes an array of document ids. Like createDocuments, you must provide parent or pack as needed to specify where these deletions are taking place. Alternatively, you can use deleteEmbeddedDocuments, which is an instance method of the parent that doesn't require parent or pack but does require you to specify the document type being deleted. If these deletions are occurring across multiple locations, leverage foundry.documents.modifyBatch.
Document definitions used throughout the Foundry Virtual Tabletop framework. The Document class represents a discrete entry in the server-side database.
Document Subclasses
Foundry has over 30 distinct Document types, each of which is available in the global namespace. However, the typical system only needs to customize a handful of them (Actor and Item being the most typical), while the rest can be left alone.
Subclassing a document is as simple as extending the class and replacing the reference in CONFIG.
The advantage of subclassing a document is you can adjust the behavior of core functions, such as
actor.getRollData(), the function generally used to populate roll paths (e.g.1d20 + @abilities.str.modin dnd5e). By default, this function only returns a reference to the system property of the actor, but by subclassing it we can add in more information.Now, in addition to the system information, users can access anything stored in flags with
@flags.key.value! There are many other functions in the core software which are written with subclassing in mind, which you can find through the official API documentation.Some important limitations of document subclassing:
flagsorsystem(if available).Document Subtypes
While there can only be one registered document class per document type, there can be any number of registered document subtypes. A document subtype is a way to establish categories of documents, such as "character" or "npc". Eleven document types currently support subtypes; that is to say, they have a paired "type" and "system" field (a few others, like RollTable, have a
typefield but these cannot be customized or extended by community packages and do not have a correspondingsystemfield).You don’t need to implement your own subtypes for each of these; most systems only need to specify subtypes for Actor and Item. The rest, without registered subtypes, will default to
type: "base".Registering a subtype is done in the "documentTypes" property of system.json or module.json.
This informs the server to expect that Actor will have two subtypes: "character" and "npc", while Items will have "equipment" and "feature". Foundry strictly prohibits creating un-registered document subtypes, however systems are not the only ones that can define a subtype — modules are also able to register subtypes. A module-registered subtype is always of the format
module-id.subtype.The second part of defining a document subtype is registering a data model for each subtype. The Data Models page gets into the details of how to fill in these data models, but the following is a good pattern for defining data models. Systems should always subclass TypeDataModel for these purposes (The exception is ActiveEffect subtypes, which should subclass
foundry.data.ActiveEffectTypeDataModel).The
actorConfigobject, above, is necessary to associate the names of the subtypes defined insystem.jsonwith their data models. That is to say, each Actor with a type of "character" will have aCharacterModelinstance for its system property.Working with Documents
All package developers need to work with documents in some form. Documents are stored in collections, such as a
WorldCollection,CompendiumCollection, or anEmbeddedCollection. Document operations often referenceparentand foundry.abstract.Document.pack; a document'sparentis the document that has theEmbeddedCollectioncontaining it, and a document'spackis the ID of theCompendiumCollectionholding it. A document embedded inside of another document that is inside a compendium would have both aparentand apack.These relationships are predefined and validated by the server — there is no world collection of active effects and items cannot be embedded inside of each other. Many documents are only valid as embedded documents, e.g. a Token is always contained within a Scene. Individual document class pages have more information about the available collections the document can be included in and what valid embedded documents it has if any.
Accessing Documents
Documents are distinguished within a collection by their
_id, a 16-character string that is the only property that is guaranteed unique within a given collection. Collections of all forms provide synchronous access with thegetmethod, which takes the document's_idand returns the document. A basic example of this isgame.actors.get, which accesses theActorscollection to return an actor contained within the world. Collections have many other methods for working with their contents, such as being iterable forforloops; check out the variousfoundry.utils.Collectionsubclass pages for more information on them.The
uuidproperty provides the full path of collections and IDs necessary to retrieve a document. For example, an active effect inside of an item on an unlinked token actor might have a UUID ofScene.6ES7pwxLe2XpxS7u.Token.PhV3rSGgTBDwMJzi.Actor.lhsXXokRvqYijRSp.Item.jFoLCNS2BFxZQeVd.ActiveEffect.Q0A91xSgxHvzCrXM; you can see how the UUID alternates between document names and IDs. ThefromUuidSyncmethod can retrieve any world document, regardless of how deeply embedded, when provided a valid UUID. Thefoundry.utils.parseUuidmethod can be helpful when trying to understand pieces of a UUID programmatically.Working with compendiums is more complicated. Documents inside of a compendium are not fully loaded, which has the benefit of improving performance but restricts options available to developers. The typical way of accessing a document inside of a compendium is by knowing the UUID and using
fromUuid, which will return a Promise that resolves to the document. A typical construction isconst doc = await fromUuid(uuid). If you need many documents from a givenpack, thepack.getDocuments()method can help. You can find individual compendium collections insidegame.packs, which itself is aCollectionsubclass that uses a concatenation of the packageidand packname, e.g.crucible.affixes. All documents stored inside of a compendium have a UUID that starts withCompendium, e.g.Compendium.crucible.affixes.ActiveEffect.determination000.If you need to access information about documents inside a compendium at synchronous speed, some data is cached in the
pack.indexproperty. This includes each document'sname(helpful if you want to gopack.index.getName),img, andtype, alongside the_idand a constructeduuidproperty. Alongside the various methods of iterating on collections likefilter, this can allow you to synchronously prepare a list of documents without needing to perform a costly full-fetch. You can expand the cached information with thegetIndexmethod; thereadyhook is generally the most appropriate time to expand the indices this way.Creating Documents
Document creation in Foundry is performed through factory methods like
Item.create— normal class construction likenew Itemdoes not communicate with the database on the server to properly persist the created document.Document Data: The first argument to
createis the document data. The valid data for each document type is determined by the document's schema, which is validated by the server. You can see a full list of document typedefs on the types page. This document data should be JSON serializable (so no complex objects like classes or sets). Most fields for documents have initial values and are not required, but a few fields likenameandtypeare usually required. This document data is saved to the created document's_source. When Foundry makes changes to core document schemas it provides migrations and shims to help and minimize immediate breakage but it is still important to stay on top of any changes that do happen.Document Operation Parameters: The second argument to create methods is almost always
operations, which informs the operation. The two most important parameters areparentandpack, which help locate where you want to create the document;Item.createdefaults to creating a world item, but if you want the item to be inside an Actor you need to pass that actor as theparent, and if you want the item to be created inside of a compendium you need to provide the pack ID forpack. If you want to create an item inside an actor that is inside a compendium you do not need to pass both, justparentis required. There are other optional parameters as listed in the DatabaseCreateOperation typedef, and unlike document data you can define your own parameters — any JSON serializable properties will be kept throughout the full database transaction process.Batched Operations: If you want to create multiple documents at once in the same place,
createDocumentsis your friend — instead of a single data object, you provide an array of them. Alternatively, you can usecreateEmbeddedDocuments, which is an instance method of the parent that doesn't requireparentorpackbut does require you to specify the document type being created. If you need to operate in multiple locations, usefoundry.documents.modifyBatch.Updating Documents
Updating documents follows many of the same principles as document creation, but instead of a static factory method the
updatemethod is called on the document instance to be updated, e.g.item.update. If you're updating a nested property, Foundry accepts using dot-separated strings for the keys, e.g.actor.update({ "system.hp.value": 10 }). Like creation, updates have their ownoperationparameter which is defined by DatabaseUpdateOperation, but this is much more rarely needed for individual updates — Foundry will automatically fill in thepackandparentfor the document being updated.Special Operators: Document updates default to partial diffing (except for arrays, which are always fully replaced). To help Foundry has made the
_deland_replaceoperators globally available._delwill remove a key from an object (usually useful withTypedObjectField), while_replace(data)indicates that the properties should be fully replaced by the provideddataobject rather than performing a partial update.Flags: The
setFlagandunsetFlagmethods are wrappers forupdatethat can simplify the process of constructing the flag update while validating it's of the proper format. If you have a more complex update to perform it may be preferable to use the generalupdatemethod.Batched Operations: To update multiple documents at the same time it is necessary to use methods like
updateDocuments. This method takes an array of update data, which in addition to the data that is being updated also requires providing the matching_idfor each document so Foundry knows which documents are being updated. LikecreateDocuments, you must provideparentorpackas needed to specify where these updates are taking place. Alternatively, you can useupdateEmbeddedDocuments, which is an instance method of the parent that doesn't requireparentorpackbut does require you to specify the document type being updated. If these updates are occurring across multiple locations, leveragefoundry.documents.modifyBatch.Deleting Documents
Deleting an individual document is fairly simple; just call the
deletemethod from the document instance. You may want to usedeleteDialoginstead to obtain confirmation, since deletions are irreversible. Like creation, deletion has its ownoperationparameter which is defined by DatabaseDeleteOperation, but this is much more rarely needed for individual deletions — Foundry will automatically fill in thepackandparentfor the document being deleted.Batched Operations: To delete multiple documents at the same time it is necessary to use methods like
deleteDocuments. This method takes an array of document ids. LikecreateDocuments, you must provideparentorpackas needed to specify where these deletions are taking place. Alternatively, you can usedeleteEmbeddedDocuments, which is an instance method of the parent that doesn't requireparentorpackbut does require you to specify the document type being deleted. If these deletions are occurring across multiple locations, leveragefoundry.documents.modifyBatch.