Get Productive in 10 Minutes
This quickstart gets you from zero to querying data in minutes. We’ll build a simple artist dashboard that demonstrates the core artutils workflow.
Prerequisites
Before starting, ensure you have:
- Database access: artutils connects to the Artalytics PostgreSQL database
- Environment variables configured: Database credentials must be set
- artcore installed: artutils depends on artcore for infrastructure
Step 1: Validate Your Environment
Always start by validating your setup. This catches configuration issues early:
What to check: - ART_PGHOST - Database host - ART_PGPORT - Database port (usually 5432) - ART_PGUSER - Your database username - ART_PGPASS - Your database password - ART_PGDATA - Database name
If check_env_dba() fails, see the artcore README for configuration instructions.
Step 2: Your First Query
Let’s find an artist and get their basic info:
# Look up artist by URL slug (the human-friendly identifier)
artist_record <- get_artist_by_slug("bobby-fatemi")
# Check if artist exists
if (is.null(artist_record)) {
stop("Artist not found - check the slug spelling")
}
# Extract the UUID for subsequent queries
artist <- artist_record$artist_uuid
# Display basic info
cat("Found:", artist_record$artist_name, "\n")
cat("UUID:", artist, "\n")
cat("Location:", artist_record$city, ",", artist_record$state, "\n")
cat("Member since:", format(artist_record$created_utc, "%Y-%m-%d"), "\n")Key pattern: artutils functions return NULL for missing data instead of throwing errors during lookup. Always check for NULL before proceeding.
Step 3: Gather Artist Statistics
Now get aggregate statistics for the profile header:
These statistics are pre-aggregated in the database for performance. They’re updated by update_artist_stats() after modifications.
Step 4: Build a Simple Dashboard
Let’s combine everything into a reusable dashboard function:
build_artist_dashboard <- function(slug) {
# Step 1: Resolve slug
artist_record <- get_artist_by_slug(slug)
if (is.null(artist_record)) {
return(list(error = "artist_not_found", slug = slug))
}
artist <- artist_record$artist_uuid
# Step 2: Gather data (reuse connection for efficiency)
cn <- dbc()
on.exit(dbd(cn))
stats <- get_artist_stats(artist, cn = cn)
collections <- get_artist_collections_summary(artist, cn = cn)
recent_works <- get_artist_recent_works(artist, limit = 6, cn = cn)
# Step 3: Build dashboard structure
list(
artist = list(
name = artist_record$artist_name,
location = paste(artist_record$city, artist_record$state, sep = ", "),
bio = artist_record$bio,
avatar_url = path_artist_thumb(artist),
social = list(
instagram = artist_record$url_ig,
website = artist_record$url_site
)
),
portfolio = list(
artworks = stats$artworks,
collections = stats$collections,
total_hours = stats$total_hours,
total_brushes = stats$total_brushes,
avg_hours_per_work = stats$total_hours / max(stats$artworks, 1)
),
collections = collections,
recent_works = recent_works
)
}
# Use it
dashboard <- build_artist_dashboard("bobby-fatemi")
if (is.null(dashboard$error)) {
cat("\n=== ARTIST DASHBOARD ===\n")
cat("Name:", dashboard$artist$name, "\n")
cat("Location:", dashboard$artist$location, "\n")
cat("Portfolio:", dashboard$portfolio$artworks, "artworks\n")
cat("Avg hours/work:", round(dashboard$portfolio$avg_hours_per_work, 1), "\n")
cat("Recent works:", nrow(dashboard$recent_works), "\n")
}Step 5: Add Asset URLs
Path functions construct CDN URLs for images and media:
# These are pure string operations - no database queries
avatar_url <- path_artist_thumb(artist)
cat("Avatar URL:", avatar_url, "\n")
# Get thumbnails for recent works
recent_works <- get_artist_recent_works(artist, limit = 3)
for (i in seq_len(nrow(recent_works))) {
artwork_uuid <- recent_works$art_uuid[i]
title <- recent_works$art_title[i]
thumb_url <- path_artwork_thumb(artist, artwork_uuid)
cat("\n", title, "\n")
cat(" Thumbnail:", thumb_url, "\n")
cat(" Main image:", path_art_main_image(artist, artwork_uuid), "\n")
}Common Pitfalls & Solutions
Pitfall 1: Forgetting to Close Connections
Problem:
cn <- dbc()
result <- get_artist_stats(artist, cn = cn)
# Forgot to close connection!Solution:
cn <- dbc()
on.exit(dbd(cn)) # Ensures cleanup even if error occurs
result <- get_artist_stats(artist, cn = cn)
# Connection closes when function exitsPitfall 2: Not Checking for NULL
Problem:
artist_record <- get_artist_by_slug("nonexistent-artist")
artist <- artist_record$artist_uuid # ERROR: $ operator on NULLSolution:
artist_record <- get_artist_by_slug("nonexistent-artist")
if (is.null(artist_record)) {
cat("Artist not found\n")
# Handle 404 case
} else {
artist <- artist_record$artist_uuid
# Proceed with valid data
}Pitfall 3: Using Deprecated Function Names
After v0.18.0, all functions use snake_case. Old names throw errors:
Problem:
# This will ERROR in v0.18.0+
stats <- getArtistStats(artist)Solution:
# Use snake_case names
stats <- get_artist_stats(artist)
collections <- get_artist_collections_summary(artist)
recent <- get_artist_recent_works(artist, limit = 6)Pitfall 4: Inefficient Connection Management
Problem:
# Opens and closes connection 3 times (150ms overhead)
stats <- get_artist_stats(artist) # Open, query, close
collections <- get_artist_collections_summary(artist) # Open, query, close
recent <- get_artist_recent_works(artist, limit = 6) # Open, query, closeSolution:
# Reuse single connection (50ms overhead)
cn <- dbc()
on.exit(dbd(cn))
stats <- get_artist_stats(artist, cn = cn)
collections <- get_artist_collections_summary(artist, cn = cn)
recent <- get_artist_recent_works(artist, limit = 6, cn = cn)Quick Reference: Essential Functions
Artist Lookup
get_artist_by_slug("artist-slug") # Find by URL slug
get_artist_index(artist_uuid) # Get full profile
get_artist_stats(artist_uuid) # Portfolio statisticsCollections & Artworks
get_artist_collections_summary(artist_uuid) # Collections with counts
get_artist_recent_works(artist_uuid, limit = 10) # Recent artworks
get_artwork_index(artist_uuid, artwork_uuid) # Artwork basics
get_artwork_stats(artist_uuid, artwork_uuid) # Performance metricsCDN Paths
path_artist_thumb(artist_uuid) # Artist avatar
path_artwork_thumb(artist_uuid, artwork_uuid) # Artwork thumbnail
path_art_main_image(artist_uuid, artwork_uuid) # Full-size image
path_replay_frame(artist_uuid, artwork_uuid, 1) # Replay frameAggregated Data (Shiny Apps)
get_appdata(artist_uuid, artwork_uuid) # Everything for artwork detail viewNext Steps
Now that you can query basic data:
Data Access Patterns - More complex workflows (search, visibility filtering, frame analytics)
Data Modification Workflows - Creating collections, adding artworks, updating stats
Advanced Workflow - Multi-artist dashboards, batch operations, error recovery
Reference - Complete function documentation
Troubleshooting
“Connection refused” error
Check your database environment variables:
Sys.getenv("ART_PGHOST")
Sys.getenv("ART_PGPORT")
Sys.getenv("ART_PGUSER")
# Never print ART_PGPASS - it's a secret!“Artist not found” for valid slug
The slug may have changed. Search by name instead:
# Custom query for name search
results <- db_art_get("
SELECT artist_uuid, artist_name, slug
FROM app.artist_index
WHERE artist_name ILIKE '%search term%'
LIMIT 5
")Slow queries
Use shared connections for multiple queries:
For production Shiny apps, consider using the pool package for connection pooling.
