--- title: "Using sqlserverconnect with Shiny" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Using sqlserverconnect with Shiny} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ## Why use connection pools in Shiny? A plain DBI connection is fine for scripts, but Shiny apps can serve multiple users simultaneously. Opening a fresh connection per request is slow and can exhaust your server's connection limit. A **connection pool** solves this by maintaining a set of reusable connections that are checked out and returned automatically. `sqlserverconnect` makes this a one-argument switch: `pool = TRUE`. ## DBI vs pool: when to use which? | Use case | `pool = FALSE` (default) | `pool = TRUE` | |----------------------|----------------------------------|----------------------------------------| | Interactive scripts | Simple and direct | Usually unnecessary | | Long-running jobs | May time out if idle | Better handling of idle connections | | Shiny apps | Risk of too many connections | Recommended best practice | | Parallel workloads | Each worker opens its own conn | Pool can reuse connections per process | ## Basic Shiny setup Create the pool once at startup, use it in your server logic, and close it when the app stops: ```{r} # app.R (or global.R) library(sqlserverconnect) library(shiny) library(DBI) # Create pool at startup db_pool <- db_connect( server = "localhost", database = "master", pool = TRUE ) # Close pool when app stops onStop(function() { db_disconnect(db_pool) }) ui <- fluidPage( tableOutput("databases") ) server <- function(input, output, session) { output$databases <- renderTable({ DBI::dbGetQuery(db_pool, "SELECT TOP (10) name, create_date FROM sys.databases") }) } shinyApp(ui, server) ``` ## Key points **Create the pool once.** Put `db_connect(pool = TRUE)` in `global.R` or at the top of `app.R`, not inside `server`. Creating a pool per session defeats the purpose. **Use `onStop()` for cleanup.** This ensures the pool is closed when the app shuts down, releasing all connections back to the server. **Query the pool like a regular connection.** You can pass it directly to `DBI::dbGetQuery()`, `DBI::dbExecute()`, and other DBI functions. The pool handles checkout/return behind the scenes. **`db_disconnect()` works for both.** You don't need to remember whether you created a DBI connection or a pool — `db_disconnect()` detects the type and calls the right cleanup function. ## Reactive queries For queries that depend on user input, use them inside reactive expressions as usual: ```{r} server <- function(input, output, session) { table_data <- reactive({ req(input$table_name) query <- paste0("SELECT TOP (100) * FROM ", input$table_name) DBI::dbGetQuery(db_pool, query) }) output$data <- renderTable({ table_data() }) } ``` **Note:** Be careful with user-supplied table names — in production, validate them against a known list or use parameterized queries to avoid SQL injection.