Documentation
fenecdb is an embedded document database small enough to run inside a browser tab. It also keeps an app's file on a phone and serves any language over HTTP, a file per tenant; search is built in.
Try it now. The Playground runs the engine in this tab -- nothing to install. To put it to work, the Quickstart starts the server and runs a first query in a few commands.
What it is
Your documents live in one file, indexed as you declare, and you query them with one language, FenecQL — in the page, in an app, from Rust, or over HTTP from a server. A screen's live query runs again when a write changes what it reads, a replica on the device syncs with the server, and the server takes many clients' writes into the one file through one writer, their disk syncs shared, and many tenants into a file each.
create collection articles (
title text @text,
tags [text],
year int @hash,
published timestamp @sorted
)
get articles select title
where year >= 2024 and tags has "rust"
order published desc
limit 10
get articles select title, highlight(title)
match title $1 fuzzy
limit 10One file, and search built in
fenecdb keeps its documents in one file and indexes them — hash, unique, ordered, text, points and vectors. The indexes are held in memory and built from the documents, the first time a query reads each, and a vector graph is saved in the file too, so an open need not build it again. It answers queries from inside a browser tab, in an app, or from a server any language reaches over HTTP.
Everything in these docs describes the engine as it is built today. Where something costs you a capability, that cost is stated next to the feature rather than at the bottom of the page.
One file. Records are appended to immutable 8 MiB segments and the byte
sequence on disk is the byte sequence in memory, so a read decodes directly
over the arena. A HashMap<DocId, Loc> offset index is the
only helper structure in the whole store.
Search is not bolted on. @text is an index and
match a query clause, ranked by BM25, with fuzzy,
highlight() and facet beside it; for search by
meaning, vector<N> is a type, @hnsw an index
and near a clause. The planner knows them all, so a filter and a
search are planned together rather than stacked on each other.
Where to go next
Pick the route that matches what you are doing. The sidebar files pages by what they are instead, so a few sit under another heading there: FenecQL and From SQL are Reference, the place to start writing queries, and Build from source is Contribute. Every page is listed below the routes.
New here
- Playground
The real engine in this tab, with collections to browse and a FenecQL editor. Nothing to install.
- Quickstart
Start the server, write two rows, query and search them with curl and from your language, and see them in the studio.
- Quickstart: in the browser
The database in a page, with no server: a Vite project, one package and a script.
- Install
The install script, Homebrew, a .deb or .rpm, a tarball, the image, a package for your language, or a build from source.
- Your language
Python, JavaScript, Go, Rust, .NET, Java, PHP, Ruby, Swift, Kotlin and Dart: one program in each, tested against a real server.
- From SQL
The SQL it reads as written, what it refuses and what to write instead, and where the meaning is not SQL's.
Build an app
- FenecQL
The language: a tour from the first write to a lookup and a total, then a reference page for each family of statements.
- JavaScript and TypeScript
The browser client, the query builder and generated TypeScript types.
- Mobile and native apps
Swift, Kotlin and Flutter: a file on the device, live queries driving the UI.
- HTTP endpoint
Start a server and send it FenecQL from curl or any language's standard library: raw statements, batches and writes that are safe to retry. The reference has every route, status and stream, and security and scoped access the tokens and policies.
- Sync
A local replica that reads without the network and writes optimistically.
- Instead of Redis
Counters, rate limits, locks, sessions, leaderboards and more, each a recipe a test runs.
- Integrations
LangChain, LlamaIndex, the Vercel AI SDK and React.
- Agents and MCP
fenecdb as an agent's search and memory tool over the Model Context Protocol.
- Import
A collection from SQLite or a live PostgreSQL table in one command.
- Serverless and Cloudflare
A database in a Durable Object, or a function talking to a server.
Run it in production
- Server
Running
fenec-server: flags, tokens, durability, limits, containers. - Studio
The admin pages the server can serve: collections, rows, a query editor and the server's numbers.
- Replication
Read replicas, promotion, archives and backups restored to a moment, every tenant of a node archived at once.
- Tenants and sharding
A file per tenant behind a router: two nodes and a router on one machine, a tenant, and a node lost and failed over. The guides, the reference and the design follow it.
- Monitoring
Metrics, dashboards, a slow-statement log, an audit log and traces.
- Datadog and others
Under Datadog, New Relic, Elastic and Grafana Cloud: the metrics check, the logs, a dashboard and traces.
The details
- Limits
What it does not do, and every hard-coded ceiling.
- File format
The layout of a file: its record kinds, their bodies, and why each exception exists.
- How it works
Segments, the mapped file, one writer, and why fenecdb has no buffer pool of its own; the four ways it ships and how the crates fit together.
- Benchmarks
YCSB against SQLite, PostgreSQL, MongoDB and Redis, writers beside readers, requests, text and vector search, and sizes, each family on a page with its method.
- Compare
Beside PostgreSQL, SQLite and PGlite: when to choose each.
- Build from source
Clone the repository, build with cargo and make, run the tests.
Every page
The sidebar's pages, grouped as it groups them.
Clients
Guides
Reference
Tenants and sharding
Design and comparisons
Benchmarks
Contribute
Coding agents read the same pages as text: llms.txt is a brief with the rules FenecQL does not share with SQL, and llms-full.txt is every page in one file.
What it gave up
Staying small meant leaving some things out on purpose. The main ones:
- One writer. Many readers, but one write or batch at a time.
- No SQL dialect, and a join only on a key. Every client speaks FenecQL, over HTTP or in process; it reads SQL's everyday spellings as the statements they mean, a
JOINon an indexed key among them (From SQL). - No decimals. Store money as cents.
What it gave up, one by one
The list is short, and it is made of consequences of the design rather than a roadmap.
- One writer, no MVCC. A write, or a
/batchof them, holds the write lock for as long as it runs and lands whole or not at all; readers read beside it. There are no interactive transactions held open across requests. - A join only on a key. A query reads one collection, plus the
children
lookupattaches to each of its rows — and, chained, the children of those — and filters by another's rows only through an uncorrelatedin (get …). SQL'sJOINon an indexed key reads as one of those, answered flat; a join of any other condition is none. - No SQL dialect. FenecQL is the one language, in the page, in
Rust and over HTTP. It reads
CREATE TABLE,INSERT … VALUES,UPDATE … SET,JOIN,IN (…),EXISTS,BETWEEN,LIKEandON CONFLICTas the statements they mean and refuses the rest saying what to write; there is no wire protocol for SQL drivers, and no ORM speaks it. - No multi-writer replication, and no change of a field's type in place: it would rewrite every document under the write lock.
- No decimal type.
floatis binary floating point and unfit for money; the answer is anintin cents. - Objects only in a
jsonfield. A typed field refuses one and a list holds one type; ajsonfield holds any value JSON does, and a path reads into it.
The full accounting, including every hard-coded ceiling, is in Limits.
How it ships — the browser module, the shell, the server and the Rust library, with what each weighs — and how the crates fit together are on How it works.