See how we built our intranet. Live + Q&A, June 30

Auto-increment field on document fetch to track article views in Sanity

15 repliesLast updated: Nov 30, 2025

Hello, I’m currently stuck at one point and I’m wondering if there is a way for autoincrement a value when a document gets fetched? For example to get the most read articles of a blog? Without getting into race condition? Thanks for your help
AI Update

Tracking view counts or any auto-incrementing counter when a document is fetched is a tricky problem, especially when you need to avoid race conditions. Let me explain the best approaches for this in Sanity:

The Challenge

Incrementing a counter on every document fetch creates a race condition problem - if multiple users fetch the same document simultaneously, some increments could be lost. This is because read-and-update operations aren't atomic.

The modern approach is to use Sanity Functions to track views server-side. Instead of incrementing on fetch (which happens client-side), you can:

Here's how the increment operation works with the Sanity client:

client
  .patch('document-id')
  .inc({views: 1}) // Atomically increment by 1
  .commit()

The .inc() operation is specifically designed to handle race conditions - it performs atomic increments that won't lose counts even with concurrent updates.

Example with Sanity Functions:

// In your Sanity Function (sanity.blueprint.ts)
export default defineBlueprint({
  functions: [
    defineFunction({
      name: 'track-view',
      handler: async (event) => {
        const {articleId} = event.body
        
        await client
          .patch(articleId)
          .inc({viewCount: 1})
          .commit()
          
        return {success: true}
      }
    })
  ]
})

// In your frontend
async function trackView(articleId) {
  await fetch('https://your-project.api.sanity.io/v2025-01-01/functions/track-view', {
    method: 'POST',
    body: JSON.stringify({ articleId })
  })
}

2. Aggregate Analytics Separately

For production use cases, consider tracking views in a separate analytics system (like Google Analytics, Plausible, or a custom service) rather than in your content documents. This approach:

3. Batch Updates with Scheduled Functions

If you need the data in Sanity, you could:

Important Considerations

Safe Implementation Pattern

// When a user views an article
async function trackView(articleId) {
  // Call your Sanity Function (or own API endpoint)
  await fetch('/api/track-view', {
    method: 'POST',
    body: JSON.stringify({ articleId })
  })
}

// In your API endpoint or Sanity Function
async function incrementViewCount(articleId) {
  await client
    .patch(articleId)
    .inc({viewCount: 1}) // Atomic increment - race condition safe!
    .commit()
}

// Fetch content separately - no mutations here
async function getArticle(articleId) {
  return await client.fetch(
    `*[_type == "post" && _id == $id][0]`,
    {id: articleId}
  )
}

The key takeaway: use the atomic .inc() operation for incrementing, and separate your view tracking from content fetching. This ensures you won't lose counts even with concurrent updates, and you maintain good performance for your content queries.

Show original thread
15 replies

Was this answer helpful?

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Related contributions