Skip to Content
URNr-, q- and f-components

r-, q- and f-components

@evanion/urn 2.1.0 and its patch releases up to 2.1.1, the version on npm. Values on this page were produced by running the source at f9b1d93, a later commit than the 2.1.0 tag, and are not re-executed.

@evanion/urn’s parse splits the r-, q- and f-components off a URN into fields of their own, and stringify, given the parts as an object, writes them back.

The Problem: Identifying Specific Versions or Sections

A simple URN identifies a resource, but it doesn’t identify a specific version or subset of that resource. For example, if you need to link to a specific edition of a board game, or a specific section of its rules, you can’t do that with a bare identifier like urn:game:brass-birmingham.

Adding this information as part of the main ID (the NSS) would break the identity of the resource—you’d be treating the “2018 Edition” as a different game entirely rather than a version of the same game.

Solution: URN Components

RFC 8141 §2.3 defines three components that can be appended to a URN. These allow you to carry metadata and specific pointers without changing the identity of the resource. Each component starts with an introducer:

ComponentIntroducerRFC 8141PurposeExample Use Case
r?+§2.3.1Parameters for the resolution service?+lang=en (Request English listing)
q?=§2.3.2Parameters for the named resource?=edition=2018 (Specific edition)
f#§2.3.3A fragment within the resource#setup (The “Setup” section of the rules)

Extracting Components with parse

parse in @evanion/urn returns each component in its own field and never folds one into the nss. GameURN can read an edition and a section of the rules off a single URN:

.('urn:game:brass-birmingham?=edition=2018#setup'); // -> { urn: 'urn', nid: 'game', nss: 'brass-birmingham', fComponent: 'setup', qComponent: 'edition=2018' }

If a URN carries no component, the corresponding keys are simply absent from the returned object. The URNComponents type describes these three optional fields:

function ({ , }: URNComponents): string { return [, ].().(' / '); } (.('urn:game:azul?=edition=2017#scoring')); // -> 'edition=2017 / scoring' 'qComponent' in .('urn:game:azul'); // -> false
urn
GameURN.parse('urn:game:brass-birmingham?=edition=2018#setup')

{ urn: 'urn', nid: 'game', nss: 'brass-birmingham', fComponent: 'setup', qComponent: 'edition=2018' }

Produced by @evanion/urn 2.1.0 at f9b1d93. Not re-executed. Run it live on the unreleased page.

Try typing a URN above to see how GameURN.parse extracts the components.

Writing Components with stringify

To write components, you must pass the parts to stringify as an object: { nss, rComponent?, qComponent?, fComponent? }.

The library automatically ensures the introducers appear in the correct order (r, then q, then f), regardless of the order of keys in your object. An empty fComponent writes a bare #.

const = { : 'brass-birmingham', : 'setup', : 'edition=2018', }; .(); // -> 'urn:game:brass-birmingham?=edition=2018#setup' const = { : 'brass-birmingham', : 'lang=en', : '', }; .(); // -> 'urn:game:brass-birmingham?+lang=en#' const = 'urn:game:brass-birmingham?=edition=2018#setup'; .(.()) === ; // -> true

How Components are Identified

Because the identifier itself (the NSS) cannot contain ? or #, @evanion/urn uses these characters as delimiters. It parses components in the following order:

  1. f-component: Everything after the first #. This is the final part of the URN.
  2. r- and q-components: In the remaining string, the first ? after the NSS starts the component. If + follows, it’s an r-component; if = follows, it’s a q-component. Any other character following ? throws a ValidationError.
  3. r-component bounds: An r-component continues until it hits ?=, which marks the start of the q-component.

Components and URN Equivalence

Crucially, components are not part of the resource’s identity. RFC 8141 §3.1 excludes all three components from URN equivalence.

@evanion/urn’s equals and extractId both ignore components. This means a URN with metadata is considered equivalent to the bare URN it describes.

const = 'urn:game:brass-birmingham?=edition=2018'; const = 'urn:game:brass-birmingham#setup'; .(, ); // -> true .('urn:game:brass-birmingham?=edition=2018#setup'); // -> 'brass-birmingham'

sameNamespace and belongsToNamespace also ignore components, as they only care about the scheme and NID.

The interactive example this release showed here runs against the current source, so it is on the same page under /next/ and not on this one.

Next steps: Encoding and Comparison

Now that you can handle metadata, you need to know how to deal with “messy” identifiers—like those containing spaces or colons. Encoding and comparing URNs shows you how to use encodeNss to safely integrate titles into your URNs.

Last updated on