Skip to main content

Overview

Widget server actions handle CRUD operations for dashboard widgets, including creation, updates, deletion, reordering, and data retrieval for widget rendering.

createWidget

Creates a new widget and adds it to the project’s widget order.
export async function createWidget(
  data: Prisma.WidgetCreateInput | Prisma.WidgetUncheckedCreateInput
): Promise<Widget>

Parameters

data
WidgetCreateInput
required
Widget creation data following Prisma schema
data.projectId
string
required
ID of the project this widget belongs to
data.type
string
required
Widget type (e.g., “chart”, “table”, “metric”)Note: Type and subtype are automatically normalized via extractWidgetTypeInfo
data.subtype
string
Widget subtype (e.g., “bar”, “line”, “pie” for charts)
data.title
string
required
Widget title displayed in the dashboard
data.configs
Record<string, unknown>
Widget configuration object containing:
  • Chart settings (axes, colors, legends)
  • Data source configuration
  • Display options
  • Query parameters

Response

Returns the created Widget object with all fields populated.
id
string
Unique widget identifier
projectId
string
Parent project ID
type
string
Normalized widget type
subtype
string | null
Normalized widget subtype
title
string
Widget title
configs
object
Widget configuration object
createdAt
Date
Creation timestamp
updatedAt
Date
Last update timestamp

Behavior

  • Type normalization: Automatically normalizes type and subtype using extractWidgetTypeInfo
  • Order management: Automatically appends widget ID to project’s orderedWidgetIds array
  • Transaction safety: Widget creation and order update occur in sequence

Example

import { createWidget } from '@/app/actions/dashboard/crudWidgets'

const widget = await createWidget({
  projectId: 'clx7890abc',
  type: 'chart',
  subtype: 'bar',
  title: 'Monthly Revenue',
  configs: {
    dataSource: {
      connectionId: 'conn_123',
      table: 'sales',
      xColumn: 'month',
      yColumn: 'revenue'
    },
    display: {
      showLegend: true,
      showGrid: true,
      colorScheme: 'blue'
    }
  }
})

console.log('Created widget:', widget.id)

updateWidget

Updates an existing widget’s properties and configuration.
export async function updateWidget(
  data: Prisma.WidgetUpdateInput,
  widgetId: string
): Promise<Widget>

Parameters

data
WidgetUpdateInput
required
Widget update data (partial updates supported)
data.type
string
New widget type
data.subtype
string
New widget subtype
data.title
string
New widget title
data.configs
Record<string, unknown>
New widget configuration (replaces existing configs completely)
widgetId
string
required
ID of the widget to update

Response

Returns the updated Widget object.

Behavior

  • Type normalization: Automatically normalizes type and subtype
  • Config replacement: Configs are replaced entirely, not merged
  • Partial updates: Only provided fields are updated

Example

import { updateWidget } from '@/app/actions/dashboard/crudWidgets'

const updated = await updateWidget(
  { title: 'Q1 Revenue Analysis' },
  'widget_123'
)
The configs field replaces the entire configuration object. Include all required config properties when updating.

UpsertWidget

Creates a new widget or updates an existing one based on whether a widgetId is provided.
export async function UpsertWidget(
  data: unknown,
  widgetId?: string
): Promise<Widget>

Parameters

data
unknown
required
Widget data (will be cast to WidgetCreateInput or WidgetUpdateInput)
widgetId
string
Widget ID (if updating). Omit to create a new widget.

Response

Returns the created or updated Widget object.

Example

import { UpsertWidget } from '@/app/actions/dashboard/crudWidgets'

// Create new widget
const newWidget = await UpsertWidget({
  projectId: 'clx7890abc',
  type: 'chart',
  title: 'Sales Chart',
  configs: { /* ... */ }
})

// Update existing widget
const updatedWidget = await UpsertWidget(
  { title: 'Updated Sales Chart' },
  'widget_123'
)

upsertWidgetAction

Server action wrapper for UpsertWidget (marked with "use server").
export async function upsertWidgetAction(
  data: unknown,
  widgetId?: string
): Promise<Widget>

Parameters

Same as UpsertWidget.

Response

Returns the created or updated Widget object.

Example

import { upsertWidgetAction } from '@/app/actions/dashboard/upsertWidget'

// Use in client components or server components
const widget = await upsertWidgetAction(widgetData, widgetId)
Use upsertWidgetAction in client components. Use UpsertWidget directly in server-side code.

getWidgetById

Retrieves a single widget by its ID.
export async function getWidgetById(widgetId: string): Promise<Widget | null>

Parameters

widgetId
string
required
ID of the widget to retrieve

Response

Returns a Widget object if found, or null if the widget doesn’t exist.

Example

import { getWidgetById } from '@/app/actions/dashboard/crudWidgets'

const widget = await getWidgetById('widget_123')

if (widget) {
  console.log(`Widget: ${widget.title} (${widget.type})`)
}

deleteWidget

Deletes a widget and removes it from the project’s widget order.
export async function deleteWidget(widgetId: string): Promise<boolean>

Parameters

widgetId
string
required
ID of the widget to delete

Response

Returns true if deletion was successful, or throws an error on failure.

Behavior

  • Order cleanup: Automatically removes widget ID from project’s orderedWidgetIds array
  • Cascade deletion: Widget is permanently deleted from the database
  • Error handling: Throws error if widget not found or deletion fails

Example

import { deleteWidget } from '@/app/actions/dashboard/crudWidgets'

try {
  await deleteWidget('widget_123')
  console.log('Widget deleted successfully')
} catch (error) {
  console.error('Failed to delete widget:', error)
}

deleteWidgetAction

Server action wrapper for deleteWidget with structured error handling.
export async function deleteWidgetAction(
  widgetId: string
): Promise<DeleteWidgetResponse>

Parameters

widgetId
string
required
ID of the widget to delete

Response

success
boolean
required
Indicates if deletion was successful
error
string
Error message (only when success is false)

Example

import { deleteWidgetAction } from '@/app/actions/dashboard/deleteWidget'

const result = await deleteWidgetAction('widget_123')

if (result.success) {
  console.log('Widget deleted')
} else {
  console.error('Error:', result.error)
}

updateWidgetOrder

Updates the order of widgets in a project’s dashboard.
export async function updateWidgetOrder(
  projectId: string,
  newOrder: string[]
): Promise<UpdateOrderResponse>

Parameters

projectId
string
required
ID of the project to update widget order for
newOrder
string[]
required
Array of widget IDs in the desired order

Response

success
boolean
required
Indicates if order update was successful
error
string
Error message (only when success is false)

Behavior

  • Complete replacement: Replaces the entire orderedWidgetIds array
  • No validation: Does not verify widget IDs exist or belong to the project
  • Persistence: Immediately saves to database

Example

import { updateWidgetOrder } from '@/app/actions/dashboard/updateWidgetOrder'

// Move widget_789 to the front
const result = await updateWidgetOrder(
  'clx7890abc',
  ['widget_789', 'widget_123', 'widget_456']
)

if (result.success) {
  console.log('Widget order updated')
}
Widget order is stored in the Project.orderedWidgetIds field as an array of widget IDs.

GetTableData

Retrieves data from a database table for widget rendering.
export async function GetTableData(
  encryptedDbAccess: string,
  tableName: string
): Promise<GetTableDataResponse>

Parameters

encryptedDbAccess
string
required
Encrypted database connection credentials (from DbConnection.dbAccess)
tableName
string
required
Name of the table to query

Response

data
Record<string, unknown>[]
Array of table rows (only when success)Each row is an object with column names as keys. Values are normalized:
  • Numeric types: number or null
  • Other types: string or null
error
string
Error message (only when error occurs)Possible errors:
  • "Missing database access or table name."
  • "Table \"tableName\" could not be queried."
  • "Failed to retrieve data for table tableName: {error}"

Behavior

  • Connection pooling: Uses withKnexConnection for automatic connection management
  • Type normalization: Converts all values to string or number based on column type
  • Column introspection: Automatically detects column types from database schema
  • NULL handling: Converts undefined and invalid values to null
  • Special type handling:
    • Numeric types (int, float, decimal) → number
    • Date types → ISO string
    • Boolean → "true" or "false"
    • All others → string

Example

import { GetTableData } from '@/app/actions/dashboard/getTableData'
import { getProjectWithConnections } from '@/app/actions/project/crud'

// Get connection credentials
const project = await getProjectWithConnections(projectId, userId)
const connection = project.data?.databases[0]

if (connection) {
  // Retrieve table data
  const result = await GetTableData(
    connection.dbAccess,
    'sales'
  )
  
  if (result.data) {
    console.log(`Retrieved ${result.data.length} rows`)
    console.log('Columns:', Object.keys(result.data[0]))
  } else {
    console.error('Error:', result.error)
  }
}
This action queries the database directly with SELECT *. Ensure proper database permissions and consider performance for large tables.

Type Definitions

import type { Prisma, Widget } from '@prisma/client'

// Widget input types
type WidgetCreateInput = Prisma.WidgetCreateInput
type WidgetUpdateInput = Prisma.WidgetUpdateInput
type WidgetUncheckedCreateInput = Prisma.WidgetUncheckedCreateInput

// Response types
interface DeleteWidgetResponse {
  success: boolean
  error?: string
}

interface UpdateOrderResponse {
  success: boolean
  error?: string
}

interface GetTableDataResponse {
  data?: Record<string, unknown>[]
  error?: string
}

// Widget object structure
interface Widget {
  id: string
  projectId: string
  type: string
  subtype: string | null
  title: string
  configs: Record<string, unknown>
  createdAt: Date
  updatedAt: Date
}

// Common widget config structures
interface ChartWidgetConfigs {
  dataSource: {
    connectionId: string
    table: string
    xColumn: string
    yColumn: string
    aggregation?: 'sum' | 'avg' | 'count' | 'min' | 'max'
  }
  display: {
    showLegend?: boolean
    showGrid?: boolean
    colorScheme?: string
    orientation?: 'horizontal' | 'vertical'
  }
}

interface TableWidgetConfigs {
  dataSource: {
    connectionId: string
    table: string
    columns: string[]
    orderBy?: string
    limit?: number
  }
}

Widget Configuration

The configs field structure varies by widget type:
{
  dataSource: {
    connectionId: string      // DB connection ID
    table: string             // Table name
    xColumn: string           // X-axis column
    yColumn: string           // Y-axis column
    aggregation?: string      // 'sum', 'avg', 'count', etc.
    filters?: Filter[]        // Optional filters
  },
  display: {
    showLegend: boolean
    showGrid: boolean
    colorScheme: string       // 'blue', 'green', 'red', etc.
    orientation: string       // 'horizontal' | 'vertical'
  }
}

Build docs developers (and LLMs) love