Skip to main content

Overview

Projects are the foundation of VizBoard’s data visualization platform. Each project contains database connections, widgets, and dashboards that visualize your data. Projects can be private or public, allowing you to share your visualizations with others.

Project Structure

Every project in VizBoard includes:

Database Connections

Connect to PostgreSQL databases to access your data

Widgets

Create visualizations from your connected data sources

Dashboards

Organize widgets in a customizable layout

Sharing Options

Control project visibility with public or private access

Creating a Project

Projects can be created with or without database connections. The project creation process automatically validates connections and introspects database schemas.
1

Initialize Project

Provide a title and optional description for your project:
const projectData = {
  title: "Sales Analytics Dashboard",
  description: "Quarterly sales performance metrics",
  isPublic: false,
  userId: session.user.id,
  connections: []
};
2

Add Database Connections

Configure one or more PostgreSQL database connections. Connections are encrypted before storage:
const connections = [
  {
    title: "Production DB",
    host: "db.example.com",
    port: 5432,
    database: "sales_data",
    user: "analytics_user",
    password: "secure_password"
  }
];
See Database Connections for detailed configuration options.
3

Create with Server Action

Use the createProjectWithConnections Server Action:
import { createProjectWithConnections } from "@/app/actions/project";

const result = await createProjectWithConnections({
  title: projectData.title,
  description: projectData.description,
  isPublic: projectData.isPublic,
  userId: session.user.id,
  connections: connections
});

if (result.success) {
  console.log("Project created:", result.projectId);
  console.log("Validation result:", result.validationResult);
}

Auto-Introspection

When you create a project with database connections, VizBoard automatically:
  1. Validates each connection to ensure it can connect to the database
  2. Introspects the database schema for valid connections
  3. Stores schema metadata for use in widget configuration
src/app/actions/project/crud.ts
// After validation, introspect valid connections
if (validationResult?.success && validationResult?.connectionStatuses) {
  const validConnections = validationResult.connectionStatuses.filter(
    (conn) => conn.isValid
  );

  // Introspect each valid connection in parallel
  const introspectionPromises = validConnections.map(
    async (connStatus) => {
      await CreateDbSchema(connStatus.id);
      return { connectionId: connStatus.id, success: true };
    }
  );
}
Schema introspection runs automatically after connection validation. You don’t need to manually trigger it unless you want to refresh the schema after database changes.

Updating Projects

Projects can be updated at any time, including their metadata and database connections.

Update Project Metadata

import { updateProject } from "@/app/actions/project";

const result = await updateProject({
  id: projectId,
  title: "Updated Title",
  description: "New description",
  isPublic: true
});

Update Database Connections

You can add, modify, or remove database connections when updating a project:
await updateProject({
  id: projectId,
  connections: [
    {
      id: existingConnectionId,  // Include ID to update existing
      title: "Updated Connection",
      host: "new-host.example.com",
      port: 5432,
      database: "new_database",
      user: "user",
      password: "KEEP_CURRENT_PASSWORD"  // Special value to keep existing password
    },
    {
      // No ID means this is a new connection
      title: "New Connection",
      host: "db2.example.com",
      port: 5432,
      database: "analytics",
      user: "readonly",
      password: "new_password"
    }
  ]
});
Use "KEEP_CURRENT_PASSWORD" as the password value to preserve the existing encrypted password for a connection.

Connection Validation on Update

When updating a project, VizBoard automatically:
  • Validates all connections (new and existing)
  • Re-introspects schemas for valid connections
  • Updates connection status badges in the UI
  • Disables public sharing if no valid connections exist
src/app/actions/project/crud.ts
// If project has no valid connections, force isPublic=false
const validConnections = updatedConnections.filter(
  (conn) => conn.isValid === true
).length;

if (validConnections === 0) {
  await tx.project.update({
    where: { id: data.id },
    data: { isPublic: false, idPublic: null },
  });
}

Deleting Projects

Deleting a project is permanent and cascades to all related resources.
import { deleteProject } from "@/app/actions/project";

const result = await deleteProject(projectId, userId);

if (result.success) {
  console.log(result.message); // "Project 'Title' deleted successfully"
}

What Gets Deleted

When you delete a project, VizBoard removes:
1

Widgets

All visualization widgets associated with the project
2

Database Connections

All database connection configurations (encrypted credentials are removed)
3

Project Data

The project record and all metadata
Project deletion is permanent and cannot be undone. Make sure to export any important visualizations before deleting a project.

Public vs Private Projects

VizBoard supports two visibility modes for projects:

Private Projects (Default)

Private projects are only accessible to the project owner:
const project = {
  title: "Internal Analytics",
  isPublic: false,  // Default
  // Only accessible at /projects/{projectId}/dashboard
};

Public Projects

Public projects can be shared via a public URL:
const project = {
  title: "Public Dashboard",
  isPublic: true,
  idPublic: "abc123def456",  // Auto-generated unique ID
  // Accessible at /public/{idPublic}
};
Projects can only be made public if they have at least one valid database connection. VizBoard automatically sets isPublic: false if all connections become invalid.

Public Sharing Requirements

To enable public sharing:
  1. Project must have at least one valid database connection
  2. Connection validation must pass successfully
  3. Database schema must be introspected
// Public sharing is automatically disabled without valid connections
if (validConnections === 0) {
  project.isPublic = false;
  project.idPublic = null;
}

Retrieving Projects

Get Single Project

Retrieve a project with all its connections and metadata:
import { getProjectWithConnections } from "@/app/actions/project";

const result = await getProjectWithConnections(projectId, userId);

if (result.success) {
  const project = result.data;
  console.log(project.title);
  console.log(project.databases);  // Array of connections with status
  console.log(project.orderedWidgetIds);  // Widget display order
}

Get All User Projects

Retrieve all projects for the authenticated user with statistics:
import { getUserProjects } from "@/app/actions/project";

const result = await getUserProjects(userId);

if (result.success) {
  result.data.forEach(project => {
    console.log(project.title);
    console.log(`Connections: ${project.validConnections}/${project.totalConnections}`);
    console.log(`Widgets: ${project.widgetCount}`);
    console.log(`Last validation: ${project.lastValidationDate}`);
  });
}

Project Statistics

The getUserProjects action returns rich metadata for each project:
interface ProjectWithStats {
  // Basic fields
  id: string;
  title: string;
  description: string | null;
  isPublic: boolean;
  createdAt: Date;
  updatedAt: Date;
  
  // Connection statistics
  totalConnections: number;
  validConnections: number;
  hasValidatedConnections: boolean;
  lastValidationDate?: Date;
  
  // Schema statistics
  totalSchemas: number;
  validSchemas: number;
  hasIntrospectedSchemas: boolean;
  lastSchemaIntrospectionAt?: Date;
  
  // Widget statistics
  widgetCount: number;
}

Schema Regeneration

Refresh database schemas for all valid connections in a project:
import { regenerateProjectSchemas } from "@/app/actions/project";

const result = await regenerateProjectSchemas(projectId, userId);

if (result.success) {
  console.log(`Regenerated: ${result.data.successfulRegeneration}/${result.data.totalConnections}`);
  
  result.data.details.forEach(detail => {
    console.log(`${detail.connectionTitle}: ${detail.success ? 'Success' : detail.error}`);
  });
}
Schema regeneration is useful after making changes to your database structure (adding tables, columns, etc.). Only connections with isValid: true are re-introspected.

Data Model

Projects are stored in the Prisma schema with the following structure:
prisma/schema.prisma
model Project {
  id                  String         @id @default(uuid())
  userId              String
  title               String
  description         String?
  isPublic            Boolean        @default(false)
  idPublic            String?        @unique
  createdAt           DateTime       @default(now())
  updatedAt           DateTime       @updatedAt
  lastIntrospectionAt DateTime?
  dbconnections       DbConnection[]
  user                User           @relation(fields: [userId], references: [id], onDelete: Cascade)
  widgets             Widget[]
  orderedWidgetIds    String[]

  @@map("projects")
}

Best Practices

Use Descriptive Titles

Choose clear project titles that describe the data or purpose

Document Your Projects

Add descriptions to help team members understand the project’s purpose

Validate Regularly

Run validation checks after database changes to ensure connections remain valid

Control Public Access

Only make projects public when necessary, and review shared dashboards regularly

Next Steps

Database Connections

Configure PostgreSQL connections

Widgets

Create data visualizations

Dashboards

Build and organize dashboards

Build docs developers (and LLMs) love