@dudousxd/nestjs-agent-data
createExecuteSqlTool — governed, read-only SQL as a prebuilt tool. AST-validated single SELECTs, a fail-closed table allowlist, tenant scoping, and a row cap.
pnpm add @dudousxd/nestjs-agent-datanpm install @dudousxd/nestjs-agent-data@dudousxd/nestjs-agent-data ships one tool — executeSql — that lets the model answer questions
about your real data by writing SQL, without ever holding a connection. The model proposes a query;
the library validates, gates, rewrites, and caps it before your injected runner ever executes it.
import { createExecuteSqlTool, GroupTableAccessPolicy, TenantScopeRewriter } from '@dudousxd/nestjs-agent-data';
const { spec, handler } = createExecuteSqlTool({
runner: { run: (sql) => readOnlyPool.query(sql) },
tableAccess: new GroupTableAccessPolicy({
roleGroups: { ADMIN: ['orders'] },
tablesByGroup: { orders: ['orders', 'order_items'] },
}),
tenantScope: new TenantScopeRewriter({ tenantColumn: 'tenant_id', scopedTables: ['orders'] }),
});createExecuteSqlTool returns a plain { spec, handler } pair (a FunctionalTool), not an @AiTool
class — register it with the umbrella's provideAgentTool (or list it under
AgentModule.forRoot({ tools: [...] })) rather than declaring it as a provider.
Exports
| Export | What it is |
|---|---|
createExecuteSqlTool(deps: ExecuteSqlDeps) | Assembles the guardrail pipeline; returns { spec: ToolSpec, handler: ToolHandler }. |
SqlValidator | Layer 1 — AST-parses the statement, asserts it's exactly one SELECT, extracts the referenced base tables. |
TableAccessPolicy | The interface GroupTableAccessPolicy implements — canAccess(roles, table): boolean. |
GroupTableAccessPolicy | Layer 2 — a fail-closed, group-based table allowlist (roleGroups + tablesByGroup). |
TenantScopeRewriter | Layer 3 — AND-s tenantColumn = '<tenantRef>' into every scoped table's WHERE, or rejects what it can't statically verify. |
injectLimit | Layer 4 — wraps an un-LIMITed query in a bounding subquery. |
ExecuteSqlDeps:
| Option | Type | Default | Purpose |
|---|---|---|---|
runner | { run(sql): Promise<Record<string, unknown>[]> } | — | Your read-only pool. The package opens no connection of its own. |
tableAccess | TableAccessPolicy | — | Required. The Layer 2 allowlist. |
tenantScope | TenantScopeRewriter | — | Optional. Omit only if tableAccess alone is sufficient isolation. |
validator | SqlValidator | fresh instance | Swap in a custom validator. |
maxRows | number | 100 | Row cap injected when the query has no LIMIT. |
Defense-in-depth, not a substitute for read-only credentials
Point runner at a read-only pool with a least-privilege DB user. The four guardrails (single-SELECT,
table allowlist, tenant scoping, row cap) are the mechanism; a read-only connection is your backstop.
When to use it
Reach for -data when you want the model to answer ad-hoc questions over real tables (counts,
lookups, aggregates) without writing a bespoke read tool per query shape. It's a read-kind tool by
design — auto-executing is safe because the pipeline, not the model, decides what's reachable.
The full pipeline — why the layer order matters, worked examples for each layer, and wiring the
factory form of provideAgentTool — is in Governed SQL.
@dudousxd/nestjs-agent-react
useAgentChat, the AgentChatTransport and AgentClient, styling-agnostic chat components, dictation, and the markdown subpath.
@dudousxd/nestjs-agent-rag
Retrieval-Augmented Generation — chunking, ingestion, an embedding-backed Retriever, and in-memory + pgvector stores. Framework-agnostic, core-only dep.