feat: schema graph via FK introspection

- GET .../db-connections/:id/foreign-keys — per-dialect
  information_schema/pragma query through dbx_execute_query,
  identifier-whitelisted, read-role gated
- Databases page: Graph tab renders tables as radial SVG nodes with
  FK edges; hover highlights adjacency, click inspects the table
- demo stubs for edges + wider demo table set

Authored By: TDvorak <[email protected]>
This commit is contained in:
Tomas Dvorak committed 2026-09-16 15:27:28 +02:00
1 parent 627bd89d5e
commit d57f30cd59
12 files changed
+496 -2

No files matched your search

@@ -40,6 +40,8 @@ export type { DBConnectionListResponse } from './models/DBConnectionListResponse
export type { DBConnectionTestResult } from './models/DBConnectionTestResult';
export type { DBDatabaseListResponse } from './models/DBDatabaseListResponse';
export type { DBDescribeResponse } from './models/DBDescribeResponse';
export type { DBForeignKeyEdge } from './models/DBForeignKeyEdge';
export type { DBForeignKeysResponse } from './models/DBForeignKeysResponse';
export type { DBMDTable } from './models/DBMDTable';
export type { DBObject } from './models/DBObject';
export type { DBQueryRequest } from './models/DBQueryRequest';
@@ -118,6 +120,8 @@ export { $DBConnectionListResponse } from './schemas/$DBConnectionListResponse';
export { $DBConnectionTestResult } from './schemas/$DBConnectionTestResult';
export { $DBDatabaseListResponse } from './schemas/$DBDatabaseListResponse';
export { $DBDescribeResponse } from './schemas/$DBDescribeResponse';
export { $DBForeignKeyEdge } from './schemas/$DBForeignKeyEdge';
export { $DBForeignKeysResponse } from './schemas/$DBForeignKeysResponse';
export { $DBMDTable } from './schemas/$DBMDTable';
export { $DBObject } from './schemas/$DBObject';
export { $DBQueryRequest } from './schemas/$DBQueryRequest';
@@ -0,0 +1,13 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type DBForeignKeyEdge = {
schema?: string;
table: string;
column: string;
ref_schema?: string;
ref_table: string;
ref_column: string;
};
@@ -0,0 +1,11 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { DBForeignKeyEdge } from './DBForeignKeyEdge';
export type DBForeignKeysResponse = {
edges: Array<DBForeignKeyEdge>;
raw?: string;
note?: string;
};
@@ -0,0 +1,30 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $DBForeignKeyEdge = {
properties: {
schema: {
type: 'string',
},
table: {
type: 'string',
isRequired: true,
},
column: {
type: 'string',
isRequired: true,
},
ref_schema: {
type: 'string',
},
ref_table: {
type: 'string',
isRequired: true,
},
ref_column: {
type: 'string',
isRequired: true,
},
},
} as const;
@@ -0,0 +1,21 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $DBForeignKeysResponse = {
properties: {
edges: {
type: 'array',
contains: {
type: 'DBForeignKeyEdge',
},
isRequired: true,
},
raw: {
type: 'string',
},
note: {
type: 'string',
},
},
} as const;
@@ -8,6 +8,7 @@ import type { DBConnectionListResponse } from '../models/DBConnectionListRespons
import type { DBConnectionTestResult } from '../models/DBConnectionTestResult';
import type { DBDatabaseListResponse } from '../models/DBDatabaseListResponse';
import type { DBDescribeResponse } from '../models/DBDescribeResponse';
import type { DBForeignKeysResponse } from '../models/DBForeignKeysResponse';
import type { DBQueryRequest } from '../models/DBQueryRequest';
import type { DBQueryResponse } from '../models/DBQueryResponse';
import type { DBRedisRequest } from '../models/DBRedisRequest';
@@ -244,6 +245,38 @@ export class DatabasesService {
},
});
}
/**
* @returns DBForeignKeysResponse Foreign key edges for the schema graph
* @throws ApiError
*/
public static listDbForeignKeys({
projectId,
connectionId,
database,
schema,
}: {
projectId: string,
connectionId: string,
database?: string,
schema?: string,
}): CancelablePromise<DBForeignKeysResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/projects/{projectID}/db-connections/{connectionID}/foreign-keys',
path: {
'projectID': projectId,
'connectionID': connectionId,
},
query: {
'database': database,
'schema': schema,
},
errors: {
404: `Error response`,
502: `Error response`,
},
});
}
/**
* @returns DBQueryResponse Query result (at most 100 rows)
* @throws ApiError