Skip to main content
Version: main

Iterate over every record

Walk a result set that spans multiple pages, letting the SDK follow the pagination links for you.

When to use this pattern

  • A query can match more records than one response returns, and you need all of them
  • You're streaming records through a process one at a time rather than loading the whole set into memory
  • You'd otherwise be hand-assembling offset/limit loops — the iterator works on the response envelope and follows its links instead

Required values

ValueDescription
Table nameThe table to read (for example, incident)
A first pageAny list response — its links seed the iterator

Example

package main

import (
"context"
"fmt"
"log"

servicenow "github.com/michaeldcanady/servicenow-sdk-go/v2"
"github.com/michaeldcanady/servicenow-sdk-go/v2/core"
"github.com/michaeldcanady/servicenow-sdk-go/v2/credentials"
tableapi "github.com/michaeldcanady/servicenow-sdk-go/v2/tableapi"
)

func main() {
// Step 1: Authenticate and initialize the client
cred := credentials.NewBasicProvider("{username}", "{password}")

client, err := servicenow.NewServiceNowServiceClient(
servicenow.WithAuthenticationProvider(cred),
servicenow.WithInstance("{instance}"),
)
if err != nil {
log.Fatalf("failed to initialize client: %v", err)
}
ctx := context.Background()

// Step 2: Execute the first list request
response, err := client.Now().Table("{TableName}").Get(ctx, nil)
if err != nil {
log.Fatalf("unable to list records: %v", err)
}

// Step 3: Create an iterator from the response
iterator, err := core.NewPageIterator(response, client.GetRequestAdapter(), tableapi.CreateTableRecordFromDiscriminatorValue)
if err != nil {
log.Fatal(err)
}

// Step 4: Iterate every record on every page — the iterator follows
// the response's pagination links for you
total := 0
err = iterator.Iterate(ctx, false, func(record *tableapi.TableRecord) bool {
total++
return true // false stops the iteration early
})
if err != nil {
log.Fatal(err)
}

fmt.Printf("Visited %d records\n", total)
}

Variations

Iterate item-by-item

Fetch pages lazily while presenting a record-at-a-time interface:

// Iterate item by item using NextItem
for iterator.HasNext() {
item, err := iterator.NextItem(ctx)
if err != nil {
break
}
fmt.Println(item)
}

Iterate attachments

The Attachment API has its own iterator with the same shape:

// 1. Execute an attachment list request
attachmentResponse, err := client.Now().Attachment().Get(ctx, nil)
if err != nil {
log.Fatal(err)
}

// 2. Create the iterator
attachmentIterator, err := core.NewPageIterator(attachmentResponse, client.GetRequestAdapter(), attachmentapi.CreateAttachmentFromDiscriminatorValue)
if err != nil {
log.Fatal(err)
}

// 3. Iterate over attachments
if err := attachmentIterator.Iterate(ctx, false, func(attachment *attachmentapi.Attachment) bool {
fileName, _ := attachment.GetFileName()
fmt.Printf("Attachment: %s\n", *fileName)
return true
}); err != nil {
log.Fatal(err)
}

Iterate in reverse

Pass true to walk the result set backwards:

err = iterator.Iterate(ctx, true, func(record *tableapi.TableRecord) bool {
// Process records in reverse order
return true
})
if err != nil {
log.Fatal(err)
}

Fetch the next page yourself when you need page-level control:

// Fetch the next page of results manually
nextPage, err := iterator.Next(ctx)
if err != nil {
log.Fatal(err)
}

// Process the items on the next page
results := nextPage.Result
for _, item := range results {
fmt.Println(item)
}

Manage iteration state

// Reset the iterator to the beginning
iterator.Reset()

// Restart iteration of the current page
iterator.ResetPage()
  • Reset(): Returns the iterator to the first page and first item.
  • ResetPage(): Restarts iteration of the current page.

Tips

  • Return false from the callback to stop early — already-fetched pages aren't refetched on a later Iterate.
  • Combine with a query so you page through only the records you actually need.

Next steps

Was this page helpful?