cli icon indicating copy to clipboard operation
cli copied to clipboard

feat(graphiql): add data fetching hooks

Open amcaplan opened this issue 1 month ago β€’ 2 comments

WHY are these changes introduced?

This PR adds custom React hooks for server health monitoring and data fetching. This is the sixth PR in the 8-PR migration stack.

Context: The current template-based GraphiQL implementation uses vanilla JavaScript with setInterval to poll the server for health status and app installation state. By creating reusable React hooks, we can:

  • Integrate polling with React's component lifecycle
  • Automatically clean up intervals when components unmount
  • Use React state management for server status
  • Make polling logic testable and reusable
  • Handle errors gracefully without breaking the UI

WHAT is this pull request doing?

This PR adds two custom hooks that replace vanilla JavaScript polling with React-based data fetching.

Key Changes:

1. usePolling Hook (src/hooks/usePolling.ts): A generic hook for interval-based polling with proper cleanup and error handling.

Features:

  • Calls a callback function at regular intervals
  • Supports async functions (returns Promise)
  • Handles both sync and async errors gracefully (logs but doesn't crash)
  • Uses useRef to avoid stale closure issues
  • Calls callback immediately on mount, then at intervals
  • Automatically cleans up interval on unmount
  • Can be enabled/disabled dynamically

Usage:

usePolling(async () => {
  await checkServerHealth()
}, {
  interval: 2000,       // Poll every 2 seconds
  enabled: isActive     // Optional: control polling
})

2. useServerStatus Hook (src/hooks/useServerStatus.ts): Monitors server health and app installation status by polling two endpoints.

Two Polling Mechanisms:

Ping Polling (/graphiql/ping - every 2 seconds by default):

  • Checks if dev server is running
  • Uses timeout mechanism: marks server dead if no response within 3 seconds
  • Clears pending timeouts on successful response
  • Updates serverIsLive state

Status Polling (/graphiql/status - every 5 seconds by default):

  • Checks if app is installed in store
  • Fetches store information (storeFqdn, appName, appUrl)
  • Updates appIsInstalled state and store info

Return Value:

interface ServerStatus {
  serverIsLive: boolean      // Server is responding to pings
  appIsInstalled: boolean    // App is installed in store
  storeFqdn?: string        // e.g., "my-store.myshopify.com"
  appName?: string          // e.g., "My Test App"
  appUrl?: string           // e.g., "http://localhost:3000"
}

Usage:

const status = useServerStatus({
  baseUrl: 'http://localhost:3457',
  pingInterval: 2000,     // Optional: default 2000ms
  statusInterval: 5000,   // Optional: default 5000ms
  pingTimeout: 3000,      // Optional: default 3000ms
})

// Use status in components
<StatusBadge status={status} />
<ErrorBanner isVisible={!status.serverIsLive} />
<LinkPills status={status} />

Error Handling:

  • Network errors are caught and treated as "server down" or "app not installed"
  • Polling continues even if individual requests fail
  • No errors are thrown to crash the application
  • Uses intentional catch-all blocks (with eslint disable comments)

Replaces:

  • Vanilla JavaScript setInterval polling logic
  • Manual timeout tracking and cleanup
  • Direct DOM manipulation based on server status

Testing:

  • 386 lines of comprehensive tests (176 for usePolling, 210 for useServerStatus)
  • Tests interval execution
  • Tests error handling
  • Tests cleanup on unmount
  • Tests timeout behavior
  • Tests state updates

Files Added:

  • src/hooks/usePolling.ts - Generic polling hook (46 lines)
  • src/hooks/usePolling.test.ts - Tests (176 lines)
  • src/hooks/useServerStatus.ts - Server status hook (102 lines)
  • src/hooks/useServerStatus.test.ts - Tests (210 lines)
  • src/hooks/index.ts - Barrel exports

Dependencies

Builds on:

  • PR #6578 (package foundation)
  • PR #6579 (validation and types)

Used by:

  • PR #6580 (StatusBadge, ErrorBanner, LinkPills need ServerStatus type)
  • Subsequent PRs will use these hooks in the main layout

How to test your changes?

# Run hook tests
pnpm --filter @shopify/graphiql-console test usePolling.test.ts
pnpm --filter @shopify/graphiql-console test useServerStatus.test.ts

# Type check
pnpm --filter @shopify/graphiql-console tsc --noEmit

# All 386 lines of tests should pass, covering:
# - Polling interval execution
# - Immediate first call on mount
# - Interval cleanup on unmount
# - Error handling (sync and async)
# - Enabled/disabled toggling
# - Server ping timeout behavior
# - Status endpoint response parsing
# - Network error handling

Manual Testing: You can test the hooks in the App component:

import {useServerStatus} from './hooks'

const status = useServerStatus({
  baseUrl: 'http://localhost:3457'
})

console.log('Server status:', status)

// Stop the dev server and watch serverIsLive change to false
// Restart it and watch it change back to true

Measuring impact

  • [x] n/a - these are foundational data fetching hooks

Checklist

  • [x] I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • [x] I've considered possible documentation changes

amcaplan avatar Nov 06 '25 14:11 amcaplan

[!WARNING] This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite. Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

amcaplan avatar Nov 06 '25 14:11 amcaplan

Coverage report

St.:grey_question:
Category Percentage Covered / Total
🟑 Statements
79.36% (+0.02% πŸ”Ό)
13737/17309
🟑 Branches
73.27% (-0% πŸ”»)
6721/9173
🟑 Functions
79.38% (-0.03% πŸ”»)
3545/4466
🟑 Lines
79.73% (+0.03% πŸ”Ό)
12969/16267
Show new covered files 🐣
St.:grey_question:
File Statements Branches Functions Lines
🟒
... / usePolling.ts
100% 100% 83.33% 100%
🟒
... / useServerStatus.ts
82.35% 57.14% 66.67% 88.46%

Test suite run success

3437 tests passing in 1399 suites.

Report generated by πŸ§ͺjest coverage report action from eeb9b92fddd558af3c4b125ab14156c27b7af58b

github-actions[bot] avatar Nov 06 '25 14:11 github-actions[bot]