10 KiB
Logseq DB Properties SDK Reference
Overview
Logseq has two storage modes:
- File-based: Properties stored as
key:: valuein markdown files - DB-based: Properties stored in SQLite with structured schema, types, and relationships
This guide covers DB-based properties API for plugin development.
Core Concepts
1. Property Schema (Global Definition)
Properties must be defined globally before use. Think of it as creating columns in a database.
2. Tags as Classes
Tags act as classes/types. Pages/blocks can be tagged to inherit property schemas.
3. Property Values
After defining properties, assign values to specific blocks/pages.
API Reference
Property Schema APIs
upsertProperty(name, options)
Define or update a property schema globally.
Parameters:
name(string): Property nameoptions(object):type:'default'|'node'|'date'|'number'cardinality:'one'|'many'(fornodetype)
Returns: Property object with uuid
Example:
// Simple text property
await logseq.Editor.upsertProperty('author', { type: 'default' })
// Reference property (links to other pages)
await logseq.Editor.upsertProperty('tags', {
type: 'node',
cardinality: 'many'
})
// Date property
await logseq.Editor.upsertProperty('publishedDate', { type: 'date' })
Tag (Class) Management APIs
createTag(name, options? )
Create a tag as a class/type definition.
Parameters:
name(string): Tag nameoptions(optional object):{ uuid: string }
Returns: Tag object with uuid and id
Example:
const bookTag = await logseq.Editor.createTag('book')
const articleTag = await logseq.Editor.createTag('article', {
uuid: 'custom-uuid-123'
})
getTag(name)
Retrieve an existing tag by name.
Returns: Tag object or null
Example:
const tag = await logseq.Editor. getTag('book')
if (! tag) {
// Tag doesn't exist, create it
}
addTagProperty(tagUUID, propertyName)
Add a property to a tag's schema. Pages tagged with this tag will have this property available.
Parameters:
tagUUID(string): Tag UUIDpropertyName(string): Property name (must be defined viaupsertPropertyfirst)
Example:
// Define properties globally
await logseq.Editor. upsertProperty('author', { type: 'default' })
await logseq.Editor.upsertProperty('isbn', { type: 'default' })
// Add properties to 'book' tag
const bookTag = await logseq. Editor.createTag('book')
await logseq.Editor.addTagProperty(bookTag.uuid, 'author')
await logseq.Editor.addTagProperty(bookTag.uuid, 'isbn')
Block/Page Property Value APIs
upsertBlockProperty(blockUUID, propertyName, value)
Set property value for a specific block or page.
Parameters:
blockUUID(string): Block or page UUIDpropertyName(string): Property namevalue(any): Property value- For
nodetype withcardinality: 'many': Use array of page IDs - For
nodetype withcardinality: 'one': Use single page ID - For other types: Use primitive values
- For
Example:
const page = await logseq.Editor.getPage('my-page-uuid')
// Set simple value
await logseq.Editor.upsertBlockProperty(page.uuid, 'author', 'John Doe')
// Set number
await logseq.Editor.upsertBlockProperty(page.uuid, 'year', 2024)
// Set single reference
await logseq.Editor. upsertBlockProperty(page.uuid, 'category', categoryPageID)
// Set multiple references
await logseq.Editor.upsertBlockProperty(
page.uuid,
'tags',
[tagPageID1, tagPageID2, tagPageID3]
)
addBlockTag(blockUUID, tagUUID)
Tag a block or page, making it an instance of that tag's class.
Parameters:
blockUUID(string): Block or page UUIDtagUUID(string): Tag UUID
Example:
const page = await logseq. Editor.createPage('Moby Dick')
const bookTag = await logseq.Editor.getTag('book')
await logseq.Editor.addBlockTag(page.uuid, bookTag.uuid)
Tag Inheritance
Set Tag Parent (Class Inheritance)
Invoke API of logseq.Editor.setTagExtends
Example:
// Create parent tag
const rootTag = await logseq.Editor.createTag('Media')
// Create child tag
const bookTag = await logseq.Editor.createTag('book')
// Set inheritance
await logseq.Editor.addTagExtends(bookTag.uuid, rootTag.uuid)
// Use special property `:logseq.property.class/extends` to create tag hierarchy.
// await logseq.Editor. upsertBlockProperty(
// bookTag.uuid,
// ':logseq.property.class/extends',
// [rootTag.id] // Use . id, not .uuid
// )
Special Properties
Hide Empty Values
const prop = await logseq.Editor. upsertProperty('optionalField', { type: 'default' })
await logseq.Editor.upsertBlockProperty(
prop.uuid,
': logseq.property/hide-empty-value',
true
)
Common Patterns
Pattern 1: Define a Schema with Class Hierarchy
async function setupBookSchema() {
// 1. Create root class
const mediaTag = await logseq.Editor.createTag('Media')
// 2. Define common properties
await logseq. Editor.upsertProperty('title', { type: 'default' })
await logseq.Editor.upsertProperty('year', { type: 'number' })
await logseq.Editor.upsertProperty('tags', { type: 'node', cardinality: 'many' })
// 3. Add properties to root class
await logseq. Editor.addTagProperty(mediaTag.uuid, 'title')
await logseq.Editor.addTagProperty(mediaTag.uuid, 'year')
await logseq.Editor. addTagProperty(mediaTag.uuid, 'tags')
// 4. Create subclass
const bookTag = await logseq.Editor.createTag('book')
await logseq.Editor.upsertBlockProperty(
bookTag.uuid,
':logseq.property.class/extends',
[mediaTag.id]
)
// 5. Add book-specific properties
await logseq.Editor.upsertProperty('author', { type: 'default' })
await logseq.Editor.upsertProperty('isbn', { type: 'default' })
await logseq.Editor.addTagProperty(bookTag.uuid, 'author')
await logseq.Editor.addTagProperty(bookTag.uuid, 'isbn')
}
Pattern 2: Create an Instance
async function createBookInstance(bookData) {
// 1. Create page with stable UUID
const pageUUID = generateStableUUID(bookData.id)
let page = await logseq.Editor.getPage(pageUUID)
if (!page) {
page = await logseq.Editor.createPage(
bookData.title,
{},
{ customUUID: pageUUID, redirect: false }
)
}
// 2. Tag the page
const bookTag = await logseq.Editor.getTag('book')
await logseq.Editor.addBlockTag(page.uuid, bookTag. uuid)
// 3. Set property values
await logseq. Editor.upsertBlockProperty(page.uuid, 'title', bookData.title)
await logseq.Editor.upsertBlockProperty(page.uuid, 'author', bookData.author)
await logseq.Editor.upsertBlockProperty(page.uuid, 'isbn', bookData.isbn)
await logseq.Editor.upsertBlockProperty(page.uuid, 'year', bookData.year)
return page. uuid
}
Pattern 3: Link Related Entities
async function linkBookToCategories(bookPageUUID, categoryKeys) {
// Get or create category pages
const categoryIDs = await Promise.all(
categoryKeys.map(async (key) => {
const catUUID = generateStableUUID(key)
let catPage = await logseq.Editor.getPage(catUUID)
if (!catPage) {
catPage = await logseq.Editor.createPage(key, {}, {
customUUID: catUUID
})
}
return catPage.id // Use . id for references
})
)
// Link book to categories
await logseq.Editor.upsertBlockProperty(
bookPageUUID,
'tags', // Must be 'node' type with 'many' cardinality
categoryIDs
)
}
Pattern 4: Batch Schema Setup from JSON
async function setupSchemaFromConfig(config) {
// config = { "book": { "fields": ["author", "isbn"], "parent": "Media" } }
for (const [tagName, tagConfig] of Object.entries(config)) {
let tag = await logseq.Editor.getTag(tagName)
if (!tag) {
tag = await logseq.Editor.createTag(tagName)
// Set parent if specified
if (tagConfig. parent) {
const parentTag = await logseq. Editor.getTag(tagConfig. parent)
if (parentTag) {
await logseq.Editor.upsertBlockProperty(
tag. uuid,
':logseq.property.class/extends',
[parentTag.id]
)
}
}
}
// Add properties
for (const fieldName of tagConfig.fields) {
await logseq.Editor.upsertProperty(fieldName, { type: 'default' })
await logseq.Editor.addTagProperty(tag.uuid, fieldName)
}
}
}
Best Practices
1. Use Stable UUIDs
Generate deterministic UUIDs from stable identifiers:
import { v5 as uuidv5 } from 'uuid'
const NAMESPACE = 'your-plugin-namespace-uuid'
function generateStableUUID(id: string): string {
return uuidv5(id, NAMESPACE)
}
2. Check Before Creating
Always check if tags/pages exist before creating:
let tag = await logseq.Editor.getTag('book')
if (!tag) {
tag = await logseq.Editor.createTag('book')
}
3. Property Definition Order
- Define property globally with
upsertProperty - Create tag with
createTag - Add property to tag with
addTagProperty - Create page and tag it with
addBlockTag - Set values with
upsertBlockProperty
4. Use . id for References
When setting node type properties, use .id not .uuid:
await logseq.Editor.upsertBlockProperty(
page. uuid,
'category',
categoryPage.id // ✅ Correct
// categoryPage.uuid // ❌ Wrong
)
5. Error Handling
Wrap API calls in try-catch for robustness:
try {
await logseq.Editor.upsertBlockProperty(uuid, 'field', value)
} catch (error) {
console.error(`Failed to set property: ${error}`)
// Handle gracefully
}
Real-World Example: Zotero Plugin
See complete implementation: xyhp915/logseq-zotero-plugin
Key techniques:
- Schema defined from
z_item_types. jsonmetadata - Tag hierarchy:
Zotero→book,article, etc. - Stable UUIDs via
v5(itemKey, namespace) - Relationship properties for collections
- Nested blocks for attachments
Reference
- Type Definitions: See
@logseq/libspackage - Examples: [logseq-zotero-plugin/handlers.ts](https://github.com/xyhp915/logseq-zotero-plugin/blob/main/src/handlers. ts)
- Search More: GitHub Code Search