Documentation Index Fetch the complete documentation index at: https://mintlify.com/moqtail/moqtail/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The fetch() method enables you to retrieve bounded ranges of historical objects from publishers. Unlike subscribe operations that stream live content, fetch operations provide reliable, ordered delivery of static content.
Fetch operations are optimized for reliable delivery of historical data. For live streaming content, use the subscribe method instead.
Fetch Characteristics
Fetch operations have distinct characteristics compared to subscriptions:
Single Stream All requested objects delivered sequentially in one stream
Reliable Delivery Uses QUIC streams for guaranteed, ordered delivery
No Cancellation All requested objects are delivered (historical data)
Bounded Range Fetch a specific range with defined start and end
Fetch Types
MOQtail supports three types of fetch operations:
StandAlone Fetch
Retrieve a historical slice of a track independent of any subscriptions:
import { FetchType , GroupOrder , Location } from 'moqtail' ;
const result = await client . fetch ({
priority: 64 ,
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . StandAlone ,
props: {
fullTrackName: FullTrackName . tryNew ( 'archive/recordings' , 'video' ),
startLocation: new Location ( 0 n , 0 n ), // Group 0, Object 0
endLocation: new Location ( 100 n , 0 n ) // Through Group 100
}
}
});
This is the most common fetch type for accessing historical content like archived recordings or cached data.
Relative Fetch
Fetch objects relative to an existing subscription’s current position:
// First, create a subscription
const subResult = await client . subscribe ({
fullTrackName ,
filterType: FilterType . LatestObject ,
forward: true ,
groupOrder: GroupOrder . Original ,
priority: 0
});
if ( ! ( subResult instanceof SubscribeError )) {
// Fetch the last 5 groups relative to the subscription
const fetchResult = await client . fetch ({
priority: 32 ,
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . Relative ,
props: {
joiningRequestId: subResult . requestId ,
joiningStart: 5 n // Fetch 5 groups back from current
}
}
});
}
Relative fetch requires an active subscription. The joiningRequestId must reference a valid SUBSCRIBE request.
Absolute Fetch
Fetch using absolute group/object offsets tied to an existing subscription:
const fetchResult = await client . fetch ({
priority: 32 ,
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . Absolute ,
props: {
joiningRequestId: subResult . requestId ,
joiningStart: 0 n // Absolute offset from subscription anchor
}
}
});
Basic Fetch Workflow
Configure fetch parameters
Define what content you want to retrieve: import { FetchType , GroupOrder , Location , FetchError } from 'moqtail' ;
const result = await client . fetch ({
priority: 64 , // 0 (highest) to 255 (lowest)
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . StandAlone ,
props: {
fullTrackName: FullTrackName . tryNew ( 'recordings' , 'session-1' ),
startLocation: new Location ( 0 n , 0 n ),
endLocation: new Location ( 50 n , 0 n )
}
}
});
Handle fetch response
Check if the fetch was successful: if ( result instanceof FetchError ) {
console . error ( `Fetch failed: ${ result . reasonPhrase . phrase } ` );
switch ( result . errorCode ) {
case FetchErrorCode . InvalidRange :
console . error ( 'The requested range is invalid' );
break ;
case FetchErrorCode . Timeout :
console . error ( 'Fetch request timed out' );
break ;
default :
console . error ( `Error code: ${ result . errorCode } ` );
}
return ;
}
const { requestId , stream } = result ;
console . log ( `Fetch started with ID: ${ requestId } ` );
Process fetched objects
Consume objects from the stream: const reader = stream . getReader ();
try {
while ( true ) {
const { done , value : object } = await reader . read ();
if ( done ) {
console . log ( 'Fetch complete' );
break ;
}
// Process the fetched object
if ( object . objectStatus === ObjectStatus . Normal && object . payload ) {
await processArchivedData ( object );
}
}
} finally {
reader . releaseLock ();
}
Priority Management
Priority determines how fetch requests compete for bandwidth:
// High priority fetch for important historical data
const criticalFetch = await client . fetch ({
priority: 0 , // Highest priority
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . StandAlone ,
props: { fullTrackName , startLocation , endLocation }
}
});
// Lower priority background fetch
const backgroundFetch = await client . fetch ({
priority: 200 , // Lower priority
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . StandAlone ,
props: { fullTrackName , startLocation , endLocation }
}
});
Priority must be in the range [0-255], where 0 is the highest priority and 255 is the lowest.
Group Ordering
Control the order in which groups are delivered:
Original Order
Deliver groups in the same order they were published:
import { GroupOrder } from 'moqtail' ;
const result = await client . fetch ({
priority: 64 ,
groupOrder: GroupOrder . Original , // Preserve publisher order
typeAndProps: { /* ... */ }
});
Ascending Order
Deliver groups in ascending order by group ID:
const result = await client . fetch ({
priority: 64 ,
groupOrder: GroupOrder . Ascending , // Smallest group ID first
typeAndProps: { /* ... */ }
});
Descending Order
Deliver groups in descending order (most recent first):
const result = await client . fetch ({
priority: 64 ,
groupOrder: GroupOrder . Descending , // Largest group ID first
typeAndProps: { /* ... */ }
});
Complete Example
Here’s a complete example fetching archived video content:
import {
MOQtailClient ,
FetchType ,
GroupOrder ,
Location ,
FetchError ,
ObjectStatus
} from 'moqtail' ;
async function fetchArchivedRecording () {
// Initialize client
const client = await MOQtailClient . new ({
url: 'https://relay.example.com/transport' ,
supportedVersions: [ 0xff00000b ]
});
// Define the range to fetch
const startLocation = new Location ( 0 n , 0 n );
const endLocation = new Location ( 100 n , 0 n );
// Fetch historical content
const result = await client . fetch ({
priority: 64 ,
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . StandAlone ,
props: {
fullTrackName: FullTrackName . tryNew ( 'archive/2024' , 'recording-1' ),
startLocation ,
endLocation
}
}
});
// Handle errors
if ( result instanceof FetchError ) {
console . error ( `Fetch failed: ${ result . reasonPhrase . phrase } ` );
return ;
}
const { requestId , stream } = result ;
console . log ( `Fetching with ID: ${ requestId } ` );
// Process the stream
const reader = stream . getReader ();
const objects : MoqtObject [] = [];
try {
while ( true ) {
const { done , value : object } = await reader . read ();
if ( done ) break ;
if ( object . objectStatus === ObjectStatus . Normal && object . payload ) {
objects . push ( object );
console . log (
`Fetched object ${ object . objectId } from group ${ object . groupId } ` +
`( ${ object . payload . byteLength } bytes)`
);
}
}
console . log ( `Fetch complete: ${ objects . length } objects retrieved` );
return objects ;
} finally {
reader . releaseLock ();
}
}
Combining Fetch and Subscribe
A common pattern is fetching historical context while subscribing to live content:
async function subscribeWithHistory () {
const client = await MOQtailClient . new ({ /* ... */ });
// Subscribe to live content
const subResult = await client . subscribe ({
fullTrackName ,
filterType: FilterType . LatestObject ,
forward: true ,
groupOrder: GroupOrder . Original ,
priority: 0
});
if ( subResult instanceof SubscribeError ) {
console . error ( 'Subscription failed' );
return ;
}
// Fetch recent history (last 10 groups) relative to subscription
const fetchResult = await client . fetch ({
priority: 32 ,
groupOrder: GroupOrder . Original ,
typeAndProps: {
type: FetchType . Relative ,
props: {
joiningRequestId: subResult . requestId ,
joiningStart: 10 n // Last 10 groups
}
}
});
if ( ! ( fetchResult instanceof FetchError )) {
// Process historical objects first
const historyReader = fetchResult . stream . getReader ();
while ( true ) {
const { done , value } = await historyReader . read ();
if ( done ) break ;
processHistoricalObject ( value );
}
historyReader . releaseLock ();
}
// Then process live stream
const liveReader = subResult . stream . getReader ();
try {
while ( true ) {
const { done , value } = await liveReader . read ();
if ( done ) break ;
processLiveObject ( value );
}
} finally {
liveReader . releaseLock ();
}
}
Error Handling
Handle common fetch errors appropriately:
import { FetchErrorCode } from 'moqtail' ;
if ( result instanceof FetchError ) {
switch ( result . errorCode ) {
case FetchErrorCode . InvalidRange :
console . error ( 'Invalid range specified' );
// Adjust range and retry
break ;
case FetchErrorCode . Timeout :
console . error ( 'Request timed out' );
// Retry with exponential backoff
break ;
case FetchErrorCode . InternalError :
console . error ( 'Server error:' , result . reasonPhrase . phrase );
// Log and alert
break ;
default :
console . error ( `Fetch error ${ result . errorCode } : ${ result . reasonPhrase . phrase } ` );
}
}
Next Steps
Subscribing to Live Content Learn about live streaming subscriptions
Playout Buffer Implement smooth media playback