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/limitloops — the iterator works on the response envelope and follows its links instead
Required values
| Value | Description |
|---|---|
| Table name | The table to read (for example, incident) |
| A first page | Any 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)
}
Navigate pages manually
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
falsefrom the callback to stop early — already-fetched pages aren't refetched on a laterIterate. - Combine with a query so you page through only the records you actually need.
Next steps
- List and query records: The requests that produce paginated results.
- Query builder: Shrink the result set before you page through it.