From logseq-db-plugin-api
Essential knowledge for developing Logseq plugins for DB (database) graphs. Layered: (1) authoritative upstream docs mirrored from logseq/logseq master, (2) production-tested patterns from logseq-checklist v1.0.0, (3) related skills (Datascript schema, Electron debugging). Covers core APIs, event-driven updates, multi-layered tag detection, property iteration, advanced query patterns.
How this skill is triggered — by the user, by Claude, or both
Slash command
/logseq-db-plugin-api:logseq-db-plugin-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Comprehensive guidance for building Logseq plugins for **DB (database) graphs**, organized into three layers: authoritative upstream documentation, production-tested patterns, and related sibling skills.
references/core-apis.mdreferences/event-handling.mdreferences/logseq-official/AGENTS.mdreferences/logseq-official/LICENSEreferences/logseq-official/README.mdreferences/logseq-official/db_properties_guide.mdreferences/logseq-official/db_properties_skill.mdreferences/logseq-official/db_query_guide.mdreferences/logseq-official/db_tag_property_idents_notes.mdreferences/logseq-official/experiments_api_guide.mdreferences/logseq-official/starter_guide.mdreferences/pitfalls-and-solutions.mdreferences/plugin-architecture.mdreferences/property-management.mdreferences/queries-and-database.mdreferences/tag-detection.mdComprehensive guidance for building Logseq plugins for DB (database) graphs, organized into three layers: authoritative upstream documentation, production-tested patterns, and related sibling skills.
This skill provides essential knowledge for building Logseq plugins that work with the new DB graph architecture. It covers:
Use this skill when developing Logseq plugins that:
| Aspect | Markdown Graphs | DB Graphs |
|---|---|---|
| Data Storage | Files (.md) | Database (SQLite) |
| Properties | YAML frontmatter | Typed database entities |
| Tags | Simple text markers | Classes with schemas |
| Queries | File-based attributes | Datalog / Database relationships |
| Property Access | Text parsing | Namespaced keys (:user.property/name) |
Precedence: Layer 1 is authoritative ground truth for API contracts. Layer 2 adds production-validated context and patterns not covered by official docs. When they conflict, Layer 1 wins on API facts; Layer 2 wins on real-world pitfalls (things that work on paper but fail in practice).
Source: mirrored verbatim from logseq/logseq libs/development-notes/ via scripts/sync-logseq-docs.sh. Each file carries a footer recording upstream commit SHA and fetch timestamp. License: AGPL-3.0 (see references/logseq-official/LICENSE).
| File | Covers |
|---|---|
references/logseq-official/AGENTS.md | AI-agent development guide — SDK repo structure, core patterns, conventions |
references/logseq-official/starter_guide.md | Plugin setup walkthrough: Node/TypeScript install, Logseq dev environment, hello world |
references/logseq-official/db_properties_skill.md | DB properties SDK reference — schema definition, tags-as-classes, property operations |
references/logseq-official/db_properties_guide.md | File graph vs DB graph properties — text vs typed entities, SDK API differences |
references/logseq-official/db_query_guide.md | Datascript query guide — logseq.DB.q, datascriptQuery, parameterized Datalog |
references/logseq-official/db_tag_property_idents_notes.md | Ident system — namespace conventions (:logseq.property/, :plugin.property.*), when idents apply |
references/logseq-official/experiments_api_guide.md | logseq.Experiments — React integration, custom renderers, script loading, ClojureScript interop |
Refresh: bash scripts/sync-logseq-docs.sh from repo root. Idempotent — no-op if upstream HEAD matches .last-synced-sha.
Battle-tested code from real-world plugin development. All patterns validated through logseq-checklist v1.0.0.
Tag Detection — Reliable multi-layered detection
Three-tier approach (content → datascript → properties) for maximum reliability when block.properties.tags fails.
Search for: hasTag, block.properties.tags undefined, multi-layered
Pitfalls & Solutions — Errors and fixes discovered in production
Tag creation validation, property conflicts, query syntax mistakes, or-join variable mismatches, method-name errors.
Search for: validation errors, query returns no results, addTag not a function
Event Handling — DB.onChanged patterns Database change detection, datom filtering, debouncing strategies. Essential for plugins that maintain derived state.
Search for: DB.onChanged, debouncing, transaction datoms
Property Management — Reading property values Iteration patterns for unknown property names, type-based detection, namespaced key access.
Search for: property iteration, namespaced keys, :user.property/
Core APIs — Essential methods Tag/class management, page/block creation, property operations, icons, utilities.
Search for: createTag, addBlockTag, upsertProperty, createPage
Queries and Database — Datalog patterns
Query syntax, common patterns, caching strategies, tag inheritance with or-join, :block/title vs :block/name.
Search for: datascriptQuery, datalog, caching, or-join, tag inheritance
Plugin Architecture — Best practices File organization, settings registration, error handling, testing strategy, deployment checklist.
Search for: file organization, settings schema, production patterns
For specialized concerns, defer to sibling skills with their own activation triggers:
| Skill | Use for |
|---|---|
logseq-schema (RCmerci) | Authoritative Datascript schema reference when writing Datalog queries — covers entity attributes, relationships, cardinality. Install from github.com/RCmerci/skills. |
logseq-electron-debug (RCmerci) | Chrome DevTools against a running Logseq app — useful when debugging your plugin's runtime behavior. Install from github.com/RCmerci/skills. |
logseq-db-knowledge | Foundational DB graph concepts — use alongside this skill for understanding why DB graphs work the way they do. |
logseq-cli-skill | Logseq CLI usage — Datalog queries run from shell, useful for bulk operations outside plugins. |
mkdir my-logseq-plugin
cd my-logseq-plugin
pnpm init
pnpm add @logseq/libs
pnpm add -D typescript vite vite-plugin-logseq @types/node
mkdir src
src/index.ts — Entry point:
import '@logseq/libs'
async function main() {
console.log('Plugin loaded')
// Register settings, initialize features
}
logseq.ready(main).catch(console.error)
vite.config.ts:
import { defineConfig } from 'vite'
import logseqDevPlugin from 'vite-plugin-logseq'
export default defineConfig({
plugins: [logseqDevPlugin()],
build: { target: 'esnext', minify: 'esbuild', sourcemap: true }
})
package.json:
{
"name": "my-logseq-plugin",
"version": "0.0.1",
"main": "dist/index.js",
"scripts": { "build": "vite build", "dev": "vite build --watch" },
"logseq": { "id": "my-logseq-plugin", "title": "My Logseq Plugin", "main": "dist/index.html" }
}
pnpm run dev # Watch mode
pnpm run build # Production build
# Load plugin: Settings → Plugins → Load unpacked plugin
Properties in DB graphs are stored as namespaced keys on block objects:
const block = await logseq.Editor.getBlock(uuid)
// Direct access
const value = block[':user.property/myProperty']
// Iteration (if name unknown)
for (const [key, value] of Object.entries(block)) {
if (key.startsWith(':user.property/')) { /* ... */ }
}
CRITICAL: block.properties.tags and block.properties[name] are often unreliable. Use direct key access or iteration instead.
Simple property checks fail. Use multi-layered detection — see references/tag-detection.md for the full pattern.
// Tier 1: Content check (fast)
if (block.content.includes('#mytag')) return true
// Tier 2: Datascript query (reliable)
const results = await logseq.DB.datascriptQuery(
`[:find (pull ?b [*]) :where [?b :block/tags ?t] [?t :block/title "mytag"]]`
)
// Tier 3: Properties fallback (rarely works)
if (block.properties?.tags?.includes('mytag')) return true
For plugins that maintain derived state:
if (logseq.DB?.onChanged) {
logseq.DB.onChanged((changeData) => {
const { txData } = changeData
for (const [entityId, attribute, value, txId, added] of txData) {
if (attribute.includes('property')) scheduleUpdate(entityId)
}
})
}
See references/event-handling.md for debouncing strategies.
Always define property types before using them:
await logseq.Editor.upsertProperty('title', { type: 'string' })
await logseq.Editor.upsertProperty('year', { type: 'number' })
await logseq.Editor.upsertProperty('published', { type: 'checkbox' })
await logseq.Editor.upsertProperty('modifiedAt', { type: 'datetime' })
await logseq.Editor.createPage('Item', {
title: 'My Item',
year: 2024,
published: true,
modifiedAt: Date.now()
})
// 1. Create tag
const tag = await logseq.Editor.createTag('zot')
// 2. Define properties FIRST
await logseq.Editor.upsertProperty('title', { type: 'string' })
await logseq.Editor.upsertProperty('author', { type: 'string' })
await logseq.Editor.upsertProperty('year', { type: 'number' })
// 3. Add properties to tag schema (parent frame API)
const parentLogseq = (window as any).parent?.logseq
await parentLogseq.api.add_tag_property(tag.uuid, 'title')
await parentLogseq.api.add_tag_property(tag.uuid, 'author')
await parentLogseq.api.add_tag_property(tag.uuid, 'year')
// 4. Create page with tag and properties
await logseq.Editor.createPage('My Item', {
tags: ['zot'],
title: 'Paper Title',
author: 'Jane Doe',
year: 2024
})
const query = `
{:query [:find (pull ?b [*])
:where
[?b :block/tags ?t]
[?t :block/title "zot"]]}
`
const results = await logseq.DB.datascriptQuery(query)
Tag Hierarchies (items tagged with #task OR any tag extending #task):
const query = `
{:query [:find (pull ?b [*])
:where
(or-join [?b]
(and [?b :block/tags ?t]
[?t :block/title "task"])
(and [?b :block/tags ?child]
[?child :logseq.property.class/extends ?parent]
[?parent :block/title "task"]))]}
`
See references/queries-and-database.md for advanced patterns.
const pendingUpdates = new Set<string>()
let updateTimer: NodeJS.Timeout | null = null
function handleDatabaseChanges(changeData: any): void {
const txData = changeData?.txData || []
for (const [entityId, attribute, value, txId, added] of txData) {
if (attribute.includes('property')) {
pendingUpdates.add(String(entityId))
if (updateTimer) clearTimeout(updateTimer)
updateTimer = setTimeout(async () => {
for (const id of pendingUpdates) await updateBlock(id)
pendingUpdates.clear()
}, 300)
}
}
}
File Structure:
src/
├── index.ts # Entry point, initialization
├── events.ts # DB.onChanged handlers, debouncing
├── logic.ts # Pure business logic (testable)
├── settings.ts # Settings schema and accessors
└── types.ts # TypeScript interfaces
Settings Registration:
import { SettingSchemaDesc } from '@logseq/libs/dist/LSPlugin.user'
const settings: SettingSchemaDesc[] = [
{
key: 'tagName',
type: 'string',
title: 'Tag Name',
description: 'Tag to monitor',
default: 'mytag'
}
]
logseq.useSettingsSchema(settings)
See references/plugin-architecture.md for error handling, testing, and deployment.
addBlockTag() not addTag()block.properties.tags — iterate namespaced keys:block/title not :db/ident for custom tagscreated, modified — use dateAdded, dateModifiedYYYY-MM-DD for date propertiesgetPage()See references/pitfalls-and-solutions.md for detailed solutions.
When encountering issues:
logseq-electron-debug skill (RCmerci) — for debugging Logseq itselfThree layers, in order of priority:
Load the files you need for the current task. Layer 1 answers "what does the API do"; Layer 2 answers "what breaks in practice"; Layer 3 answers adjacent concerns that deserve their own skill activation.
npx claudepluginhub kerim/spellbook --plugin logseq-db-plugin-apiProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.