liveBook · Manning

Revision identifier

As explained in API Design Patterns

A revision identifier is a value that distinguishes one revision of an API resource from other revisions of that resource.

A revision identifier distinguishes one revision of an API resource from another. A revisable resource adds revisionId and revisionCreateTime fields while retaining its ordinary resource identifier; retrieving the resource by that identifier returns the latest revision. section 28.2

Choose an identifier strategy deliberately:

  • Counting numbers provide chronological order but expose deleted revisions through gaps. section 28.3.1
  • Timestamps tolerate gaps and convey ordering, but concurrent writes can produce collisions. section 28.3.1
  • Random identifiers are opaque, uniquely identify one revision, and avoid exposing deletion history; use one consistent strategy across revisable resources. section 28.3.1 section 28.3.2

Populate revisionId when the resource is first created, regardless of the selected strategy. APIs commonly create revisions implicitly whenever stored data changes, though scheduled or explicitly requested creation is also possible. section 28.3.2

Addressing a specific revision

A Message can be requested with /chatRooms/1/messages/2@1234. The @ separator combines the resource ID and revision ID, allowing the standard get method to interpret the request without changing its interface. A response retains the resource ID and places the selected value in revisionId. section 28.3.3

Treat revision identifiers as inputs to revision-specific operations such as retrieval, restoration, and deletion. Restore an older revision by creating a new revision copied from it rather than moving or rewriting historical entries; expose deletion through a separate method to avoid confusing resource deletion with revision deletion. section 28.3.5 section 28.3.6

Listing 28.2 Example code to generate random resource revision identifiers
const crypto = require('crypto');
const base32Decode = require('base32-decode');
 
function generateRandomId(length: number): string {
  const b32Chars = '012345689ABCDEFGHJKMNPQRSTVWXYTZ';      #1
  let id = '';
  for (let i = 0; i < length; i++) {
    let rnd = crypto.randomInt(0, b32Chars.length);
    id += b32Chars[rnd];
  }
  return id + getChecksumCharacter(id);                     #2
}
 
function getChecksumCharacter(value: string): string {
  const bytes = Buffer.from(
      base32Decode(value, 'Crockford'));                    #3
  const intValue = BigInt(`0x${bytes.toString('hex')}`);    #4
  const checksumValue = Number(intValue % BigInt(37));      #5
 
  const alphabet = '0123456789ABCDEFG' +                    #6
                   'HJKMNPQRSTVWXYZ*~$=U';
  return alphabet[Math.abs(checksumValue)];
}

Inspect a random revision-identifier generator using Crockford Base32 characters and a checksum character. section 28.3.1

How API Design Patterns covers it

How deep it goes
Compares counting, timestamp, and random identifiers, then addresses gaps, collisions, consistency, creation, and destructive operations.
Examples
Uses Message resources, @-separated paths, random Crockford Base32 values, and restoreRevision and deleteRevision API methods.
What it assumes
Assumes familiarity with resource identifiers and standard get, update, and delete methods.
What you can do after
Apply it via resource fields and API methods to retrieve, restore, or delete specific historical revisions safely.
How it connects
Connects resource revisions with resource identifiers, timestamps, standard get methods, restoration, and deletion.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of API Design Patterns. The section links point to the parts of the book it draws on.