Skip to content

Repository files navigation

effect-rpc-tanstack-devtools

Developer tools for Effect RPC that integrate with TanStack Devtools.

Shots Mockups 1x

Features

  • Real-time RPC request/response monitoring
  • Request timing and duration tracking
  • Payload and response inspection with JSON viewer
  • Mutation/query classification with visual badges
  • Filter requests by method name
  • Copy payloads to clipboard
  • Dark theme UI (CSS-in-JS, no external dependencies)

Installation

npm install effect-rpc-tanstack-devtools
# or
bun add effect-rpc-tanstack-devtools

Peer Dependencies

{
"@effect/rpc": ">=0.70.0",
"@tanstack/devtools-event-client": ">=0.3.0",
"effect": ">=3.0.0",
"react": ">=18.0.0"
}

Quick Start

1. Add the Protocol Layer

Wrap your RPC client's protocol layer with DevtoolsProtocolLayer to capture all RPC traffic:

import{RpcClient,RpcSerialization}from"@effect/rpc"import{BrowserSocket}from"@effect/platform-browser"import{DevtoolsProtocolLayer}from"@hazel/rpc-devtools"import{Layer}from"effect"// Your base protocol layerconstBaseProtocolLive=RpcClient.layerProtocolSocket({retryTransientErrors: true,}).pipe(Layer.provide(BrowserSocket.layerWebSocket("wss://api.example.com/rpc")),Layer.provide(RpcSerialization.layerNdjson))// Add devtools in development onlyexportconstRpcProtocolLive=import.meta.env.DEV
? Layer.provideMerge(DevtoolsProtocolLayer,BaseProtocolLive)
: BaseProtocolLive

2. Add the Devtools Panel

Add the React component to your app, typically integrated with TanStack Devtools:

import{TanStackDevtools}from"@tanstack/react-devtools"import{RpcDevtoolsPanel}from"@hazel/rpc-devtools/components"functionApp(){return(<>{import.meta.env.DEV&&(<TanStackDevtoolsplugins={[{name: "Effect RPC",render: <RpcDevtoolsPanel/>,},]}/>)}{/* Your app */}</>)}

That's it! You'll now see all RPC requests in the devtools panel.

Optional: Mutation/Query Classification

By default, the devtools uses heuristics to classify methods as mutations or queries based on naming patterns (e.g., create, update, delete = mutation; get, list, find = query).

For accurate classification, you have two options:

Option A: Use the RPC Builders (Recommended)

The package provides Rpc.mutation() and Rpc.query() builder functions that automatically annotate your RPCs:

import{Rpc}from"@hazel/rpc-devtools"import{RpcGroup}from"@effect/rpc"import{Schema}from"effect"// Define RPCs with explicit type annotationsconstcreateChannel=Rpc.mutation("channel.create",{payload: {name: Schema.String},success: Channel,error: ChannelError,})constlistChannels=Rpc.query("channel.list",{payload: {organizationId: Schema.String},success: Schema.Array(Channel),})exportconstChannelRpcs=RpcGroup.make("channels").add(createChannel).add(listChannels)

Then configure the resolver with your RPC groups:

import{createRpcTypeResolver,setRpcTypeResolver}from"@hazel/rpc-devtools"// Call this once at app initializationif(import.meta.env.DEV){setRpcTypeResolver(createRpcTypeResolver([ChannelRpcs,UserRpcs,/* ... */]))}

Option B: Use Standard Effect RPC

You can continue using standard Effect RPC definitions. The devtools will use heuristic classification:

import{Rpc,RpcGroup}from"@effect/rpc"import{Schema}from"effect"// Standard Effect RPC - works fine, uses heuristic classificationconstcreateChannel=Rpc.make("channel.create",{payload: {name: Schema.String},success: Channel,})exportconstChannelRpcs=RpcGroup.make("channels").add(createChannel)

Heuristic patterns detected:

  • Mutations:create, update, delete, add, remove, set, mark, regenerate
  • Queries:list, get, me, search, find

Option C: Custom Resolver

Provide your own classification logic:

import{setRpcTypeResolver}from"@hazel/rpc-devtools"setRpcTypeResolver((method)=>{if(method.startsWith("admin."))return"mutation"if(method.endsWith(".fetch"))return"query"returnundefined// Fall back to heuristics})

API Reference

Main Exports (@hazel/rpc-devtools)

// Protocol layer for capturing RPC trafficexport{DevtoolsProtocolLayer,clearRequestTracking}from"./protocol-interceptor"// React hooks for accessing captured dataexport{useRpcRequests,useRpcStats,clearRequests}from"./store"// Type resolutionexport{createRpcTypeResolver,setRpcTypeResolver,heuristicResolver,getRpcType}from"./rpc-type-resolver"// Optional RPC builders with type annotationsexport{Rpc,RpcType}from"./builders"// Event client for advanced usageexport{rpcEventClient}from"./event-client"// Typesexporttype{CapturedRequest,RpcRequestEvent,RpcResponseEvent,RpcDevtoolsEventMap}from"./types"

Component Exports (@hazel/rpc-devtools/components)

// Main devtools panelexport{RpcDevtoolsPanel}from"./RpcDevtoolsPanel"// Individual components for custom UIsexport{RequestList}from"./RequestList"export{RequestDetail}from"./RequestDetail"// Styles for custom themingexport{styles,injectKeyframes}from"./styles"

Using the Hooks Directly

Build custom UIs using the provided hooks:

import{useRpcRequests,useRpcStats,clearRequests}from"@hazel/rpc-devtools"functionMyCustomDevtools(){constrequests=useRpcRequests()conststats=useRpcStats()return(<div><p>Total: {stats.total}, Pending: {stats.pending}, Avg: {stats.avgDuration}ms</p><buttononClick={clearRequests}>Clear</button><ul>{requests.map((req)=>(<likey={req.captureId}>{req.method} - {req.response?.status??"pending"}</li>))}</ul></div>)}

License

MIT

About

No description, website, or topics provided.

Resources

Stars

66 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages