Skip to main content Inicio Creadores jeremylongshore tons-of-skills-marketplace miro-sdk-patterns
miro-sdk-patterns Apply production-ready patterns for @mirohq/miro-api client usage.
Use when implementing Miro integrations, refactoring SDK usage,
or establishing coding standards for Miro REST API v2.
Trigger with phrases like "miro SDK patterns", "miro best practices",
"miro code patterns", "miro client wrapper", "miro typescript".
Ir a la instalación Skills Marketplace Descubre y explora habilidades de IA creadas por la comunidad.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Copiar promptMostrar detalles del prompt Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/jeremylongshore/tons-of-skills-marketplace --skill miro-sdk-patternsEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Descargar Zip Descargando... Más de este repositorio langchain-deploy-integration Deploy a LangChain 1.0 / LangGraph 1.0 app to Cloud Run, Vercel, or LangServe correctly — with timeouts sized for chain length, cold-start mitigation, SSE anti-buffering headers, and Secret Manager over .env. Use when prepping a first production deploy, debugging a stream that hangs behind a proxy, or diagnosing p99 latency spikes. Trigger with "langchain deploy", "langchain cloud run", "langchain vercel python", "langchain langserve", or "langchain docker".
langchain-langgraph-agents Build a correct LangGraph 1.0 ReAct agent with create_react_agent — typed tools, error propagation, recursion caps, and stop conditions that actually stop. Use when writing a first tool-calling agent, migrating from AgentExecutor or initialize_agent, or diagnosing an agent that loops on vague prompts. Trigger with "langgraph agent", "create_react_agent", "langgraph tool calling", "AgentExecutor migration", or "agent loop cost".
langchain-langgraph-human-in-loop Build LangGraph 1.0 human-in-the-loop approval flows with interrupt_before /
interrupt_after and Command(resume=...) — JSON-serializable state, clean
resume semantics, and UI wiring for approval decisions. Use when adding an
approval gate before an expensive tool call, wiring a Slack/web UI for agent
approvals, or debugging a graph that crashes on interrupt.
Trigger with "langgraph human in loop", "langgraph interrupt_before",
"langgraph approval flow", "Command resume", "langgraph HITL".
name miro-sdk-patterns description Apply production-ready patterns for @mirohq/miro-api client usage.
Use when implementing Miro integrations, refactoring SDK usage,
or establishing coding standards for Miro REST API v2.
Trigger with phrases like "miro SDK patterns", "miro best practices",
"miro code patterns", "miro client wrapper", "miro typescript".
allowed-tools Read, Write, Edit version 1.7.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","miro","patterns","typescript"] compatibility Designed for Claude Code
Miro SDK Patterns
Overview
Production-ready patterns for the @mirohq/miro-api Node.js client and direct REST API v2 usage. Covers the high-level Miro client (stateful, OAuth-aware) and the low-level MiroApi client (stateless, token-based).
Prerequisites
@mirohq/miro-api installed
TypeScript 5+ project
Understanding of Miro REST API v2 item model
Two Client Modes
import { Miro , MiroApi } from '@mirohq/miro-api' ;
const miro = new Miro ({
clientId : process.env .MIRO_CLIENT_ID !,
clientSecret : process.env .MIRO_CLIENT_SECRET !,
redirectUrl : process.env .MIRO_REDIRECT_URI !,
});
const userApi = await miro.as ('user-id' );
const api = new MiroApi (process.env .MIRO_ACCESS_TOKEN !);
Pattern 1: Type-Safe Board Service
import { MiroApi } from '@mirohq/miro-api' ;
{
: ;
: ;
: ;
: ;
: ;
: ;
}
{
: ;
: | | | | | | | | ;
: < , >;
: { : ; : ; : };
?: { ?: ; ?: };
: ;
: { : ; : };
}
<T> {
: T[];
: ;
: ;
: ;
: ;
?: ;
}
{
( ) {}
( : ): < > {
response = . . (boardId);
response. ;
}
(
: ,
: { ?: ; ?: ; ?: } = {}
): < < >> {
params = ();
(options. ) params. ( , options. );
(options. ) params. ( , (options. ));
(options. ) params. ( , options. );
response = (
,
{ : . () }
);
response. ();
}
( : ): < []> {
: [] = [];
: | ;
{
page = . (boardId, { : , cursor });
items. (...page. );
cursor = page. ;
} (cursor);
items;
}
( ) {
{
: ,
: ,
};
}
}
interface
MiroBoard
id
string
type
'board'
name
string
description
string
createdAt
string
modifiedAt
string
interface
MiroBoardItem
id
string
type
'sticky_note'
'shape'
'card'
'text'
'frame'
'image'
'document'
'embed'
'app_card'
data
Record
string
unknown
position
x
number
y
number
origin
string
geometry
width
number
height
number
createdAt
string
createdBy
id
string
type
string
interface
PaginatedResponse
data
total
number
size
number
offset
number
limit
number
cursor
string
export
class
BoardService
constructor
private api : MiroApi
async
getBoard
boardId
string
Promise
MiroBoard
const
await
this
api
getBoard
return
body
as
unknown
as
MiroBoard
async
listItems
boardId
string
options
type
string
limit
number
cursor
string
Promise
PaginatedResponse
MiroBoardItem
const
new
URLSearchParams
if
type
set
'type'
type
if
limit
set
'limit'
String
limit
if
cursor
set
'cursor'
cursor
const
await
fetch
`https://api.miro.com/v2/boards/${boardId} /items?${params} `
headers
this
authHeaders
return
json
async
getAllItems
boardId
string
Promise
MiroBoardItem
const
items
MiroBoardItem
let
cursor
string
undefined
do
const
await
this
listItems
limit
50
push
data
cursor
while
return
private
authHeaders
return
'Authorization'
`Bearer ${process.env.MIRO_ACCESS_TOKEN} `
'Content-Type'
'application/json'
Pattern 2: Item Factory
type StickyNoteColor = 'light_yellow' | 'light_green' | 'light_blue'
| 'light_pink' | 'gray' | 'light_cyan' | 'light_orange' ;
interface CreateStickyNoteParams {
boardId : string ;
content : string ;
color ?: StickyNoteColor ;
x ?: number ;
y ?: number ;
width ?: number ;
}
interface CreateShapeParams {
boardId : string ;
content : string ;
shape ?: 'rectangle' | 'circle' | 'triangle' | 'rhombus'
| 'round_rectangle' | 'parallelogram' | 'star'
| 'right_arrow' | 'left_arrow' | 'pentagon' | 'hexagon'
| 'octagon' | 'trapezoid' | 'flow_chart_predefined_process'
| 'can' | 'cross' | 'cloud' ;
fillColor ?: string ;
x ?: number ;
y ?: number ;
width ?: number ;
height ?: number ;
}
interface CreateCardParams {
boardId : string ;
title : string ;
description ?: string ;
dueDate ?: string ;
assigneeId ?: string ;
x ?: number ;
y ?: number ;
}
export class ItemFactory {
constructor (private token : string ) {}
async createStickyNote (params : CreateStickyNoteParams ): Promise <MiroBoardItem > {
return this .post (`/v2/boards/${params.boardId} /sticky_notes` , {
data : { content : params.content , shape : 'square' },
style : { fillColor : params.color ?? 'light_yellow' , textAlign : 'center' },
position : { x : params.x ?? 0 , y : params.y ?? 0 },
geometry : { width : params.width ?? 199 },
});
}
async createShape (params : CreateShapeParams ): Promise <MiroBoardItem > {
return this .post (`/v2/boards/${params.boardId} /shapes` , {
data : { content : params.content , shape : params.shape ?? 'round_rectangle' },
style : { fillColor : params.fillColor ?? '#4262ff' , textAlign : 'center' },
position : { x : params.x ?? 0 , y : params.y ?? 0 },
geometry : { width : params.width ?? 200 , height : params.height ?? 100 },
});
}
async createCard (params : CreateCardParams ): Promise <MiroBoardItem > {
return this .post (`/v2/boards/${params.boardId} /cards` , {
data : {
title : params.title ,
description : params.description ,
dueDate : params.dueDate ,
assigneeId : params.assigneeId ,
},
position : { x : params.x ?? 0 , y : params.y ?? 0 },
});
}
private async post (path : string , body : unknown ): Promise <MiroBoardItem > {
const res = await fetch (`https://api.miro.com${path} ` , {
method : 'POST' ,
headers : {
'Authorization' : `Bearer ${this .token} ` ,
'Content-Type' : 'application/json' ,
},
body : JSON .stringify (body),
});
if (!res.ok ) {
const err = await res.json ();
throw new MiroApiError (res.status , err.message ?? 'API request failed' , err.code );
}
return res.json ();
}
}
Pattern 3: Error Handling Wrapper
export class MiroApiError extends Error {
constructor (
public readonly status : number ,
message : string ,
public readonly code ?: string ,
) {
super (message);
this .name = 'MiroApiError' ;
}
get isRetryable (): boolean {
return this .status === 429 || (this .status >= 500 && this .status < 600 );
}
get isAuthError (): boolean {
return this .status === 401 || this .status === 403 ;
}
}
async function safeMiroCall<T>(
operation : () => Promise <T>,
context : string
): Promise <{ data : T | null ; error : MiroApiError | null }> {
try {
const data = await operation ();
return { data, error : null };
} catch (err) {
if (err instanceof MiroApiError ) {
console .error (`[Miro:${context} ] ${err.status} : ${err.message} ` );
return { data : null , error : err };
}
throw err;
}
}
Pattern 4: Multi-Tenant Client Factory
import { Miro , MiroApi } from '@mirohq/miro-api' ;
const clients = new Map <string , MiroApi >();
export async function getClientForUser (userId : string ): Promise <MiroApi > {
if (!clients.has (userId)) {
const miro = new Miro ({
clientId : process.env .MIRO_CLIENT_ID !,
clientSecret : process.env .MIRO_CLIENT_SECRET !,
redirectUrl : process.env .MIRO_REDIRECT_URI !,
});
if (!await miro.isAuthorized (userId)) {
throw new Error (`User ${userId} has not authorized Miro` );
}
const api = await miro.as (userId);
clients.set (userId, api);
}
return clients.get (userId)!;
}
Pattern 5: Response Validation with Zod import { z } from 'zod' ;
const MiroBoardSchema = z.object ({
id : z.string (),
type : z.literal ('board' ),
name : z.string (),
description : z.string ().optional (),
createdAt : z.string ().datetime (),
modifiedAt : z.string ().datetime (),
});
const MiroItemSchema = z.object ({
id : z.string (),
type : z.enum (['sticky_note' , 'shape' , 'card' , 'text' , 'frame' , 'image' , 'document' , 'embed' , 'app_card' ]),
data : z.record (z.unknown ()),
position : z.object ({ x : z.number (), y : z.number () }),
});
function validateBoardResponse (data : unknown ) {
return MiroBoardSchema .parse (data);
}
Instructions Use the ordered procedures and code samples in this guide as a sequence: begin with the prerequisites, apply the configuration or operational step for the target environment, then perform the documented validation or cleanup before proceeding. Keep credentials in the documented secret store; never hard-code them in source.
Output Following this guide produces the Miro integration outcome for its topic—configuration, validation evidence, operational recovery, or a documented migration result. Record command output and relevant identifiers so a failed step is traceable.
Examples Start with the smallest applicable command or code example in the relevant section, using a dedicated test board and non-production credentials. Confirm the expected response or validation result before applying the pattern to production.
Error Handling Pattern Use Case Benefit Type-safe service Board/item CRUD Catches shape mismatches at compile time Item factory Bulk item creation Consistent defaults, validated params Error wrapper All API calls Classifies errors as retryable vs auth vs input Multi-tenant SaaS applications Isolates users, manages token lifecycle Zod validation Response parsing Runtime safety against API changes
Resources
Next Steps Apply these patterns in miro-core-workflow-a for board management operations.