Chain a DataSource refetch

Use APICall's onSuccess event to trigger a DataSource refetch after a mutation completes.

When a user action changes server data — liking a post, adding a comment — the displayed list needs to reflect the change. Put dataSource.refetch() in the mutation's onSuccess handler so the refresh happens only after the write succeeds — without relying on blanket cache invalidation.

Click a Like button to watch the chain unfold: the Posting badge appears while the APICall is in flight, then Refetching appears while the DataSource re-reads the timeline, then the new count appears.

<Component name="SocialButton">
  <Button
    borderRadius="50%"
    icon="{$props.icon}"
    variant="outlined"
    themeColor="{$props.themeColor || 'secondary'}"
    size="xs"
    onClick="emitEvent('click')" />
</Component>
<App
  var.statusColors="{{
    Posting: { background: '#f59e0b', label: 'white' },
    Refetching: { background: '#3b82f6', label: 'white' },
    Idle: { background: '#9ca3af', label: 'white' }
  }}">
  <APICall
    id="favoritePost"
    method="post"
    url="/api/posts/{$param}/favorite"
    invalidates="{[]}"
    onSuccess="timelineData.refetch()" />
  <APICall
    id="unfavoritePost"
    method="post"
    url="/api/posts/{$param}/unfavorite"
    invalidates="{[]}"
    onSuccess="timelineData.refetch()" />
  <DataSource
    id="timelineData"
    url="/api/timeline"
    method="GET" />
  <script>
    function toggleFavorite(post) {
      if (post.favourited) {
        unfavoritePost.execute(post.id);
      } else {
        favoritePost.execute(post.id);
      }
    }
  </script>
  <VStack>
    <HStack verticalAlignment="center" gap="$space-2">
      <H3>Social Media Timeline</H3>
      <Badge value="Posting" colorMap="{statusColors}"
        when="{favoritePost.inProgress || unfavoritePost.inProgress}" />
      <Badge value="Refetching" colorMap="{statusColors}"
        when="{timelineData.isRefetching}" />
      <Badge value="Idle" colorMap="{statusColors}"
        when="{!favoritePost.inProgress && !unfavoritePost.inProgress && !timelineData.isRefetching}" />
    </HStack>
    <Items data="{timelineData}">
      <Card>
        <VStack>
          <H4>{$item.author}</H4>
          <Text>{$item.content}</Text>
          <HStack verticalAlignment="center">
            <HStack verticalAlignment="center">
              <SocialButton icon="reply" />
              <Text>{$item.replies_count}</Text>
            </HStack>
            <HStack verticalAlignment="center">
              <SocialButton icon="trending-up" />
              <Text>{$item.reblogs_count}</Text>
            </HStack>
            <HStack verticalAlignment="center">
              <SocialButton
                icon='like'
                onClick="toggleFavorite($item)"
                themeColor="{$item.favourited ? 'attention' : 'secondary'}" />
              <Text variant="caption">{$item.favourites_count}</Text>
            </HStack>
          </HStack>
        </VStack>
      </Card>
    </Items>
  </VStack>
</App>
Click the Like button
<Component name="SocialButton">
  <Button
    borderRadius="50%"
    icon="{$props.icon}"
    variant="outlined"
    themeColor="{$props.themeColor || 'secondary'}"
    size="xs"
    onClick="emitEvent('click')" />
</Component>
<App
  var.statusColors="{{
    Posting: { background: '#f59e0b', label: 'white' },
    Refetching: { background: '#3b82f6', label: 'white' },
    Idle: { background: '#9ca3af', label: 'white' }
  }}">
  <APICall
    id="favoritePost"
    method="post"
    url="/api/posts/{$param}/favorite"
    invalidates="{[]}"
    onSuccess="timelineData.refetch()" />
  <APICall
    id="unfavoritePost"
    method="post"
    url="/api/posts/{$param}/unfavorite"
    invalidates="{[]}"
    onSuccess="timelineData.refetch()" />
  <DataSource
    id="timelineData"
    url="/api/timeline"
    method="GET" />
  <script>
    function toggleFavorite(post) {
      if (post.favourited) {
        unfavoritePost.execute(post.id);
      } else {
        favoritePost.execute(post.id);
      }
    }
  </script>
  <VStack>
    <HStack verticalAlignment="center" gap="$space-2">
      <H3>Social Media Timeline</H3>
      <Badge value="Posting" colorMap="{statusColors}"
        when="{favoritePost.inProgress || unfavoritePost.inProgress}" />
      <Badge value="Refetching" colorMap="{statusColors}"
        when="{timelineData.isRefetching}" />
      <Badge value="Idle" colorMap="{statusColors}"
        when="{!favoritePost.inProgress && !unfavoritePost.inProgress && !timelineData.isRefetching}" />
    </HStack>
    <Items data="{timelineData}">
      <Card>
        <VStack>
          <H4>{$item.author}</H4>
          <Text>{$item.content}</Text>
          <HStack verticalAlignment="center">
            <HStack verticalAlignment="center">
              <SocialButton icon="reply" />
              <Text>{$item.replies_count}</Text>
            </HStack>
            <HStack verticalAlignment="center">
              <SocialButton icon="trending-up" />
              <Text>{$item.reblogs_count}</Text>
            </HStack>
            <HStack verticalAlignment="center">
              <SocialButton
                icon='like'
                onClick="toggleFavorite($item)"
                themeColor="{$item.favourited ? 'attention' : 'secondary'}" />
              <Text variant="caption">{$item.favourites_count}</Text>
            </HStack>
          </HStack>
        </VStack>
      </Card>
    </Items>
  </VStack>
</App>

Key points

onSuccess runs after the backend responds: put follow-up work there when it must happen after a successful API call, such as refetching a DataSource, showing a toast, or navigating to another page.

refetch() re-issues the DataSource's request: Calling timelineData.refetch() re-sends the original query and updates every element bound to that DataSource when the fresh data arrives.

This pattern gives you surgical control: Unlike blanket cache invalidation (which refreshes every DataSource), ds.refetch() refreshes only the specific DataSource you choose.

Combine with invalidates="{[]}" to prevent double-fetching: By default a successful APICall invalidates all caches. If you manually refetch, set invalidates="{[]}" on the APICall to avoid a redundant second fetch.

Surface chain progress with inProgress and isRefetching: Bind a status indicator to <APICall>.inProgress for the write phase and <DataSource>.isRefetching for the read phase. The example uses three Badges to show Posting, Refetching, and Idle so the chain is visible without instrumenting the script.


See also