Document Status: Draft
Full TDD: https://gist.github.com/tyler-dane/5d18ecdab29edeabf526ee55cd584b32
Context: For an article on https://newsletter.fullstack.zip
The Calendar API implements synchronization using cursor pagination (nextPageToken) with incremental sync tokens (nextSyncToken). Clients perform an initial full sync, then efficient incremental sync calls returning only changes since the last sync.
- Full sync (no
syncToken): Returns all resources, paginated withnextPageToken, final page includesnextSyncToken - Incremental sync (with
syncToken): Returns only changed entries (including deletions) since token was issued - Token expiration: Server returns HTTP 410 GONE → client must clear state and full sync again
- Tokens are opaque strings—never parse or assume structure
nextPageTokenpresent → more pages exist,nextSyncTokenomittednextSyncTokenonly appears on the final page (whennextPageTokenis absent)- Client must exhaust all pages before storing
nextSyncToken
function syncSettings() {
token = loadSyncToken("settings")
pageToken = null
do {
params = {}
if (token) params["syncToken"] = token
if (pageToken) params["pageToken"] = pageToken
response = GET /users/me/settings with params
if (response.status == 410) {
clearSyncToken("settings")
clearLocalSettings()
return syncSettings() // restart full sync
}
for (setting of response.items) upsertLocalSetting(setting)
pageToken = response.nextPageToken
if (!pageToken) token = response.nextSyncToken
} while (pageToken)
if (token) saveSyncToken("settings", token)
}[Start] → NoToken → FullSync → HasToken ⟷ IncrementalSync
↓
TokenExpired/Invalidated → FullSync (HTTP 410)
Register via POST /calendars/{calendarId}/events/watch with callback URL. Notifications contain resourceId only—client must call API with syncToken to fetch actual changes.
Supports up to 50 requests per batch using multipart/mixed:
POST /batch/calendar/v3
Content-Type: multipart/mixed; boundary=batch_boundaryUse If-Match header with ETag for optimistic locking:
- Match → Update succeeds
- No match → 412 Precondition Failed → Fetch latest, merge, retry