@async/db 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +167 -0
- package/README.md +431 -0
- package/SPEC.md +1429 -0
- package/db.config.example.mjs +128 -0
- package/dist/cli/args.d.ts +8 -0
- package/dist/cli/args.js +16 -0
- package/dist/cli/commands/create.d.ts +3 -0
- package/dist/cli/commands/create.js +13 -0
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.js +31 -0
- package/dist/cli/commands/generate.d.ts +6 -0
- package/dist/cli/commands/generate.js +24 -0
- package/dist/cli/commands/operations.d.ts +12 -0
- package/dist/cli/commands/operations.js +61 -0
- package/dist/cli/commands/schema.d.ts +11 -0
- package/dist/cli/commands/schema.js +1086 -0
- package/dist/cli/commands/serve.d.ts +9 -0
- package/dist/cli/commands/serve.js +18 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.js +11 -0
- package/dist/cli/commands/types.d.ts +7 -0
- package/dist/cli/commands/types.js +37 -0
- package/dist/cli/commands/viewer.d.ts +6 -0
- package/dist/cli/commands/viewer.js +29 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +108 -0
- package/dist/cli/output.d.ts +25 -0
- package/dist/cli/output.js +149 -0
- package/dist/cli/schema-prompt.d.ts +20 -0
- package/dist/cli/schema-prompt.js +66 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +3 -0
- package/dist/client-cache.d.ts +105 -0
- package/dist/client-cache.js +916 -0
- package/dist/client.d.ts +64 -0
- package/dist/client.js +405 -0
- package/dist/config-public.d.ts +1 -0
- package/dist/config-public.js +1 -0
- package/dist/config.d.ts +54 -0
- package/dist/config.js +2 -0
- package/dist/csv.d.ts +1 -0
- package/dist/csv.js +1 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.js +3 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +1 -0
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +1 -0
- package/dist/features/config/defaults.d.ts +98 -0
- package/dist/features/config/defaults.js +95 -0
- package/dist/features/config/load.d.ts +11 -0
- package/dist/features/config/load.js +265 -0
- package/dist/features/config/public.d.ts +17 -0
- package/dist/features/config/public.js +75 -0
- package/dist/features/doctor/duplicate-ids.d.ts +18 -0
- package/dist/features/doctor/duplicate-ids.js +79 -0
- package/dist/features/doctor/field-consistency.d.ts +17 -0
- package/dist/features/doctor/field-consistency.js +48 -0
- package/dist/features/doctor/index.d.ts +39 -0
- package/dist/features/doctor/index.js +177 -0
- package/dist/features/doctor/relations.d.ts +22 -0
- package/dist/features/doctor/relations.js +90 -0
- package/dist/features/doctor/schema-guidance.d.ts +35 -0
- package/dist/features/doctor/schema-guidance.js +184 -0
- package/dist/features/generate/registry.d.ts +14 -0
- package/dist/features/generate/registry.js +37 -0
- package/dist/features/http/registry.d.ts +46 -0
- package/dist/features/http/registry.js +86 -0
- package/dist/features/operations/index.d.ts +49 -0
- package/dist/features/operations/index.js +199 -0
- package/dist/features/operations/maps.d.ts +1 -0
- package/dist/features/operations/maps.js +10 -0
- package/dist/features/operations/readiness.d.ts +30 -0
- package/dist/features/operations/readiness.js +228 -0
- package/dist/features/operations/runtime.d.ts +57 -0
- package/dist/features/operations/runtime.js +288 -0
- package/dist/features/runtime/collection.d.ts +51 -0
- package/dist/features/runtime/collection.js +198 -0
- package/dist/features/runtime/db.d.ts +152 -0
- package/dist/features/runtime/db.js +824 -0
- package/dist/features/runtime/document.d.ts +43 -0
- package/dist/features/runtime/document.js +111 -0
- package/dist/features/runtime/fanout.d.ts +24 -0
- package/dist/features/runtime/fanout.js +77 -0
- package/dist/features/runtime/json-pointer.d.ts +5 -0
- package/dist/features/runtime/json-pointer.js +49 -0
- package/dist/features/runtime/scope-state.d.ts +44 -0
- package/dist/features/runtime/scope-state.js +185 -0
- package/dist/features/runtime/state.d.ts +1 -0
- package/dist/features/runtime/state.js +1 -0
- package/dist/features/schema/api.d.ts +107 -0
- package/dist/features/schema/api.js +460 -0
- package/dist/features/schema/builders.d.ts +86 -0
- package/dist/features/schema/builders.js +110 -0
- package/dist/features/schema/fields.d.ts +38 -0
- package/dist/features/schema/fields.js +296 -0
- package/dist/features/schema/generated.d.ts +29 -0
- package/dist/features/schema/generated.js +32 -0
- package/dist/features/schema/locator.d.ts +16 -0
- package/dist/features/schema/locator.js +135 -0
- package/dist/features/schema/manifest.d.ts +91 -0
- package/dist/features/schema/manifest.js +384 -0
- package/dist/features/schema/metadata.d.ts +30 -0
- package/dist/features/schema/metadata.js +75 -0
- package/dist/features/schema/project.d.ts +46 -0
- package/dist/features/schema/project.js +442 -0
- package/dist/features/schema/relations.d.ts +38 -0
- package/dist/features/schema/relations.js +109 -0
- package/dist/features/schema/resolvers.d.ts +36 -0
- package/dist/features/schema/resolvers.js +111 -0
- package/dist/features/schema/resource.d.ts +75 -0
- package/dist/features/schema/resource.js +253 -0
- package/dist/features/schema/source-definitions.d.ts +21 -0
- package/dist/features/schema/source-definitions.js +29 -0
- package/dist/features/schema/sources.d.ts +83 -0
- package/dist/features/schema/sources.js +689 -0
- package/dist/features/schema/standard-schema.d.ts +57 -0
- package/dist/features/schema/standard-schema.js +232 -0
- package/dist/features/schema/validation.d.ts +69 -0
- package/dist/features/schema/validation.js +434 -0
- package/dist/features/storage/events.d.ts +12 -0
- package/dist/features/storage/events.js +30 -0
- package/dist/features/storage/json.d.ts +112 -0
- package/dist/features/storage/json.js +239 -0
- package/dist/features/storage/memory.d.ts +30 -0
- package/dist/features/storage/memory.js +44 -0
- package/dist/features/storage/resource-json.d.ts +31 -0
- package/dist/features/storage/resource-json.js +76 -0
- package/dist/features/storage/runtime.d.ts +37 -0
- package/dist/features/storage/runtime.js +184 -0
- package/dist/features/storage/source-metadata.d.ts +20 -0
- package/dist/features/storage/source-metadata.js +25 -0
- package/dist/features/storage/source.d.ts +37 -0
- package/dist/features/storage/source.js +60 -0
- package/dist/features/storage/static.d.ts +29 -0
- package/dist/features/storage/static.js +42 -0
- package/dist/features/sync/defaults.d.ts +21 -0
- package/dist/features/sync/defaults.js +21 -0
- package/dist/features/sync/index.d.ts +35 -0
- package/dist/features/sync/index.js +85 -0
- package/dist/features/sync/mirror-state.d.ts +14 -0
- package/dist/features/sync/mirror-state.js +4 -0
- package/dist/features/sync/runtime-dirs.d.ts +5 -0
- package/dist/features/sync/runtime-dirs.js +9 -0
- package/dist/features/sync/source-writes.d.ts +15 -0
- package/dist/features/sync/source-writes.js +27 -0
- package/dist/features/sync/synthetic-seed.d.ts +26 -0
- package/dist/features/sync/synthetic-seed.js +83 -0
- package/dist/features/viewer/manifest.d.ts +148 -0
- package/dist/features/viewer/manifest.js +165 -0
- package/dist/fs-utils.d.ts +1 -0
- package/dist/fs-utils.js +1 -0
- package/dist/generate/hono/app.d.ts +6 -0
- package/dist/generate/hono/app.js +51 -0
- package/dist/generate/hono/graphql.d.ts +7 -0
- package/dist/generate/hono/graphql.js +53 -0
- package/dist/generate/hono/index.d.ts +55 -0
- package/dist/generate/hono/index.js +140 -0
- package/dist/generate/hono/package.d.ts +6 -0
- package/dist/generate/hono/package.js +44 -0
- package/dist/generate/hono/readme.d.ts +13 -0
- package/dist/generate/hono/readme.js +28 -0
- package/dist/generate/hono/repository.d.ts +1 -0
- package/dist/generate/hono/repository.js +27 -0
- package/dist/generate/hono/rest.d.ts +1 -0
- package/dist/generate/hono/rest.js +38 -0
- package/dist/generate/hono/schema.d.ts +13 -0
- package/dist/generate/hono/schema.js +18 -0
- package/dist/generate/hono/sqlite.d.ts +20 -0
- package/dist/generate/hono/sqlite.js +266 -0
- package/dist/generate/hono/validators.d.ts +1 -0
- package/dist/generate/hono/validators.js +141 -0
- package/dist/generate/hono.d.ts +1 -0
- package/dist/generate/hono.js +1 -0
- package/dist/graphql/execute.d.ts +14 -0
- package/dist/graphql/execute.js +719 -0
- package/dist/graphql/http.d.ts +15 -0
- package/dist/graphql/http.js +29 -0
- package/dist/graphql/index.d.ts +3 -0
- package/dist/graphql/index.js +3 -0
- package/dist/graphql/parser.d.ts +54 -0
- package/dist/graphql/parser.js +433 -0
- package/dist/hono.d.ts +77 -0
- package/dist/hono.js +1 -0
- package/dist/index.d.ts +1065 -0
- package/dist/index.js +14 -0
- package/dist/integrations/hono.d.ts +136 -0
- package/dist/integrations/hono.js +508 -0
- package/dist/integrations/kv.d.ts +69 -0
- package/dist/integrations/kv.js +69 -0
- package/dist/integrations/postgres.d.ts +52 -0
- package/dist/integrations/postgres.js +113 -0
- package/dist/integrations/sqlite.d.ts +112 -0
- package/dist/integrations/sqlite.js +489 -0
- package/dist/integrations/vite.d.ts +45 -0
- package/dist/integrations/vite.js +111 -0
- package/dist/json.d.ts +48 -0
- package/dist/json.js +1 -0
- package/dist/jsonc.d.ts +1 -0
- package/dist/jsonc.js +1 -0
- package/dist/kv.d.ts +24 -0
- package/dist/kv.js +1 -0
- package/dist/mock.d.ts +1 -0
- package/dist/mock.js +1 -0
- package/dist/names.d.ts +1 -0
- package/dist/names.js +1 -0
- package/dist/operations.d.ts +3 -0
- package/dist/operations.js +3 -0
- package/dist/postgres.d.ts +24 -0
- package/dist/postgres.js +1 -0
- package/dist/redis.d.ts +14 -0
- package/dist/redis.js +1 -0
- package/dist/rest/formats.d.ts +80 -0
- package/dist/rest/formats.js +318 -0
- package/dist/rest/handler.d.ts +111 -0
- package/dist/rest/handler.js +833 -0
- package/dist/rest/shape.d.ts +33 -0
- package/dist/rest/shape.js +218 -0
- package/dist/schema-builders.d.ts +1 -0
- package/dist/schema-builders.js +1 -0
- package/dist/schema-manifest.d.ts +1 -0
- package/dist/schema-manifest.js +1 -0
- package/dist/schema.d.ts +193 -0
- package/dist/schema.js +6 -0
- package/dist/server.d.ts +116 -0
- package/dist/server.js +601 -0
- package/dist/shared/csv.d.ts +8 -0
- package/dist/shared/csv.js +149 -0
- package/dist/shared/errors.d.ts +40 -0
- package/dist/shared/errors.js +55 -0
- package/dist/shared/fs-utils.d.ts +4 -0
- package/dist/shared/fs-utils.js +30 -0
- package/dist/shared/jsonc.d.ts +2 -0
- package/dist/shared/jsonc.js +99 -0
- package/dist/shared/mock.d.ts +40 -0
- package/dist/shared/mock.js +83 -0
- package/dist/shared/names.d.ts +28 -0
- package/dist/shared/names.js +127 -0
- package/dist/shared/operations.d.ts +32 -0
- package/dist/shared/operations.js +302 -0
- package/dist/sqlite.d.ts +24 -0
- package/dist/sqlite.js +1 -0
- package/dist/state.d.ts +1 -0
- package/dist/state.js +1 -0
- package/dist/sync.d.ts +1 -0
- package/dist/sync.js +1 -0
- package/dist/tracing.d.ts +95 -0
- package/dist/tracing.js +260 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.js +285 -0
- package/dist/viewer-manifest.d.ts +1 -0
- package/dist/viewer-manifest.js +1 -0
- package/dist/vite.d.ts +59 -0
- package/dist/vite.js +1 -0
- package/dist/web/json-viewer.d.ts +5 -0
- package/dist/web/json-viewer.js +176 -0
- package/dist/web/viewer.d.ts +12 -0
- package/dist/web/viewer.js +1015 -0
- package/docs/README.md +42 -0
- package/docs/architecture.md +112 -0
- package/docs/ci-and-release.md +177 -0
- package/docs/cms-storage-patterns.md +108 -0
- package/docs/concepts.md +141 -0
- package/docs/configuration.md +552 -0
- package/docs/fixtures-and-schemas.md +527 -0
- package/docs/fork-branch-workflows.md +108 -0
- package/docs/generated-files.md +174 -0
- package/docs/getting-started.md +165 -0
- package/docs/integrations.md +206 -0
- package/docs/json-production.md +120 -0
- package/docs/package-api.md +418 -0
- package/docs/prototype-to-production.md +378 -0
- package/docs/server-and-viewer.md +466 -0
- package/docs/store-graduation.md +120 -0
- package/docs/typescript-schema-sources.md +79 -0
- package/examples/advanced/README.md +55 -0
- package/examples/advanced/db/projects.schema.jsonc +44 -0
- package/examples/advanced/db/settings.jsonc +9 -0
- package/examples/advanced/db/users.json +23 -0
- package/examples/advanced/db/users.schema.mjs +31 -0
- package/examples/advanced/db.config.mjs +18 -0
- package/examples/advanced/example.json +5 -0
- package/examples/advanced/src/generated/db.types.d.ts +64 -0
- package/examples/basic/README.md +95 -0
- package/examples/basic/db/operations/get-user.jsonc +8 -0
- package/examples/basic/db/settings.json +7 -0
- package/examples/basic/db/users.schema.jsonc +36 -0
- package/examples/basic/db.config.mjs +68 -0
- package/examples/basic/example.json +5 -0
- package/examples/basic/src/generated/db.types.d.ts +39 -0
- package/examples/cms-json-publish/README.md +21 -0
- package/examples/cms-json-publish/db/navigation.json +7 -0
- package/examples/cms-json-publish/db/pages.json +18 -0
- package/examples/cms-json-publish/example.json +5 -0
- package/examples/cms-json-publish/src/cms.mjs +104 -0
- package/examples/computed-fields/README.md +93 -0
- package/examples/computed-fields/db/orders.schema.mjs +62 -0
- package/examples/computed-fields/db/posts.schema.mjs +59 -0
- package/examples/computed-fields/db/products.schema.mjs +39 -0
- package/examples/computed-fields/db/users.schema.mjs +43 -0
- package/examples/computed-fields/db.config.mjs +15 -0
- package/examples/computed-fields/example.json +5 -0
- package/examples/computed-fields/src/generated/db.types.d.ts +81 -0
- package/examples/content-collections/README.md +91 -0
- package/examples/content-collections/db/authors.json +12 -0
- package/examples/content-collections/db/authors.schema.mjs +20 -0
- package/examples/content-collections/db/blog/draft-roadmap.mdx +12 -0
- package/examples/content-collections/db/blog/index.schema.mjs +61 -0
- package/examples/content-collections/db/blog/launch-notes.mdx +15 -0
- package/examples/content-collections/db/docs/index.schema.mjs +32 -0
- package/examples/content-collections/db/docs/intro.mdx +11 -0
- package/examples/content-collections/db/docs/schema-workflow.mdx +10 -0
- package/examples/content-collections/db/site.schema.jsonc +21 -0
- package/examples/content-collections/db.config.mjs +26 -0
- package/examples/content-collections/example.json +5 -0
- package/examples/content-collections/src/content-preview.mjs +66 -0
- package/examples/content-collections/src/generated/db.types.d.ts +81 -0
- package/examples/csv/README.md +52 -0
- package/examples/csv/db/customers.csv +4 -0
- package/examples/csv/db.config.mjs +13 -0
- package/examples/csv/example.json +5 -0
- package/examples/data-first/README.md +54 -0
- package/examples/data-first/db/posts.json +16 -0
- package/examples/data-first/db/settings.json +8 -0
- package/examples/data-first/db/users.json +14 -0
- package/examples/data-first/db.config.mjs +13 -0
- package/examples/data-first/example.json +5 -0
- package/examples/diagnostics/README.md +55 -0
- package/examples/diagnostics/db/projects.schema.jsonc +27 -0
- package/examples/diagnostics/db/users.json +9 -0
- package/examples/diagnostics/db/users.schema.jsonc +23 -0
- package/examples/diagnostics/db.config.mjs +16 -0
- package/examples/diagnostics/example.json +5 -0
- package/examples/free-plan-upgrade/README.md +22 -0
- package/examples/free-plan-upgrade/db/appSettings.json +4 -0
- package/examples/free-plan-upgrade/db/projects.json +7 -0
- package/examples/free-plan-upgrade/example.json +5 -0
- package/examples/free-plan-upgrade/src/upgrade-tenant-to-paid.mjs +105 -0
- package/examples/hono-auth/README.md +74 -0
- package/examples/hono-auth/db/pages.schema.jsonc +44 -0
- package/examples/hono-auth/db/users.schema.jsonc +42 -0
- package/examples/hono-auth/db.config.mjs +17 -0
- package/examples/hono-auth/example.json +5 -0
- package/examples/hono-auth/package.json +14 -0
- package/examples/hono-auth/src/app.mjs +79 -0
- package/examples/hono-auth/src/server.mjs +13 -0
- package/examples/production-json/README.md +102 -0
- package/examples/production-json/db/appSettings.schema.jsonc +41 -0
- package/examples/production-json/db/featureFlags.schema.jsonc +84 -0
- package/examples/production-json/db/operations/get-control-plane.jsonc +6 -0
- package/examples/production-json/db/operations/get-feature-flag.jsonc +9 -0
- package/examples/production-json/db/operations/list-feature-flags.jsonc +8 -0
- package/examples/production-json/db/operations/read-public-settings.jsonc +8 -0
- package/examples/production-json/db.config.mjs +33 -0
- package/examples/production-json/example.json +5 -0
- package/examples/production-json/src/client-demo.mjs +28 -0
- package/examples/production-json/src/generated/db.types.d.ts +60 -0
- package/examples/relations/README.md +56 -0
- package/examples/relations/db/posts.schema.jsonc +46 -0
- package/examples/relations/db/users.schema.jsonc +34 -0
- package/examples/relations/db.config.mjs +13 -0
- package/examples/relations/example.json +5 -0
- package/examples/rest-client/README.md +54 -0
- package/examples/rest-client/db/settings.json +5 -0
- package/examples/rest-client/db/users.schema.jsonc +42 -0
- package/examples/rest-client/db.config.mjs +13 -0
- package/examples/rest-client/example.json +5 -0
- package/examples/rest-client/src/client-demo.mjs +24 -0
- package/examples/schema-first/README.md +55 -0
- package/examples/schema-first/db/auditEvents.schema.jsonc +24 -0
- package/examples/schema-first/db/settings.schema.jsonc +29 -0
- package/examples/schema-first/db/users.schema.jsonc +36 -0
- package/examples/schema-first/db.config.mjs +15 -0
- package/examples/schema-first/example.json +5 -0
- package/examples/schema-first/src/generated/db.types.d.ts +47 -0
- package/examples/schema-manifest/README.md +50 -0
- package/examples/schema-manifest/db/projects.schema.jsonc +48 -0
- package/examples/schema-manifest/db/users.schema.jsonc +35 -0
- package/examples/schema-manifest/db.config.mjs +41 -0
- package/examples/schema-manifest/example.json +5 -0
- package/examples/schema-manifest/src/generated/db.schema.json +130 -0
- package/examples/schema-manifest/src/generated/db.types.d.ts +50 -0
- package/examples/schema-ui/README.md +103 -0
- package/examples/schema-ui/db/pages.schema.jsonc +53 -0
- package/examples/schema-ui/db/users.schema.jsonc +30 -0
- package/examples/schema-ui/db.config.mjs +55 -0
- package/examples/schema-ui/example.json +5 -0
- package/examples/schema-ui/src/cms-ssr.mjs +276 -0
- package/examples/schema-ui/src/generated/db.schema.json +133 -0
- package/examples/schema-ui/src/generated/db.types.d.ts +46 -0
- package/examples/schema-ui/src/render-admin.mjs +175 -0
- package/examples/schema-ui/src/schema-ui-ssr-handler.mjs +149 -0
- package/examples/schema-ui/src/start-schema-ui-server.mjs +140 -0
- package/examples/standard-schema/README.md +55 -0
- package/examples/standard-schema/db/settings.schema.mjs +22 -0
- package/examples/standard-schema/db/users.schema.mjs +72 -0
- package/examples/standard-schema/example.json +5 -0
- package/package.json +108 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# Server And Viewer
|
|
2
|
+
|
|
3
|
+
`async-db serve` starts a local development server. It syncs on startup, watches fixture sources, serves REST and GraphQL endpoints, and exposes the built-in data viewer.
|
|
4
|
+
|
|
5
|
+
## Local Trust Boundary
|
|
6
|
+
|
|
7
|
+
The server binds to `127.0.0.1` by default. It is intended for local development and tests, not public hosting.
|
|
8
|
+
|
|
9
|
+
Important write surfaces:
|
|
10
|
+
|
|
11
|
+
- REST writes update runtime state.
|
|
12
|
+
- GraphQL mutations update runtime state.
|
|
13
|
+
- Viewer CSV import writes CSV files into the configured `dbDir`.
|
|
14
|
+
- Resources bound to the `sourceFile` store may write supported changes back to source fixtures.
|
|
15
|
+
|
|
16
|
+
Config and schema JavaScript are trusted project code. Do not treat `.schema.mjs`, `.schema.js`, or config hooks as untrusted data.
|
|
17
|
+
|
|
18
|
+
## Request Tracing
|
|
19
|
+
|
|
20
|
+
Enable request tracing when a local page appears stuck or slow and you need to
|
|
21
|
+
see where a DB request spent time:
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import { defineConfig } from '@async/db/config';
|
|
25
|
+
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
server: {
|
|
28
|
+
trace: true,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Tracing adds an `x-async-db-request-id` response header, emits
|
|
34
|
+
`request-trace` events through `GET /__db/log`, and prints concise console
|
|
35
|
+
lines unless disabled. Traces include method, pathname, query keys, route
|
|
36
|
+
family, resource, operation, id when known, status, handled state, duration,
|
|
37
|
+
slow status, safe error code/message, and phase timings for clear boundaries
|
|
38
|
+
such as route matching, mock behavior, REST handling, Hono hooks, collection or
|
|
39
|
+
document reads/writes, response shaping, registered operations, batch items, and
|
|
40
|
+
fork dispatch.
|
|
41
|
+
|
|
42
|
+
Trace data intentionally omits request bodies, response bodies, cookie headers,
|
|
43
|
+
authorization headers, and query values. Query strings are represented as keys
|
|
44
|
+
only, for example `["select", "expand"]`.
|
|
45
|
+
|
|
46
|
+
## Viewer
|
|
47
|
+
|
|
48
|
+
Open the built-in viewer after starting the server:
|
|
49
|
+
|
|
50
|
+
```txt
|
|
51
|
+
http://127.0.0.1:7331/__db
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The default viewer and dev-tool route base is `/__db`. Change it with
|
|
55
|
+
`server.apiBase` when an app needs a different reserved path; schema, batch,
|
|
56
|
+
import, events, log, and fork routes move with that base.
|
|
57
|
+
|
|
58
|
+
Opening `http://127.0.0.1:7331/` in a browser shows a small index with links to the data viewer, schema, GraphQL endpoint, and resource routes. API-style requests to `/` keep returning JSON discovery data by default.
|
|
59
|
+
|
|
60
|
+
The viewer includes:
|
|
61
|
+
|
|
62
|
+
- resource and data browsing
|
|
63
|
+
- drag-and-drop CSV import into the configured fixture folder
|
|
64
|
+
- REST specs with copyable examples
|
|
65
|
+
- a REST request runner
|
|
66
|
+
- GraphQL SDL and operation references
|
|
67
|
+
- schema and field inspection
|
|
68
|
+
- source diagnostics when one fixture file is broken
|
|
69
|
+
|
|
70
|
+
## Custom Viewer Manifest
|
|
71
|
+
|
|
72
|
+
The built-in viewer reads the same JSON manifest that custom viewer UIs can use:
|
|
73
|
+
|
|
74
|
+
```txt
|
|
75
|
+
GET /__db/manifest
|
|
76
|
+
GET /__db/manifest.json
|
|
77
|
+
GET /__db/manifest.html
|
|
78
|
+
GET /__db/manifest.md
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`/manifest.json` returns JSON. `/manifest.html` returns the built-in formatted JSON viewer with dark mode by default, dark/light/system theme controls, copy, and pretty/raw formatting controls. `/manifest.md` returns Markdown with the manifest JSON in a fenced code block for AI clients. `/manifest` chooses from registered response formats using the request `Accept` header. If `server.apiBase` changes, the routes move with it, for example `GET /_db/manifest`.
|
|
82
|
+
|
|
83
|
+
The manifest includes:
|
|
84
|
+
|
|
85
|
+
- API links for the viewer, manifest, manifest JSON/HTML/Markdown routes, schema, events, batch, import, GraphQL, and each REST resource
|
|
86
|
+
- built-in and configured custom viewer links
|
|
87
|
+
- resource and field metadata, including generated UI hints and relation hints
|
|
88
|
+
- viewer capabilities such as writes, batching, CSV import, GraphQL, and live events
|
|
89
|
+
- diagnostics suitable for display in a custom UI
|
|
90
|
+
|
|
91
|
+
The manifest does not include seed records, source paths, source hashes, runtime state paths, or GraphQL SDL. Custom viewers should fetch `manifest.json` for UI metadata and route links, then fetch actual records from REST or GraphQL. `api.formats` lists the registered response formats, media types, and manifest paths for custom viewers and tools.
|
|
92
|
+
|
|
93
|
+
Add custom viewer links when a project ships its own data UI:
|
|
94
|
+
|
|
95
|
+
Override the built-in Markdown renderer when a project needs a different shape:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
import { defineConfig } from '@async/db/config';
|
|
99
|
+
import { stringify as stringifyYaml } from 'yaml';
|
|
100
|
+
|
|
101
|
+
export default defineConfig({
|
|
102
|
+
server: {
|
|
103
|
+
viewerLinks: [
|
|
104
|
+
{ label: 'App Data Viewer', href: 'http://127.0.0.1:5173/db' },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
You can also write the same shape to a committed artifact:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import { defineConfig } from '@async/db/config';
|
|
114
|
+
|
|
115
|
+
export default defineConfig({
|
|
116
|
+
outputs: {
|
|
117
|
+
viewerManifest: './src/generated/db.viewer.json',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
async-db viewer manifest --out ./src/generated/db.viewer.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## REST Routes
|
|
127
|
+
|
|
128
|
+
REST routes are enabled by default. Set `rest.enabled: false` to turn off
|
|
129
|
+
generated resource routes and REST batching while keeping dev-tool routes such
|
|
130
|
+
as the viewer, schema, manifest, import, events, and GraphQL available.
|
|
131
|
+
|
|
132
|
+
The app-facing REST route base defaults to `/db`, matching the fixture folder.
|
|
133
|
+
For a fixture at `db/users.json`, fetch the synced runtime resource with:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
const users = await fetch('/db/users.json').then((response) => response.json());
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Scoped REST remains available under the tool route base, such as
|
|
140
|
+
`GET /__db/rest/users.json`. Standalone `async-db serve` also keeps root REST
|
|
141
|
+
routes such as `GET /users` for local convenience. Set `server.dataPath: false`
|
|
142
|
+
to disable only the `/db` alias.
|
|
143
|
+
|
|
144
|
+
When a prototype route needs to become an app-owned API route, see the
|
|
145
|
+
[Prototype To Production REST Guide](./prototype-to-production.md) for
|
|
146
|
+
`/api/db/*`, `/api/*`, registered operation refs, and route lockdown.
|
|
147
|
+
|
|
148
|
+
### Fixture-Like `.json` Routes
|
|
149
|
+
|
|
150
|
+
Use `.json` routes when you want the URL to resemble the source fixture path:
|
|
151
|
+
`db/users.json` becomes `GET /db/users.json`. The server still reads the
|
|
152
|
+
synced runtime resource, not the source fixture file directly, so local writes
|
|
153
|
+
continue going to the selected runtime store.
|
|
154
|
+
|
|
155
|
+
Collections can use `.json` for list and record reads:
|
|
156
|
+
|
|
157
|
+
```txt
|
|
158
|
+
GET /db/users.json
|
|
159
|
+
GET /db/users/u_1.json
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Singleton documents can use the same fixture-like read shape:
|
|
163
|
+
|
|
164
|
+
```txt
|
|
165
|
+
GET /db/settings.json
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Use `?id=` only with the explicit collection `.json` route:
|
|
169
|
+
|
|
170
|
+
```txt
|
|
171
|
+
GET /db/users.json?id=u_1
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Extensionless REST routes keep normal REST semantics and return a structured
|
|
175
|
+
error for `?id=`. Use `GET /db/users/u_1.json` or `GET /db/users/u_1` there.
|
|
176
|
+
Query options such as `select`, `expand`, `offset`, and `limit` apply before
|
|
177
|
+
the response format renders. Sibling `.html` and `.md` routes use the same
|
|
178
|
+
shaped data for browser or Markdown views.
|
|
179
|
+
|
|
180
|
+
Collections:
|
|
181
|
+
|
|
182
|
+
```txt
|
|
183
|
+
GET /db/users.json
|
|
184
|
+
GET /db/users/:id.json
|
|
185
|
+
POST /db/users
|
|
186
|
+
PATCH /db/users/:id
|
|
187
|
+
DELETE /db/users/:id
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Singleton documents:
|
|
191
|
+
|
|
192
|
+
```txt
|
|
193
|
+
GET /db/settings.json
|
|
194
|
+
PUT /db/settings
|
|
195
|
+
PATCH /db/settings
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
REST examples:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
curl http://127.0.0.1:7331/db/users.json
|
|
202
|
+
curl 'http://127.0.0.1:7331/db/users.json?select=id,name&offset=0&limit=20'
|
|
203
|
+
curl 'http://127.0.0.1:7331/db/users.json?id=u_1&select=id,name'
|
|
204
|
+
curl http://127.0.0.1:7331/db/users/u_1.json
|
|
205
|
+
curl http://127.0.0.1:7331/db/settings.json
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
curl -X POST http://127.0.0.1:7331/db/users \
|
|
210
|
+
-H 'content-type: application/json' \
|
|
211
|
+
-d '{"id":"u_2","name":"Grace Hopper","email":"grace@example.com"}'
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
curl -X PATCH http://127.0.0.1:7331/db/users/u_2 \
|
|
216
|
+
-H 'content-type: application/json' \
|
|
217
|
+
-d '{"name":"Rear Admiral Grace Hopper"}'
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
curl -X DELETE http://127.0.0.1:7331/db/users/u_2
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Schema-backed computed fields are resolved only when selected. For example,
|
|
225
|
+
`GET /db/users/u_1.json?select=id,fullName` calls the trusted resolver registered
|
|
226
|
+
by `field.computed(...)`; default reads continue returning only stored fixture
|
|
227
|
+
fields.
|
|
228
|
+
|
|
229
|
+
## REST Formats
|
|
230
|
+
|
|
231
|
+
Resource `GET` routes return JSON by default. The explicit `.json` extension uses the same shaped data:
|
|
232
|
+
|
|
233
|
+
```txt
|
|
234
|
+
GET /db/users
|
|
235
|
+
GET /db/users.json
|
|
236
|
+
GET /db/users.html
|
|
237
|
+
GET /db/users.md
|
|
238
|
+
GET /db/users/u_1
|
|
239
|
+
GET /db/users/u_1.json
|
|
240
|
+
GET /db/users/u_1.html
|
|
241
|
+
GET /db/users/u_1.md
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`.json`, `.html`, and `.md` are built in. Config entries with the same extension override the built-in resource renderer, and object entries can also override manifest rendering. Extensionless resource and manifest routes negotiate registered media types from `Accept`; unsupported `Accept` values fall back to the configured default format. Format renderers receive data after normal REST shaping, so `select`, `expand`, `offset`, and `limit` apply before rendering.
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
import { defineConfig } from '@async/db/config';
|
|
248
|
+
|
|
249
|
+
export default defineConfig({
|
|
250
|
+
rest: {
|
|
251
|
+
formats: {
|
|
252
|
+
default: 'json',
|
|
253
|
+
md({ resourceName, data }) {
|
|
254
|
+
return {
|
|
255
|
+
body: `# ${resourceName}\n\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\`\n`,
|
|
256
|
+
contentType: 'text/markdown; charset=utf-8',
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
yaml: {
|
|
260
|
+
mediaTypes: ['application/yaml', 'text/yaml'],
|
|
261
|
+
contentType: 'application/yaml; charset=utf-8',
|
|
262
|
+
render({ data }) {
|
|
263
|
+
return stringifyYaml(data);
|
|
264
|
+
},
|
|
265
|
+
renderManifest({ data }) {
|
|
266
|
+
return stringifyYaml(data);
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Function shorthand is resource-only for compatibility. Use object syntax when a format needs media-type negotiation or manifest support, such as `GET /__db/manifest.yaml`. @async/db does not execute `.jsx` routes directly; JSX is a source/runtime choice for your renderer, while `.html` is the response format.
|
|
275
|
+
|
|
276
|
+
## Relationship Expansion
|
|
277
|
+
|
|
278
|
+
Schema-backed scalar fields can declare relation metadata while fixtures keep plain ids:
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"authorId": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"required": true,
|
|
285
|
+
"relation": {
|
|
286
|
+
"name": "author",
|
|
287
|
+
"to": "users",
|
|
288
|
+
"toField": "id",
|
|
289
|
+
"cardinality": "one"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Then REST can expand that to-one relation:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
curl 'http://127.0.0.1:7331/db/posts/p_1.json?expand=author&select=id,title,author.name'
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`select` supports top-level fields and one nested expanded relation field. Relation expansion is depth 1 in this MVP. Reverse to-many expansion is intentionally deferred.
|
|
302
|
+
|
|
303
|
+
## REST Batching
|
|
304
|
+
|
|
305
|
+
REST batching is supported through:
|
|
306
|
+
|
|
307
|
+
```txt
|
|
308
|
+
POST /__db/batch
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
If `server.apiBase` is changed, the batch endpoint follows that base, for
|
|
312
|
+
example `POST /_db/batch`.
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
[
|
|
316
|
+
{
|
|
317
|
+
"method": "GET",
|
|
318
|
+
"path": "/db/users.json"
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"method": "PATCH",
|
|
322
|
+
"path": "/db/settings",
|
|
323
|
+
"body": {
|
|
324
|
+
"theme": "dark"
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
REST batches execute sequentially and are intentionally non-transactional. If an earlier write succeeds and a later batch item fails, the earlier write stays committed.
|
|
331
|
+
|
|
332
|
+
Errors are shaped for humans and automation:
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"error": {
|
|
337
|
+
"code": "REST_BATCH_INVALID_PATH",
|
|
338
|
+
"message": "REST batch path must start with \"/\": users",
|
|
339
|
+
"hint": "Use absolute local paths such as \"/users\", \"/settings\", or \"/__db/schema\".",
|
|
340
|
+
"details": {
|
|
341
|
+
"path": "users"
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Registered REST Operations
|
|
348
|
+
|
|
349
|
+
Registered queries are optional REST or GraphQL request templates with callable
|
|
350
|
+
refs and optional names. They let production-style apps allowlist specific
|
|
351
|
+
reads and writes while local fixture CRUD can stay open by default. The
|
|
352
|
+
underlying config and CLI still use the `operations` name.
|
|
353
|
+
|
|
354
|
+
```txt
|
|
355
|
+
GET /users/{id}.json?select=id,name
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{
|
|
360
|
+
"name": "GetUser",
|
|
361
|
+
"method": "GET",
|
|
362
|
+
"path": "/users/{id}.json",
|
|
363
|
+
"query": {
|
|
364
|
+
"select": "id,name"
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
GraphQL templates use the same registry and ref execution route:
|
|
370
|
+
|
|
371
|
+
```json
|
|
372
|
+
{
|
|
373
|
+
"name": "GetUser",
|
|
374
|
+
"query": "query GetUser($id: ID!) { user(id: $id) { id name } }",
|
|
375
|
+
"operationName": "GetUser",
|
|
376
|
+
"variables": {
|
|
377
|
+
"id": "{id}"
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Build the registry and optional client-safe refs:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
async-db operations build --out ./src/generated/db.operations.json --refs-out ./src/generated/db.operation-refs.json
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
At runtime, registered operation execution is always a POST to the dev-tool base:
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
curl -X POST http://127.0.0.1:7331/__db/operations/users.get \
|
|
392
|
+
-H 'content-type: application/json' \
|
|
393
|
+
-d '{"variables":{"id":"u_1"}}'
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Readable names work as app-facing operation refs. Refs are allowlist identifiers, not
|
|
397
|
+
secrets. Generated refs default to `hashOperation(template)`, and apps can set
|
|
398
|
+
their own `ref` in operation sources when they want readable or app-owned ids.
|
|
399
|
+
Keep the full registry server-side. Client refs contain names and callable refs
|
|
400
|
+
but not full request templates, variables, or request bodies. Use
|
|
401
|
+
`async-db operations contract --check` in CI when committed refs need approval
|
|
402
|
+
before the browser-facing operation contract changes. `operations.acceptRefs`
|
|
403
|
+
controls whether the server accepts refs, names, or both. Use `operations.resolveRef` or
|
|
404
|
+
`operations.validateRef` for custom server-side registry lookup or policy.
|
|
405
|
+
|
|
406
|
+
To block raw REST while allowing registered operations:
|
|
407
|
+
|
|
408
|
+
```js
|
|
409
|
+
import { defineConfig } from '@async/db/config';
|
|
410
|
+
|
|
411
|
+
export default defineConfig({
|
|
412
|
+
outputs: {
|
|
413
|
+
operationRegistry: './src/generated/db.operations.json',
|
|
414
|
+
},
|
|
415
|
+
operations: {
|
|
416
|
+
enabled: true,
|
|
417
|
+
acceptRefs: 'ref',
|
|
418
|
+
},
|
|
419
|
+
server: {
|
|
420
|
+
expose: {
|
|
421
|
+
rest: 'registered-only',
|
|
422
|
+
graphql: false,
|
|
423
|
+
viewer: 'dev',
|
|
424
|
+
schema: 'dev',
|
|
425
|
+
manifest: 'dev',
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
`registered-only` blocks raw REST resource and batch routes. Registered REST
|
|
432
|
+
operation execution still uses normal REST shaping, including `select`,
|
|
433
|
+
formats, schema validation, and computed resolver projection. Registered
|
|
434
|
+
GraphQL operations execute through the same GraphQL executor as direct GraphQL
|
|
435
|
+
requests, and still require `graphql.enabled !== false`.
|
|
436
|
+
|
|
437
|
+
## GraphQL Boundary
|
|
438
|
+
|
|
439
|
+
GraphQL is available at `/graphql` for apps that prefer it. It supports aliases, variables, `operationName`, `__typename`, named and inline fragments, `@include`/`@skip`, HTTP batching, and minimal `__schema`/`__type` introspection for local tooling.
|
|
440
|
+
|
|
441
|
+
Set `graphql.enabled: false` when an app wants REST, schema, manifest, viewer, import, and events without a GraphQL endpoint. Root discovery reports the GraphQL link as unavailable, and direct GraphQL requests return a structured `GRAPHQL_DISABLED` error.
|
|
442
|
+
|
|
443
|
+
GraphQL HTTP batches execute sequentially and are intentionally non-transactional. If an earlier mutation succeeds and a later batch item fails, the earlier mutation stays committed.
|
|
444
|
+
|
|
445
|
+
REST remains the documented happy path because REST plus the viewer is the intended default workflow.
|
|
446
|
+
|
|
447
|
+
GraphQL selections use the same read projection/fanout path as REST for computed
|
|
448
|
+
fields. Registered GraphQL operations are fixed query templates; they are not a
|
|
449
|
+
direct database query language and do not expose backend SQL or Redis commands.
|
|
450
|
+
|
|
451
|
+
Use `server.expose.graphql`, `server.expose.viewer`, `server.expose.schema`, and
|
|
452
|
+
`server.expose.manifest` to keep non-REST surfaces `open`, `dev`, or disabled in
|
|
453
|
+
production-like servers.
|
|
454
|
+
|
|
455
|
+
Unsupported in v1:
|
|
456
|
+
|
|
457
|
+
- subscriptions
|
|
458
|
+
- full GraphQL spec introspection
|
|
459
|
+
- general-purpose GraphQL validation beyond @async/db's local subset
|
|
460
|
+
- relation traversal from schema relation metadata; GraphQL projects stored fields in v1
|
|
461
|
+
|
|
462
|
+
## Watch Behavior
|
|
463
|
+
|
|
464
|
+
`serve` watches fixture sources, ignores `.db/`, reloads valid resources when files change, and surfaces file-specific diagnostics in the viewer without breaking unrelated resources.
|
|
465
|
+
|
|
466
|
+
If an app commits generated files under frontend source folders, Vite may still reload when those files genuinely change. Only ignore generated files that the browser does not need to hot reload.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Resource Graduation And Mixed Stores
|
|
2
|
+
|
|
3
|
+
`@async/db` lets each resource choose the store that matches its operational risk while the app keeps one data layer. The usual production shape is:
|
|
4
|
+
|
|
5
|
+
- JSON for control-plane resources such as settings, feature flags, templates, policy rules, and seed data.
|
|
6
|
+
- SQLite, Postgres, or a custom store for data-plane resources such as orders, activity, user-owned records, and high-write collections.
|
|
7
|
+
- Registered operations as the public API boundary so client code does not care which store backs a resource.
|
|
8
|
+
|
|
9
|
+
## When To Graduate A Resource
|
|
10
|
+
|
|
11
|
+
Move one resource out of JSON when it starts needing database behavior:
|
|
12
|
+
|
|
13
|
+
- writes happen often enough that whole-file rewrites are wasteful
|
|
14
|
+
- more than one process or app instance can write it
|
|
15
|
+
- users need filtered, indexed, or paginated queries over a growing dataset
|
|
16
|
+
- records need transactional guarantees, audit controls, or operational retention
|
|
17
|
+
- the data is business-critical enough that database backup and restore should own it
|
|
18
|
+
|
|
19
|
+
Do not graduate the whole app by default. Keep file-suitable resources in JSON and move only the resource that outgrew it.
|
|
20
|
+
|
|
21
|
+
## Postgres Example
|
|
22
|
+
|
|
23
|
+
Keep control-plane resources on JSON and put app data in Postgres:
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import { defineConfig } from '@async/db/config';
|
|
27
|
+
import { postgresStore } from '@async/db/postgres';
|
|
28
|
+
import { pool } from './src/server/postgres-client.js';
|
|
29
|
+
|
|
30
|
+
export default defineConfig({
|
|
31
|
+
stores: {
|
|
32
|
+
default: 'json',
|
|
33
|
+
appDb: postgresStore({
|
|
34
|
+
client: pool,
|
|
35
|
+
namespace: 'production',
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
resources: {
|
|
39
|
+
appSettings: { store: 'json' },
|
|
40
|
+
featureFlags: { store: 'json' },
|
|
41
|
+
promptTemplates: { store: 'json' },
|
|
42
|
+
orders: { store: 'appDb' },
|
|
43
|
+
activityEvents: { store: 'appDb' },
|
|
44
|
+
},
|
|
45
|
+
operations: {
|
|
46
|
+
enabled: true,
|
|
47
|
+
acceptRefs: 'ref',
|
|
48
|
+
sourceDir: './db/operations',
|
|
49
|
+
},
|
|
50
|
+
server: {
|
|
51
|
+
expose: {
|
|
52
|
+
rest: 'registered-only',
|
|
53
|
+
graphql: false,
|
|
54
|
+
viewer: 'dev',
|
|
55
|
+
schema: 'dev',
|
|
56
|
+
manifest: 'dev',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The built-in Postgres store persists each resource value in JSONB envelopes. That is enough for a resource graduation path behind the `@async/db` API. If a mature resource needs relational tables, custom SQL, or Postgres-native indexes per field, move that resource behind an app-owned custom store or generated API while preserving the same operation refs.
|
|
63
|
+
|
|
64
|
+
## SQLite Example
|
|
65
|
+
|
|
66
|
+
For single-node production apps, desktop apps, internal tools, and small deployments, SQLite can be the first graduation step:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { defineConfig } from '@async/db/config';
|
|
70
|
+
import { sqliteStore } from '@async/db/sqlite';
|
|
71
|
+
|
|
72
|
+
export default defineConfig({
|
|
73
|
+
stores: {
|
|
74
|
+
default: 'json',
|
|
75
|
+
appDb: sqliteStore({
|
|
76
|
+
file: './.db/app.sqlite',
|
|
77
|
+
}),
|
|
78
|
+
},
|
|
79
|
+
resources: {
|
|
80
|
+
appSettings: { store: 'json' },
|
|
81
|
+
featureFlags: { store: 'json' },
|
|
82
|
+
orders: { store: 'appDb' },
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
SQLite keeps the deployment simple, but it still changes the operational boundary: backup the SQLite file, avoid multi-writer network filesystems, and move to Postgres when many app instances need concurrent writes.
|
|
88
|
+
|
|
89
|
+
## What The App Keeps
|
|
90
|
+
|
|
91
|
+
When a resource graduates, preserve these contracts:
|
|
92
|
+
|
|
93
|
+
- resource name, such as `orders`
|
|
94
|
+
- schema fields and generated TypeScript names where possible
|
|
95
|
+
- registered operation name and ref
|
|
96
|
+
- client call shape, such as `db.query(operationRefs.operations.ListOrders.ref)`
|
|
97
|
+
- app-owned auth and policy checks around the operation route
|
|
98
|
+
|
|
99
|
+
The store config changes, but app code keeps calling the same data layer:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
await db.query(operationRefs.operations.GetControlPlane.ref);
|
|
103
|
+
await db.query(operationRefs.operations.ListOrders.ref, { accountId });
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`GetControlPlane` can read JSON-backed `appSettings` and `featureFlags`; `ListOrders` can read a database-backed `orders` resource. Both calls still go through `@async/db` registered operations.
|
|
107
|
+
|
|
108
|
+
## Graduation Steps
|
|
109
|
+
|
|
110
|
+
1. Add or tighten the explicit schema for the resource while it is still JSON-backed.
|
|
111
|
+
2. Add a registered operation for the browser-facing read or write path.
|
|
112
|
+
3. Generate and commit the client-safe operation contract if the app ships refs.
|
|
113
|
+
4. Add the target store to `db.config.mjs`.
|
|
114
|
+
5. Set only the graduating resource to that store with `resources.<name>.store`.
|
|
115
|
+
6. Run `async-db sync` so the target store hydrates from the existing seed or fixture.
|
|
116
|
+
7. Move existing production state with an app-owned migration or export/import script.
|
|
117
|
+
8. Keep JSON resources in JSON unless they have their own reason to graduate.
|
|
118
|
+
9. Run `async-db doctor --production`, operation contract checks, and app integration tests before deploying.
|
|
119
|
+
|
|
120
|
+
See [examples/production-json](../examples/production-json/README.md) for the feature flags/settings side of this pattern.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# TypeScript Schema Sources
|
|
2
|
+
|
|
3
|
+
@async/db executes schema modules with normal Node.js ESM loading. It supports JavaScript schema files at runtime and lets TypeScript projects compile schema authoring files into those runtime files.
|
|
4
|
+
|
|
5
|
+
@async/db does not execute `.ts` schema files directly in this pass. Add a build step when you author schemas in TypeScript.
|
|
6
|
+
|
|
7
|
+
## Supported Authoring Paths
|
|
8
|
+
|
|
9
|
+
### JavaScript ESM
|
|
10
|
+
|
|
11
|
+
Use `.schema.js` when the nearest `package.json` has `"type": "module"`:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"type": "module"
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
// db/users.schema.js
|
|
21
|
+
import { collection, field } from '@async/db/schema';
|
|
22
|
+
|
|
23
|
+
export default collection({
|
|
24
|
+
idField: 'id',
|
|
25
|
+
fields: {
|
|
26
|
+
id: field.string({ required: true }),
|
|
27
|
+
email: field.string({ required: true, unique: true }),
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This works for `db.schema.js`, `db/*.schema.js`, and `db/**/index.schema.js`.
|
|
33
|
+
|
|
34
|
+
### TypeScript Authoring
|
|
35
|
+
|
|
36
|
+
Author TypeScript in a separate source folder and compile it into supported runtime schema files:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// db/schema-src/users.schema.ts
|
|
40
|
+
import { collection, field } from '@async/db/schema';
|
|
41
|
+
|
|
42
|
+
export default collection({
|
|
43
|
+
idField: 'id',
|
|
44
|
+
fields: {
|
|
45
|
+
id: field.string({ required: true }),
|
|
46
|
+
email: field.string({ required: true, unique: true }),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Compile to `.schema.js`:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"scripts": {
|
|
56
|
+
"db:schema:build": "tsc -p tsconfig.db-schema.json",
|
|
57
|
+
"db:sync": "npm run db:schema:build && async-db sync"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
When the project root `package.json` is not already `"type": "module"`, @async/db creates `db/package.json` with `"type": "module"` before loading `.schema.js` files from the fixture folder. Set `schema.autoModulePackageJson: false` when you want to manage that file yourself.
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"extends": "./tsconfig.json",
|
|
67
|
+
"compilerOptions": {
|
|
68
|
+
"rootDir": "db/schema-src",
|
|
69
|
+
"outDir": "db",
|
|
70
|
+
"module": "NodeNext",
|
|
71
|
+
"moduleResolution": "NodeNext",
|
|
72
|
+
"target": "ES2022",
|
|
73
|
+
"declaration": false,
|
|
74
|
+
"sourceMap": false,
|
|
75
|
+
"strict": true
|
|
76
|
+
},
|
|
77
|
+
"include": ["db/schema-src/**/*.schema.ts"]
|
|
78
|
+
}
|
|
79
|
+
```
|