Skip to content

Commit 44fafc0

Browse files
committed
2 parents 1a414f1 + 2029a89 commit 44fafc0

3 files changed

Lines changed: 80 additions & 5 deletions

File tree

content/docs/guides/driver-configuration.mdx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,33 @@ new InMemoryDriver();
182182
Use the memory driver for unit tests. It requires no setup and runs instantly.
183183
</Callout>
184184

185+
## `apps/objectos` and Single-Project Local Mode
186+
187+
When running `apps/objectos` (or any boot stack with `runtime: { cloudUrl: 'local' }`),
188+
two databases are involved:
189+
190+
| DB | Purpose | Env var |
191+
|--------------|-----------------------------------------------------------|---------------------------------|
192+
| Project DB | Your business data (the records served at `/api/v1/data/*`) | `OS_DATABASE_URL` / `OS_DATABASE_DRIVER` |
193+
| Control DB | Framework bookkeeping (`sys_organization`, `sys_project`, …) | `OS_CONTROL_DATABASE_URL` |
194+
195+
Both honor the same URL-scheme inference described above. If
196+
`OS_CONTROL_DATABASE_URL` is unset, the control DB defaults to a local
197+
SQLite file (`<dataDir>/control.db`); for backward compatibility
198+
`OS_DATABASE_URL` is also accepted as a fallback for the control DB
199+
when no explicit `OS_CONTROL_DATABASE_URL` is provided **and** no project DB
200+
URL has consumed it.
201+
202+
```bash
203+
# Run apps/objectos with MongoDB as the project DB (control DB stays sqlite):
204+
OS_DATABASE_URL=mongodb://localhost:27017/objectos pnpm dev
205+
206+
# Pin both DBs explicitly:
207+
OS_DATABASE_URL=mongodb://localhost:27017/objectos \
208+
OS_CONTROL_DATABASE_URL=postgres://user:pass@host/control \
209+
pnpm dev
210+
```
211+
185212
## Multi-Datasource
186213

187214
ObjectStack supports multiple data sources. Objects can target specific datasources:

packages/services/service-cloud/src/cloud-stack.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,20 @@ export async function createCloudStack(config: CloudStackConfig): Promise<{
8181
apiPrefix,
8282
} = config;
8383

84+
// Resolve the control-plane DB URL.
85+
// Priority:
86+
// 1. OS_CONTROL_DATABASE_URL (explicit, dedicated to the control plane)
87+
// 2. controlDriverUrl (explicit param from the calling stack)
88+
// 3. OS_DATABASE_URL (legacy alias — only used here when no
89+
// higher-priority source is set; reserved
90+
// going forward for the project's data DB)
91+
// 4. TURSO_DATABASE_URL (legacy alias)
92+
// 5. file:./.objectstack/data/control.db (default)
93+
const explicitControlUrl = process.env.OS_CONTROL_DATABASE_URL?.trim();
94+
const legacyControlUrl = (process.env.OS_DATABASE_URL || process.env.TURSO_DATABASE_URL)?.trim();
8495
const controlDriverPromise = buildControlDriver(
85-
(process.env.OS_DATABASE_URL || process.env.TURSO_DATABASE_URL)?.trim() || controlDriverUrl,
86-
process.env.OS_DATABASE_AUTH_TOKEN || process.env.TURSO_AUTH_TOKEN || controlDriverAuthToken,
96+
explicitControlUrl || controlDriverUrl || legacyControlUrl || `file:${resolvePath(process.cwd(), '.objectstack/data/control.db')}`,
97+
process.env.OS_CONTROL_DATABASE_AUTH_TOKEN || process.env.OS_DATABASE_AUTH_TOKEN || process.env.TURSO_AUTH_TOKEN || controlDriverAuthToken,
8798
);
8899

89100
// Default base plugins (per-project kernel — business data only).

packages/services/service-cloud/src/runtime-stack.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,29 @@ import { resolveAuthSecret, resolveBaseUrl } from './boot-env.js';
3434
import type { AppBundleResolver } from './project-kernel-factory.js';
3535
import { createObjectOSStack } from './objectos-stack.js';
3636

37+
/**
38+
* Infer the storage driver type from a database connection-URL scheme.
39+
* Returns `''` if the URL is empty or the scheme is unrecognised.
40+
*
41+
* mongodb://, mongodb+srv:// → 'mongodb'
42+
* postgres://, postgresql:// → 'postgres'
43+
* mysql://, mysql2:// → 'mysql'
44+
* libsql://, https://*.turso.* → 'turso'
45+
* file:, sqlite:, :memory:, *.db,
46+
* *.sqlite, *.sqlite3 → 'sqlite'
47+
*/
48+
function inferDriverFromUrl(url: string | undefined): string {
49+
if (!url) return '';
50+
const u = url.trim();
51+
if (/^mongodb(\+srv)?:\/\//i.test(u)) return 'mongodb';
52+
if (/^postgres(ql)?:\/\//i.test(u)) return 'postgres';
53+
if (/^mysql2?:\/\//i.test(u)) return 'mysql';
54+
if (/^libsql:\/\//i.test(u)) return 'turso';
55+
if (/^https?:\/\//i.test(u) && /\.turso\./i.test(u)) return 'turso';
56+
if (/^file:/i.test(u) || /^sqlite:/i.test(u) || u === ':memory:' || /\.(db|sqlite|sqlite3)$/i.test(u)) return 'sqlite';
57+
return '';
58+
}
59+
3760
/**
3861
* Default ObjectStack Cloud base URL — the local `apps/cloud` instance
3962
* running on port 4000. Override via `OS_CLOUD_URL` (or
@@ -124,8 +147,22 @@ export async function createRuntimeStack(config?: RuntimeStackConfig): Promise<R
124147
const dataDir = cfg.dataDir ?? resolvePath(cwd, '.objectstack/data');
125148
mkdirSync(dataDir, { recursive: true });
126149

127-
const controlDbUrl = `file:${resolvePath(dataDir, 'control.db')}`;
128-
const projectDbUrl = `file:${resolvePath(dataDir, `${projectId}.db`)}`;
150+
// Control-plane DB. In single-project local mode this is the framework's
151+
// bookkeeping DB (sys_organization / sys_project / …). It defaults to a
152+
// local SQLite file. Users can override with `OS_CONTROL_DATABASE_URL`
153+
// (preferred); `OS_DATABASE_URL` is reserved for the *project's* data.
154+
const controlDbUrl = process.env.OS_CONTROL_DATABASE_URL?.trim()
155+
|| `file:${resolvePath(dataDir, 'control.db')}`;
156+
157+
// Project DB. This is the user's business-data DB. When `OS_DATABASE_URL`
158+
// is set, honour it (and infer the driver from its scheme unless
159+
// `OS_DATABASE_DRIVER` overrides). Otherwise fall back to a local
160+
// SQLite file beside `control.db`.
161+
const envProjectDbUrl = process.env.OS_DATABASE_URL?.trim();
162+
const projectDbUrl = envProjectDbUrl || `file:${resolvePath(dataDir, `${projectId}.db`)}`;
163+
const projectDbDriver = (process.env.OS_DATABASE_DRIVER?.trim().toLowerCase())
164+
|| inferDriverFromUrl(projectDbUrl)
165+
|| 'sqlite';
129166

130167
const authSecret = cfg.authSecret ?? resolveAuthSecret();
131168
const baseUrl = cfg.baseUrl ?? resolveBaseUrl();
@@ -176,7 +213,7 @@ export async function createRuntimeStack(config?: RuntimeStackConfig): Promise<R
176213
createSingleProjectPlugin({
177214
projectId,
178215
projectDatabaseUrl: projectDbUrl,
179-
projectDatabaseDriver: 'sqlite',
216+
projectDatabaseDriver: projectDbDriver,
180217
apiPrefix: cfg.apiPrefix,
181218
}),
182219
);

0 commit comments

Comments
 (0)