Skip to content

Instantly share code, notes, and snippets.

@Kuniwak
Created July 30, 2025 05:44
Show Gist options
  • Select an option

  • Save Kuniwak/d7b60901faf1729b1eb4f2b891e25c32 to your computer and use it in GitHub Desktop.

Select an option

Save Kuniwak/d7b60901faf1729b1eb4f2b891e25c32 to your computer and use it in GitHub Desktop.
Miro API Client Example
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strconv"
)
type ItemID string
type Item struct {
ID ItemID `json:"id"`
Name string `json:"name"`
Data map[string]interface{} `json:"data"`
}
type BoardID string
type Board struct {
ID BoardID `json:"id"`
Name string `json:"name"`
}
type Client struct {
accessToken string
httpClient *http.Client
}
func (c *Client) GetBoards() ([]Board, error) {
req, err := http.NewRequest("GET", "https://api.miro.com/v2/boards", nil)
if err != nil {
return nil, err
}
return DoJSONWithOffsetPagination[Board](c, 50, req)
}
func (c *Client) GetItems(boardID BoardID) ([]Item, error) {
req, err := http.NewRequest("GET", "https://api.miro.com/v2/boards/"+string(boardID)+"/items", nil)
if err != nil {
return nil, err
}
return DoJSONWithCursorPagination[Item](c, 50, req)
}
func (c *Client) GetStickyNotes(boardID BoardID) ([]Item, error) {
req, err := http.NewRequest("GET", "https://api.miro.com/v2/boards/"+string(boardID)+"/items", nil)
if err != nil {
return nil, err
}
q := req.URL.Query()
q.Add("type", "sticky_note")
req.URL.RawQuery = q.Encode()
return DoJSONWithCursorPagination[Item](c, 50, req)
}
func DoJSON[T any](c *Client, req *http.Request) (int, T, error) {
var v T
req.Header.Set("Authorization", "Bearer "+c.accessToken)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return resp.StatusCode, v, err
}
defer resp.Body.Close()
err = json.NewDecoder(resp.Body).Decode(&v)
if err != nil {
return resp.StatusCode, v, err
}
return resp.StatusCode, v, nil
}
type CursorPaginatedResponse[T any] struct {
Data []T `json:"data"`
Cursor string `json:"cursor,omitempty"`
}
func DoJSONWithCursorPagination[T any](c *Client, itemsPerPage int, req *http.Request) ([]T, error) {
var items []T
var cursor *string
limit := itemsPerPage
for {
var req2 = *req
q := req2.URL.Query()
q.Set("limit", strconv.Itoa(limit))
if cursor != nil {
q.Set("cursor", *cursor)
}
req2.URL.RawQuery = q.Encode()
statusCode, items2, err := DoJSON[CursorPaginatedResponse[T]](c, &req2)
if err != nil {
return nil, err
}
items = append(items, items2.Data...)
if statusCode != http.StatusOK {
return nil, fmt.Errorf("status code: %d", statusCode)
}
if items2.Cursor == "" {
return items, nil
}
cursor = &items2.Cursor
}
}
type OffsetPaginatedResponse[T any] struct {
Data []T `json:"data"`
}
func DoJSONWithOffsetPagination[T any](c *Client, itemsPerPage int, req *http.Request) ([]T, error) {
var items []T
limit := itemsPerPage
for {
var req2 = *req
q := req2.URL.Query()
q.Set("limit", strconv.Itoa(limit))
q.Set("offset", strconv.Itoa(len(items)))
req2.URL.RawQuery = q.Encode()
statusCode, items2, err := DoJSON[OffsetPaginatedResponse[T]](c, &req2)
if err != nil {
return nil, err
}
items = append(items, items2.Data...)
if statusCode != http.StatusOK {
return nil, fmt.Errorf("status code: %d", statusCode)
}
if len(items2.Data) < limit {
return items, nil
}
}
}
func main() {
accessToken := os.Getenv("MIRO_ACCESS_TOKEN")
client := &Client{
accessToken: accessToken,
httpClient: http.DefaultClient,
}
items, err := client.GetBoards()
if err != nil {
fmt.Println(err.Error())
}
for _, item := range items {
fmt.Printf("%s: %+v\n", item.ID, items)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment