adapter sdk
Start with one file, then add only the primitives your service needs.
import {
defineAdapter,
definePage,
PageHeader,
} from "@northgraindata/dsui-adapter-sdk";
export default defineAdapter({
metadata: { id: "hello", name: "Hello", version: "1.0.0" },
pages: [
definePage({
path: "/",
render: () => [PageHeader({ title: "Hello" })],
}),
],
});This adapter has no connection and no data. It still validates and renders.
The result is a native DSUI page rendered from the adapter definition. The adapter supplies the workflow; DSUI supplies routing, shared components, and the browser runtime.
Use a connection schema and context when the adapter needs a client. Add
resources for reads and actions for mutations. Pages compose the existing DSUI
components from those bindings.
The smallest useful adapter normally has this shape:
connection schema -> context/client -> resources and actions -> pageResources are for reads and declare how they refresh. Actions are for commands and mutations; they should invalidate resources that may have changed. Stores hold adapter or page state and are the only persistence mechanism available to adapter code.
adapter.ts adapter definition and context
resources/ reads from the service
actions/ mutations and commands
pages/ routes composed from DSUI componentsEvery action should invalidate the resources it changes. Every client with a persistent connection should be released through disposeContext.
An adapter or plugin that needs host executables or system libraries can declare
them in its package.json. The Docker build collects dsui.image declarations
from workspace adapters and plugins and installs them into the runtime image.
Use apt for Debian packages and pipx for Python command-line tools:
{
"dsui": {
"image": {
"apt": ["python3", "python3-venv", "pipx"],
"pipx": ["dbt-core==${DSUI_DBT_VERSION}"]
}
}
}Build arguments can be referenced as ${NAME}. The dbt adapter uses
DSUI_DBT_VERSION, which defaults to 1.10.15 and can be overridden with
docker build --build-arg DSUI_DBT_VERSION="$DBT_VERSION" .. Declarations are
kept with the adapter/plugin package so its image requirements evolve alongside
its code.
The complete example adapter includes the package setup, a test, and instructions for loading the adapter from a local checkout. Use it as the working template instead of copying only the minimal definition above:
Open the complete example adapter
For a private service, the same flow works with an internal API: define its connection fields, wrap its reads and commands as resources and actions, and compose the result with shared DSUI components. Private adapters are an extension path for unsupported services, not a requirement for using the built-in adapters.
A connection method can opt into retaining its context between reads:
connectionMethods: {
api: {
label: "API",
schema: apiConnectionSchema,
contextIdleTimeoutMs: 300_000,
},
}contextIdleTimeoutMs defaults to 0: the host calls disposeContext after
that request, before returning its response. Positive values retain the client
until the configured number of milliseconds has elapsed after the last
completed request. A running operation cannot expire. Values must be integers
between 0 and 2147483647; grouped connection methods declare this on each
leaf method.
The server retains isolated host processes per service for five minutes of idle time, or longer when the method requests a longer context timeout. Host retention is capped at 16 processes per adapter. Idle hosts can be evicted; when every retained host is busy, another service uses an ephemeral host. Context retention is an optimization, not a guarantee of session durability.
Page, resource, and health requests for one service run sequentially in its
host; different services can run concurrently. Actions, scheduled jobs, and
unscoped connection tests use separate ephemeral hosts, so a long command does
not block page reads. requestConcurrency: "serial" still queues all calls for
that adapter method, including actions and jobs, when an external resource
requires exclusive access.
Retained contexts are never shared between services or connection configurations. Stores and resource execution scopes are recreated per request, so reads see persisted updates made by isolated actions and jobs. No resource result cache is introduced. Removing a service, replacing an adapter, and runtime shutdown release its hosts. A failed, timed-out, or cancelled request is not replayed automatically.
Keep context creation lightweight; explicit connection testing and health
should perform availability probes. Always release sockets, pools, and database
instances through disposeContext. DuckDB file methods keep the default 0
to release their file lock after every operation, even while their host stays
warm. Closing a DuckDB connection alone is insufficient; close its database
instance too.
The official dbt, Airflow, PostgreSQL, and S3 methods retain contexts for five minutes. Local dbt caches successful CLI version probes for sixty seconds; failed or cancelled probes are not cached, and other health checks still run. These policies apply in the DSUI server; direct SDK callers continue to own instance disposal explicitly.