# depager Trades REST API paging logic and request throttling for a channel and a `for` loop. For the moment, *depager* only supports JSON responses. *depager* requires structs conforming to the following interfaces: ```go // The `Page` interface must wrap server responses. This // allows pagers to calculate page sizes and iterate over // response aggregates. // // The underlying implementation must be a pointer to a // struct containing the desired response fields, all tagged // appropriately. Any fields corresponding to // platform-specific error responses should also be // included. // // `Count()` must return the total number of items to be // paged. `Elems()` must return the items from the current // page. type Page[T any] interface { Count() uint64 Elems() []T } type Client interface { Get(uri url.URL) ([]byte, error) } type PagedURI interface { PageURI(limit, offset uint64) url.URL } ``` And in return, *depager* provides the following: ```go type Pager[T any] interface { Iter() <-chan T LastErr() error } ``` ## Example ```go import ( dp "idio.link/go/depager" ) type pagedURI struct { uri *url.URL } func (u pagedURI) PageURI(limit, offset uint64) url.URL { if limit > MaxPageSize { limit = MaxPageSize } uri := *u.uri q := (&uri).Query() q.Add("offset", strconv.FormatUint(offset, 10)) q.Add("size", strconv.FormatUint(limit, 10)) (&uri).RawQuery = q.Encode() return uri } func NewMyAggregate[T any]() *MyAggregate[T] { return &MyAggregate[T]{Items: make([]T, 0, 64)} } type MyAggregate[T any] struct { Total int32 `json:"total"` Items []T `json:"items"` } func (a *MyAggregate[_]) Count() uint64 { return uint64(a.Total) } func (a *MyAggregate[T]) Elems() []T { return a.Items } // The client can use this func for all its paged responses. func makeMyPager[T any]( client *MyClient, resource *url.URL, ) dp.Pager[T] { return dp.NewPager( pagedURI{resource}, client, MaxPageSize, // most days, this should be the maximum page size that the server will provide func() dp.Page[T] { return NewMyAggregate[T]() }, ) } // . // . // . type Thing struct { Id int32 `json:"id"` Name string `json:"name"` } func main() { id := 82348 var thingPager dp.Pager[*Thing] = client.GetThings(id) for thing := range thingPager.Iter() { if ctx.Err() != nil { // stay responsive! break } // do stuff with each thing } // finally, check for errors if err := thingPager.LastErr(); err != nil { log.Printf("do stuff with things: %v", err) return err } } ``` ## Why? In a world of plentiful memory, application-level paging, when implemented over a buffered, flow-controlled protocol like TCP, becomes a needless redundancy, at best; at worst, it is a mindless abnegation of system-level affordances. But since we twisted the web into a transport layer, we no longer have the option of leveraging underlying flow control, and workarounds like QUIC only double down on this bizarre state of affairs. Since I have no expectation that the scourge of the web, as transport, will disappear anytime soon, I may as well recapitulate the same basic flow control that TCP would provide us, if only we let it.