Artifacts Overlay
Use config files as globals and gj_artifacts as the caller-scoped overlay for saved queries, fragments, and workflows.
Globals and user artifacts
GraphJin has one resolution model for saved queries, fragments, and workflows:
- Look for a user artifact in
gj_artifactsby(kind, name)for the requestuser_id. - Fall back to the global config file under the configured config path.
Kinds do not mask each other. A saved query named daily_report does not hide a workflow named daily_report.
Global files are the baseline:
config/
queries/
products.graphql
fragments/
product_fields.gql
workflows/
nightly-report.jsWhen an artifact store is configured and the request has user_id, GraphJin stores user-owned edits in gj_artifacts with visibility = "user", owner_id = <user_id>, optional account_id, source = "database", and read_only = false. Config files appear as visibility = "global", source = "config", and read_only = true.
TestUserArtifactSavedQueryOverridesGlobalOnlyForOwner
serv/artifact_overlay_test.go:94TestCatalogSnapshotMergesCallerScopedArtifacts
serv/artifact_overlay_test.go:173Configure the store
sources:
- name: app
kind: database
type: postgres
artifacts:
enabled: true
source: app
schema: _graphjin
globals_path: ./config
auto_init: trueWith auto_init: true, GraphJin creates _graphjin.artifacts when the service starts. Current updates increment the live artifact row’s revision value in place; GraphJin does not create or expose artifact history rows.
One store, one bounded projection
gj_artifacts is backed by a real SQL table, but GraphJin never hand-writes SQL against it: control-plane reads and writes run back through GraphJin’s own engine under the reserved, non-forgeable __graphjin_internal_store role — the same compiled, validated query machinery that serves your app, across every supported database. Watches
persist through the same store.
List and search reads are served from an in-memory nanoDB projection, so discovery costs no database round-trips. The projection is a bounded search index, not a copy of the store: per artifact, content is capped (default 32KB, tunable via artifacts.projection_content_max_bytes) and marked with content_truncated: true; oversized content_json/metadata_json are dropped from the projection rather than truncated into invalid JSON. Execution reads — running a saved query, loading a workflow — always read through to the store, so nothing functional is ever truncated. The projection refreshes immediately on GraphJin-made mutations and via a revision-gated poller (artifacts.poll_seconds) for external writes; an idle system does no projection work.
TestArtifactProjectionCapsJSONFieldsButStoreKeepsFull
serv/artifact_overlay_test.go:345TestArtifactProjectionHonorsConfiguredCap
serv/artifact_overlay_test.go:395Writes in development
Named query auto-save keeps the existing development behavior unless both conditions are true:
- The artifact store is configured.
- The request context has
user_id.
With both conditions, named queries and captured fragments save to gj_artifacts. Without either condition, dev-mode auto-save falls back to the global queries/ and queries/fragments/ files.
Workflow writes use the same rule. gj_workflow and save_workflow write user workflow artifacts when user_id and the store are present. Otherwise they write global workflows/*.js files only in dev fallback mode.
TestSavedQueryAutoSaveUsesUserArtifactWhenConfigured
serv/artifact_overlay_test.go:71TestUserArtifactWorkflowOverridesGlobalOnlyForOwner
serv/artifact_overlay_test.go:148Reads and execution
REST saved-query routes, MCP execute_saved_query, the server-side agent, workflow scripts, gj_workflow_execution, and catalog discovery all use the merged resolver. This means a user artifact can override a global saved query or workflow for that caller without changing what another caller sees.
Artifact-backed saved queries can import artifact-backed fragments with the same #import "./fragments/name" syntax as files. Import traversal and absolute paths are rejected by the same resolver used for filesystem saved queries.
TestArtifactSavedQueryImportsArtifactFragmentsAndRejectsTraversal
serv/artifact_overlay_test.go:120