Fortune.js
Fortune.js is a non-native graph database abstraction layer designed for both Node.js and web browsers, currently at stable version 5.5.19. It provides an application-level implementation of graph-like features, including bi-directional relationships, inverse updates, and referential integrity, all built upon a user-defined data model. The library maintains a steady release cadence, with recent updates focusing on dependency management. Key differentiators include its ability to enable portable storage options and facilitate the sharing of identical data models across server and client environments, streamlining full-stack development. While the core module handles data modeling and relationship management, persistent storage is achieved via optional, separate database adapters for systems like MongoDB, Postgres, or IndexedDB. It avoids traditional ORM complexities by treating records as plain data structures and managing relationships at the application level.
Common errors
-
ERR_REQUIRE_ESM: require() of ES Module ... not supported.
cause Attempting to use `require()` to import Fortune.js (or one of its ESM-only plugins) in a Node.js project configured for ES Modules, or when the package itself only provides an ESM entry point.fixIf your project uses ES Modules (e.g., `"type": "module"` in `package.json`), use `import fortune from 'fortune'` instead of `const fortune = require('fortune')`. Ensure that any plugins you use are imported correctly based on their module type. -
SyntaxError: Cannot use import statement outside a module
cause Attempting to use `import` statements for Fortune.js (or any other module) in a CommonJS context (e.g., a `.js` file without `"type": "module"` in `package.json` in Node.js, or in older browser environments without module support).fixFor CommonJS environments, use `const fortune = require('fortune')`. If you intend to use ES Modules, ensure your project is configured for it (e.g., by adding `"type": "module"` to your `package.json` or using `.mjs` file extensions). -
TypeError: Invalid type for field 'yourFieldName'
cause A field in your record type definition has an invalid type. Fortune.js expects specific native JavaScript types (String, Number, Boolean, Date, Object, Buffer) or a custom type function. Nested object structures must explicitly use the `Object` type, not nested inline definitions.fixEnsure all fields in your record type definitions use one of the allowed native types (String, Number, Boolean, Date, Object, Buffer) or a correctly defined custom type function. For nested data, declare the field as `Object`. For example, `coordinates: Object` instead of `coordinates: { lat: Number, lon: Number }`.
Warnings
- breaking Version 5.0.0 extracted several core functionalities (IndexedDB adapter, HTTP, and WebSocket implementations) into separate npm packages. The `fortune.net` namespace was removed. For example, `fortune-http` must now be explicitly imported and used instead of `fortune.net.http`. The IndexedDB adapter is no longer the default for browsers and must be manually specified.
- breaking Version 2.0.0 introduced several breaking changes including the removal of `Serializer` from the core (moved to `net.http.Serializer`), changes to serializer configuration to accept arrays, and changes in how adapters and transform functions are defined (now also as arrays). The `fortune.request` options object format was also modified to match `fortune.net.request`.
- gotcha The primary key `id` is a reserved field name for all record types and should not be explicitly defined in your schema. Fortune.js automatically manages this field for unique identification.
- gotcha Fortune.js automatically manages inverse relationship updates and referential integrity. Changes to a record's links will automatically affect related records by denormalizing relationships, meaning updates can cascade across your data model. This behavior is by design but requires awareness.
- gotcha By default, Fortune.js uses an in-memory database for persistence, which is suitable for development but does not persist data beyond the application lifecycle. For production use or persistent storage, you must explicitly configure a database adapter (e.g., `fortune-mongodb`, `fortune-postgres`, `fortune-indexeddb`).
Install
-
npm install fortune -
yarn add fortune -
pnpm add fortune
Imports
- fortune
const fortune = require('fortune')import fortune from 'fortune'
- message
import { message } from 'fortune'import fortune from 'fortune'; const { message } = fortune; - Adapter
import { Adapter } from 'fortune'import fortune from 'fortune'; const { Adapter } = fortune;
Quickstart
const fortune = require('fortune') // Or use `import fortune from 'fortune';` in an ESM project.
// Define record types for a micro-blogging service
const store = fortune({
user: {
name: String,
// Many-to-many inversely related fields
following: [ Array('user'), 'followers' ],
followers: [ Array('user'), 'following' ],
// One-to-many relationship
posts: [ Array('post'), 'author' ]
},
post: {
message: String,
// Many-to-one relationship
author: [ 'user', 'posts' ]
}
})
// Example: Create a user and a post, then find them
(async () => {
// Create a user record
const [ user ] = await store.create('user', [
{ id: 'user123', name: 'Alice' }
]);
console.log('Created User:', user);
// Create a post record associated with the user
const [ post ] = await store.create('post', [
{ id: 'post456', message: 'Hello Fortune.js!', author: user.id }
]);
console.log('Created Post:', post);
// Find the user and include their posts
const foundUsers = await store.find('user', ['user123'], null, [['posts']]);
console.log('Found Users with posts:', JSON.stringify(foundUsers, null, 2));
// Find the post and include its author
const foundPosts = await store.find('post', ['post456'], null, [['author']]);
console.log('Found Posts with author:', JSON.stringify(foundPosts, null, 2));
// Disconnect the store (important if using a persistent adapter)
await store.disconnect();
})().catch(console.error);