# Server Mode Guide for bettr

Version 1.6.0 and later

## Overview

Version 1.6.0 introduces server mode, a new way to deploy and use bettr that enables browser-based JSON file uploads, automatic caching, and programmatic data loading via URLs. This makes bettr suitable for:

- Multi-user server deployments where different users need to visualize their own data
- Web-based workflows without requiring R programming knowledge
- Data sharing through portable JSON files and direct links

## What’s New in Version 1.6.0

### Server Mode for JSON Uploads

The `bettr()` function now supports a `serverMode` parameter that launches the app in a special mode where users upload JSON files through the browser.

Benefits:

- No R programming required for end users
- Multiple users can use the same server instance with their own data
- Data is isolated per user session
- Automatic browser caching for quick reload

### JSON Serialization

New functions for converting bettr data to/from JSON format:

- `bettrToJSON()` - Export `SummarizedExperiment` to JSON
- `bettrFromJSON()` - Import JSON back to `SummarizedExperiment`

Benefits:

- Portable data format (language-agnostic)
- Easy data sharing between users and systems
- Preserves all metadata, colors, weights, and transformations
- Enables web-based workflows

### Browser localStorage Caching

Automatic client-side caching of uploaded JSON data in server mode.

What gets cached:

- Uploaded JSON data

Benefits:

- Page refresh doesn’t lose your work
- Survives browser restarts
- No manual saving required
- Automatic caching after upload

### Cache Versioning

Administrators can force cache invalidation when deploying updates.

Use cases:

- Deploy schema changes that require fresh data
- Force all users to re-upload after breaking changes
- Manage cache in multi-tenant environments
- Troubleshooting cache-related issues

### URL Parameters for Programmatic Loading

Load JSON data automatically via URL query parameters.

Benefits:

- Create direct links to specific visualizations
- Integrate with external tools and pipelines
- Automate opening visualizations after benchmark runs
- Share analysis results with a simple URL

## Server Mode Basics

### Starting a Server

    library(bettr)

    # Basic server mode
    bettr(serverMode = TRUE)

    # With cache versioning
    bettr(serverMode = TRUE, cacheVersion = "v1.0")

    # Custom theme
    bettr(serverMode = TRUE, bstheme = "flatly")

### What Happens When You Start Server Mode

1. The app starts listening on a local port (e.g., `http://127.0.0.1:4567`)
2. Users navigate to this URL in their browser
3. The upload interface is displayed
4. Users can:
   - Upload JSON files manually
   - Have data loaded automatically via URL parameters (if provided)
5. Once data is uploaded, the full bettr interface appears
6. All interactions are automatically cached in the browser

### User Interface

Upload Mode Interface:

- Clean, simple upload area
- “Choose JSON File” button
- Information about browser caching
- No “Close app” button (for multi-user safety)

After Upload:

- Full bettr visualization interface
- All standard features available
- “Load Data” panel for uploading new files
- “Clear Cached Data” button

## JSON Serialization

### Creating JSON Files

#### From Data Frames

    library(bettr)

    # Create benchmark data
    benchmark_data <- data.frame(
        Method = c("AlgorithmA", "AlgorithmB", "AlgorithmC"),
        Accuracy = c(0.95, 0.92, 0.89),
        Speed = c(120, 150, 100),
        Memory = c(256, 512, 128)
    )

    # Add metric metadata
    metric_info <- data.frame(
        Metric = c("Accuracy", "Speed", "Memory"),
        Type = c("Quality", "Performance", "Resource"),
        Unit = c("Proportion", "ms", "MB")
    )

    # Define initial transformations
    transforms <- list(
        Speed = list(flip = TRUE, transform = "[0,1]"),
        Memory = list(flip = TRUE, transform = "[0,1]")
    )

    # Create SummarizedExperiment
    bettrSE <- assembleSE(
        df = benchmark_data,
        idCol = "Method",
        metricInfo = metric_info,
        initialTransforms = transforms
    )

    # Export to JSON
    json_file <- tempfile(fileext = ".json")
    bettrToJSON(bettrSE, file = json_file)

Click to view example JSON structure

The exported JSON file has the following structure:

    {
      "idCol": "Method",
      "data": [
        {
          "Method": "AlgorithmA",
          "Accuracy": 0.95,
          "Speed": 120,
          "Memory": 256,
          "_row": "AlgorithmA"
        },
        {
          "Method": "AlgorithmB",
          "Accuracy": 0.92,
          "Speed": 150,
          "Memory": 512,
          "_row": "AlgorithmB"
        },
        {
          "Method": "AlgorithmC",
          "Accuracy": 0.89,
          "Speed": 100,
          "Memory": 128,
          "_row": "AlgorithmC"
        }
      ],
      "metricInfo": [
        {
          "Metric": "Accuracy",
          "Type": "Quality",
          "Unit": "Proportion"
        },
        {
          "Metric": "Speed",
          "Type": "Performance",
          "Unit": "ms"
        },
        {
          "Metric": "Memory",
          "Type": "Resource",
          "Unit": "MB"
        }
      ],
      "idInfo": {},
      "initialWeights": {},
      "initialTransforms": {
        "Speed": {
          "flip": true,
          "transform": "[0,1]"
        },
        "Memory": {
          "flip": true,
          "transform": "[0,1]"
        }
      }
    }

Key components:

- `idCol`: Name of the identifier column
- `data`: Array of objects, one per method with all metric values
- `metricInfo`: Metadata about each metric (optional classifications, units, etc.)
- `idInfo`: Additional metadata about methods (optional)
- `initialWeights`: Default metric weights (optional)
- `initialTransforms`: Transformations to apply to metrics (flip for “lower is better”, transform for normalization)

#### From Existing SummarizedExperiment

    # If you already have a SummarizedExperiment
    json_file <- tempfile(fileext = ".json")
    bettrToJSON(my_existing_SE, file = json_file)

    # Get JSON as string (for programmatic use)
    json_string <- bettrToJSON(my_existing_SE, file = NULL)

### Reading JSON Files

    # Read from file
    bettrSE <- bettrFromJSON(file = "benchmark_results.json")

    # Read from JSON string
    bettrSE <- bettrFromJSON(json = json_string)

    # Use in non-server mode
    bettr(bettrSE = bettrSE)

## URL Parameters for Programmatic Loading

### Overview

URL query parameters allow you to automatically load JSON data when users open the app.

### Two Loading Methods

#### Load from URL (`jsonUrl`)

    http://localhost:4567/?jsonUrl=https://example.com/data.json

Use cases:

- Data hosted on web servers
- GitHub raw file URLs
- Cloud storage (S3, Google Cloud Storage, etc.)
- Internal company servers

Example:

    # Start server
    bettr(serverMode = TRUE)

    # Construct URL
    base_url <- "http://localhost:4567"
    data_url <- "https://raw.githubusercontent.com/user/repo/main/results.json"
    full_url <- paste0(base_url, "/?jsonUrl=", URLencode(data_url, reserved = TRUE))

    # Open in browser
    browseURL(full_url)

#### Load from File Path (`jsonFile`)

    http://localhost:4567/?jsonFile=/absolute/path/to/data.json

Use cases:

- Local server deployments
- Internal network file shares
- Automated pipelines on same machine
- Development and testing

Example:

    # Start server
    bettr(serverMode = TRUE)

    # Construct URL
    base_url <- "http://localhost:4567"
    file_path <- "/Users/username/benchmarks/latest_results.json"
    full_url <- paste0(base_url, "/?jsonFile=", URLencode(file_path, reserved = TRUE))

    # Open in browser
    browseURL(full_url)

Using the included example data:

The bettr package includes a pre-exported JSON file from the DuoClustering2018 dataset that can be used for testing and examples.

    # Start server
    bettr(serverMode = TRUE)

    # Get path to included example JSON file
    json_path <- system.file("extdata", "duo2018_bettr.json", package = "bettr")

    # Construct URL
    base_url <- "http://localhost:4567"
    full_url <- paste0(base_url, "/?jsonFile=", URLencode(json_path, reserved = TRUE))

    # Open in browser
    browseURL(full_url)

## Browser Cache Management

### How Caching Works

1. Automatic Save:
   - When you upload a JSON file, it’s saved to browser localStorage
   - Happens immediately after successful upload
   - No manual action required
2. Automatic Restore:
   - When you reload the page, cached data is automatically restored
   - A notification confirms the restoration
3. Cache Storage:
   - Stored in browser’s localStorage (typically 5-10MB limit)
   - Specific to your browser and domain
   - Persists across browser restarts
   - Not shared across different browsers or devices

### Manual Cache Control

#### Clear Cache Button

Located in the “Load Data” accordion panel:

    Load Data
    ├── Choose JSON File
    └── Clear Cached Data (button)

Click to:

- Remove all cached data
- Start fresh with a new dataset
- Free up browser storage

### Cache Versioning (Administrators)

#### Setting Up Versioning

    # Deploy version 1.0
    bettr(serverMode = TRUE, cacheVersion = "v1.0")

    # Later, deploy version 1.1 with breaking changes
    bettr(serverMode = TRUE, cacheVersion = "v1.1")

#### Version String Format

Any string works - choose what makes sense for your workflow:

    # Semantic versioning
    cacheVersion = "v1.2.3"

    # Date-based
    cacheVersion = "2025-10-07"

    # Build numbers
    cacheVersion = "build-456"

    # Custom
    cacheVersion = "production-release-oct2025"

#### User Experience

When version changes:

1. User opens the app
2. Browser detects version mismatch
3. Cache is automatically cleared
4. User sees: “Cache was cleared due to version update. Please upload your data.”
5. User uploads their JSON file again
6. New cache is created with current version

# Session info

    sessionInfo()
    #> R version 4.6.0 (2026-04-24)
    #> Platform: x86_64-pc-linux-gnu
    #> Running under: Ubuntu 24.04.4 LTS
    #>
    #> Matrix products: default
    #> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
    #> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so;  LAPACK version 3.12.0
    #>
    #> locale:
    #>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
    #>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
    #>  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8
    #>  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C
    #>  [9] LC_ADDRESS=C               LC_TELEPHONE=C
    #> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
    #>
    #> time zone: Etc/UTC
    #> tzcode source: system (glibc)
    #>
    #> attached base packages:
    #> [1] stats4    stats     graphics  grDevices utils     datasets  methods
    #> [8] base
    #>
    #> other attached packages:
    #>  [1] dplyr_1.2.1                 tibble_3.3.1
    #>  [3] SummarizedExperiment_1.42.0 Biobase_2.72.0
    #>  [5] GenomicRanges_1.64.0        Seqinfo_1.2.0
    #>  [7] IRanges_2.46.0              S4Vectors_0.50.0
    #>  [9] BiocGenerics_0.58.0         generics_0.1.4
    #> [11] MatrixGenerics_1.24.0       matrixStats_1.5.0
    #> [13] bettr_1.8.0                 BiocStyle_2.40.0
    #>
    #> loaded via a namespace (and not attached):
    #>  [1] gridExtra_2.3         rlang_1.2.0           magrittr_2.0.5
    #>  [4] clue_0.3-68           GetoptLong_1.1.1      otel_0.2.0
    #>  [7] compiler_4.6.0        png_0.1-9             vctrs_0.7.3
    #> [10] stringr_1.6.0         pkgconfig_2.0.3       shape_1.4.6.1
    #> [13] crayon_1.5.3          fastmap_1.2.0         backports_1.5.1
    #> [16] XVector_0.52.0        labeling_0.4.3        utf8_1.2.6
    #> [19] learnr_0.11.6         shinyjqui_0.4.1       promises_1.5.0
    #> [22] rmarkdown_2.31        purrr_1.2.2           xfun_0.57
    #> [25] cachem_1.1.0          jsonlite_2.0.0        later_1.4.8
    #> [28] DelayedArray_0.38.0   parallel_4.6.0        cluster_2.1.8.2
    #> [31] R6_2.6.1              bslib_0.10.0          stringi_1.8.7
    #> [34] RColorBrewer_1.1-3    rpart_4.1.27          jquerylib_0.1.4
    #> [37] Rcpp_1.1.1-1.1        assertthat_0.2.1      iterators_1.0.14
    #> [40] knitr_1.51            base64enc_0.1-6       httpuv_1.6.17
    #> [43] Matrix_1.7-5          nnet_7.3-20           tidyselect_1.2.1
    #> [46] rstudioapi_0.18.0     abind_1.4-8           yaml_2.3.12
    #> [49] doParallel_1.0.17     codetools_0.2-20      lattice_0.22-9
    #> [52] shiny_1.13.0          withr_3.0.2           S7_0.2.2
    #> [55] evaluate_1.0.5        foreign_0.8-91        circlize_0.4.18
    #> [58] pillar_1.11.1         BiocManager_1.30.27   checkmate_2.3.4
    #> [61] DT_0.34.0             foreach_1.5.2         rprojroot_2.1.1
    #> [64] ggplot2_4.0.3         scales_1.4.0          xtable_1.8-8
    #> [67] glue_1.8.1            Hmisc_5.2-5           maketools_1.3.2
    #> [70] tools_4.6.0           sys_3.4.3             data.table_1.18.2.1
    #> [73] buildtools_1.0.0      cowplot_1.2.0         grid_4.6.0
    #> [76] tidyr_1.3.2           sortable_0.6.0        colorspace_2.1-2
    #> [79] htmlTable_2.5.0       Formula_1.2-5         cli_3.6.6
    #> [82] S4Arrays_1.12.0       ComplexHeatmap_2.28.0 gtable_0.3.6
    #> [85] sass_0.4.10           digest_0.6.39         SparseArray_1.12.0
    #> [88] rjson_0.2.23          htmlwidgets_1.6.4     farver_2.1.2
    #> [91] htmltools_0.5.9       lifecycle_1.0.5       GlobalOptions_0.1.4
    #> [94] mime_0.13