Set up your app for Hypertill DB
The cleanest structure is still the best one: keep database code in a dedicated db/ folder and keep UI code out of it.
src/
db/
schema.ts
models.ts
migrations.ts
index.ts
App.tsx
The examples below use a small library app with Book and Chapter models. The rest of the docs reuse that same example so the API stays easy to follow.
1. Define your schema
Create src/db/schema.ts:
import { appSchema, tableSchema } from '@hypertill/db'
export const schema = appSchema({
version: 1,
tables: [
tableSchema({
name: 'books',
columns: [
{ name: 'title', type: 'string' },
{ name: 'author', type: 'string' },
{ name: 'status', type: 'string' },
],
}),
tableSchema({
name: 'chapters',
columns: [
{ name: 'book_id', type: 'string', isIndexed: true },
{ name: 'title', type: 'string' },
{ name: 'position', type: 'number' },
],
}),
],
})
If you bootstrap with createPlatformAdapter(), Hypertill DB injects created_at, updated_at, deleted_at, and the *_tz metadata columns for you. You only need to define your business columns here.
If you need migrations, keep them in src/db/migrations.ts and pass them to the adapter in the next step.
2. Define your models
Create src/db/models.ts:
import { Model, Query, Relation } from '@hypertill/db'
import { children, field, relation, text } from '@hypertill/db/decorators'
export class Book extends Model {
static table = 'books'
static associations = {
chapters: { type: 'has_many', foreignKey: 'book_id' },
} as const
@text('title') title!: string
@text('author') author!: string
@text('status') status!: string
@children('chapters') chapters!: Query<Chapter>
}
export class Chapter extends Model {
static table = 'chapters'
static associations = {
books: { type: 'belongs_to', key: 'book_id' },
} as const
@field('book_id') bookId!: string
@text('title') title!: string
@field('position') position!: number
@relation('books', 'book_id') book!: Relation<Book>
}
export const modelClasses = [Book, Chapter]
Every persisted model already exposes reserved createdAt, updatedAt, and deletedAt getters on Model. When the matching metadata columns exist, those getters return Date | null, so you do not repeat timestamp decorators on every model.
3. Create the database
Create src/db/index.ts:
import { createPlatformAdapter, Database } from '@hypertill/db'
import { modelClasses } from './models'
import { schema } from './schema'
const adapter = createPlatformAdapter({
schema,
dbName: 'library',
})
export const database = new Database({
adapter,
modelClasses,
})
createPlatformAdapter() is the current recommended bootstrap:
- native and Node.js targets use SQLite
- web targets use LokiJS
- default metadata columns are injected for you:
created_at,updated_at,deleted_at, and*_tz
If you need lower-level adapter options, pass them through sqlite or loki inside createPlatformAdapter(...).
If you have a migrations.ts file, pass it as migrations in the same object.
4. Provide the database to React
Wrap your app once in DatabaseProvider:
import { DatabaseProvider } from '@hypertill/db/react'
import { database } from './src/db'
import { LibraryScreen } from './src/LibraryScreen'
export default function App() {
return (
<DatabaseProvider database={database}>
<LibraryScreen />
</DatabaseProvider>
)
}
5. Use the current React split
At this point your app is ready for the package's current React pattern:
- reactive reads with
hooks - writes and imperative work with
useDatabase - custom reactive composition with
withObservableswhen the hooks are not enough
Hook names are generated from your model class names:
Bookgives youhooks.useBook()andhooks.useBooks()Chaptergives youhooks.useChapter()andhooks.useChapters()
Continue with Connecting Components for the React side.