MDX.org.ai
Structured Datamdxdb

Getting Started

Set up mdxdb for your project

Getting Started with mdxdb

This guide walks you through setting up mdxdb for your project.

Choose an Adapter

First, decide which storage backend fits your needs:

AdapterBest For
@mdxdb/fsLocal development, static sites, Git-based workflows
@mdxdb/sqliteEmbedded applications, edge computing, serverless
@mdxdb/clickhouseAnalytics, time-series data, large datasets

Installation

Each adapter is a standalone package. Install the one you need:

npm install @mdxdb/fs      # File system
npm install @mdxdb/sqlite  # SQLite

The mdxdb root package is the schema-first DB() facade that picks an adapter from DATABASE_URL. Install it too if you want typed entities and relationships instead of the document-level API shown below:

npm install mdxdb
import { DB } from 'mdxdb'
 
const db = DB({
  Post: { title: 'string', content: 'markdown', author: 'Author.posts' },
  Author: { name: 'string', email: 'string' },
})
 
const post = await db.Post.get('hello-world')

Basic Setup

File System

import { createDatabase } from '@mdxdb/fs'
 
const db = createDatabase({
  path: './content',
  // Optional: file extension
  extension: '.mdx',
})

SQLite

import { createDatabase } from '@mdxdb/sqlite'
 
const db = createDatabase({
  // In-memory database
  filename: ':memory:',
 
  // Or persistent file
  // filename: './data/content.db',
})

ClickHouse

import { createClickHouseDatabase } from '@mdxdb/clickhouse'
 
const db = await createClickHouseDatabase({
  url: process.env.CLICKHOUSE_URL,
})

CRUD Operations

Create

import { parse } from 'mdxld'
 
// From MDX string
const doc = parse(`---
$type: Article
title: My First Article
author: Jane Doe
publishedAt: 2024-01-15
tags:
  - tutorial
  - mdx
---
 
# My First Article
 
Welcome to my article!
 
## Introduction
 
This is the introduction...
`)
 
await db.set('articles/my-first-article', doc)

Read

// Get single document
const doc = await db.get('articles/my-first-article')
 
if (doc) {
  console.log(doc.data.title)  // 'My First Article'
  console.log(doc.content)      // '# My First Article...'
}
 
// Get without content (metadata only)
const meta = await db.get('articles/my-first-article', {
  includeContent: false,
})

Update

const doc = await db.get('articles/my-first-article')
 
// Modify
doc.data.title = 'Updated Title'
doc.data.updatedAt = new Date().toISOString()
doc.content += '\n\n## New Section\n\nAdded content...'
 
// Save
await db.set('articles/my-first-article', doc, {
  overwrite: true,
})

Delete

const result = await db.delete('articles/old-article')
 
console.log(result.success)  // true
console.log(result.path)     // 'articles/old-article'

Listing Documents

// List all documents
const all = await db.list()
 
// List with prefix
const articles = await db.list('articles/')
 
// With pagination
const page1 = await db.list('articles/', {
  limit: 10,
  offset: 0,
  orderBy: 'createdAt',
  order: 'desc',
})
 
// Filter by type
const blogPosts = await db.list('', {
  type: 'BlogPost',
})

Searching

// Basic search
const results = await db.search('typescript')
 
// Search specific fields
const titleSearch = await db.search('tutorial', {
  fields: ['title'],
  limit: 5,
})
 
// Search with type filter
const articleSearch = await db.search('react hooks', {
  type: 'Article',
  fields: ['title', 'content'],
})

Organizing Content

By Type

content/
├── articles/
│   ├── hello-world.mdx
│   └── typescript-tips.mdx
├── tutorials/
│   ├── getting-started.mdx
│   └── advanced-patterns.mdx
└── docs/
    ├── api/
    │   └── reference.mdx
    └── guides/
        └── installation.mdx

By Date

content/
└── posts/
    ├── 2024/
    │   ├── 01/
    │   │   └── hello-world.mdx
    │   └── 02/
    │       └── february-update.mdx
    └── 2023/
        └── 12/
            └── year-in-review.mdx

TypeScript Types

Every adapter exports the shared Database interface and its option/result types:

import type {
  Database,
  DatabaseConfig,
  GetOptions,
  SetOptions,
  ListOptions,
  SearchOptions,
  ListResult,
  SearchResult,
  MDXLDDocument,
} from '@mdxdb/fs'
 
// Type your documents
interface Article {
  $type: 'Article'
  title: string
  author: string
  publishedAt: string
  tags?: string[]
}
 
async function getArticle(path: string) {
  const doc = await db.get(path)
  return doc as MDXLDDocument & { data: Article }
}

Error Handling

get() resolves to null for a missing document; it does not throw. Writes that violate an option (for example set() on an existing path without overwrite) reject with a plain Error:

const doc = await db.get('articles/missing')
if (!doc) {
  console.log('Document not found')
}
 
try {
  // Rejects: the path already exists and `overwrite` was not set
  await db.set('articles/my-first-article', parse('# Duplicate'))
} catch (error) {
  console.error('Database error:', (error as Error).message)
}

The schema-first DB() facade in mdxdb also returns null from get(), and throws typed errors from writes:

import { DB, DatabaseError, EntityNotFoundError } from 'mdxdb'
 
try {
  await db.Post.update('missing', { title: 'New title' })
} catch (error) {
  if (error instanceof EntityNotFoundError) {
    console.log('Post not found')
  } else if (error instanceof DatabaseError) {
    console.error('Database error:', error.message)
  }
}

Next Steps

On this page