You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/content/1.guide/1.tutorial-server-data-inspector.md
+59-52Lines changed: 59 additions & 52 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,30 +1,32 @@
1
1
---
2
-
title: 'Tutorial: Build a Data Inspector'
3
-
description: 'Build a small devtool from an empty folder — a live view into your server''s state — then grow it one capability at a time: a dock in a hub, a static build, a standalone server, and a CLI.'
2
+
title: 'Tutorial: Build a Server Data Inspector'
3
+
description: 'Build a Devframe to display and query data from the server side, with a dock in a hub, a static build, a standalone server, and a CLI.'
4
4
---
5
5
6
-
Let's build a real devtool from nothing: a **Data Inspector** that shows the shape of your server's live state and lets you read any value out of it. We'll get it working first, then teach it new tricks one at a time — a dock in a hub, a static build, a standalone server, a CLI.
7
-
8
-
One new idea per step, nothing assumed. You'll need **Node 24+** (so `node` runs TypeScript directly) and a terminal.
6
+
Let's build a real devtool from sketch: a **Data Inspector** that shows the shape of your server's live state and lets you read any value out of it. We'll get it working first, then teach it new tricks one at a time, a dock in a hub, a static build, a standalone server, a CLI.
9
7
10
8
## The shape of a devframe app
11
9
12
-
Two halves talk over a typed connection: a **server** half in your Node process that exposes functions, and a **browser** half that calls them and shows the answers. Devframe is everything in between — the wire, the UI hosting, auth, builds, a CLI. Write the two halves once; run them anywhere.
10
+
Two halves talk over a typed connection: a **server** in your Node process that exposes server functions, and a **browser** client that gets the data and renders them nicely and provides interactivity. Devframe is everything in between, the wire, the UI hosting, auth, builds, a CLI.
11
+
12
+
## Step 1, Define the tool
13
+
14
+
Everything starts with `defineDevframe`: your tool's name, plus a `setup` where you register what it can do.
13
15
14
-
## Step 1 — Define the tool
16
+
> You will need [Node 24+](https://nodejs.org/) for this tutorial.
15
17
16
-
Everything starts with `defineDevframe`: your tool's name, plus a `setup` where you register what it can do. Create the project and the definition:
18
+
Create the project and the definition:
17
19
18
20
```sh
19
21
mkdir data-inspector &&cd data-inspector
20
22
npm init -y && npm pkg set type=module
21
23
npm install devframe && npm install -D typescript
22
24
```
23
25
24
-
```ts [src/devframe.ts]
26
+
```ts [src/data-inspector.ts]
25
27
import { defineDevframe } from'devframe'
26
28
27
-
//Whatever you want to peek at while your app runs — config, a cache, a DB handle.
29
+
//An example server-side data, whatever you want to peek at while your app runs, config, a cache, a DB handle.
`ctx.rpc.register` publishes a function the browser can call: a namespaced `name`, a `type` (`query` is read-only), and a `handler` that takes the call's arguments and returns JSON. That's the whole server. ([RPC](/guide/rpc) has the other types; [Devframe Definition](/guide/devframe-definition) has every field.)
83
87
84
-
## Step 2 — Add a UI
88
+
## Step 2, Add a UI
85
89
86
-
Now the browser half. We'll use React, but any framework works — the only devframe-specific line is `connectDevframe`, which opens the connection home.
90
+
Now the browser part. We'll use React for this example, but any framework works, the only devframe-specific line is `connectDevframe`, which opens the connection home.
87
91
88
92
```sh
89
93
npm install react react-dom @devframes/vite
@@ -148,7 +152,7 @@ export function App() {
148
152
{meta.map(m=> (
149
153
<likey={m.key}>
150
154
<code>{m.key}</code>
151
-
{'—'}
155
+
{'-'}
152
156
{m.type}
153
157
{m.length!=null?` (${m.length})`:''}
154
158
</li>
@@ -162,17 +166,17 @@ export function App() {
162
166
}
163
167
```
164
168
165
-
`client.call(name, ...args)` reaches your handlers. (We cast `.call` to call by name; wire up a typed registry later and every call is checked end to end — see [RPC](/guide/rpc).)
169
+
`client.call(name, ...args)` reaches your handlers. (We cast `.call` to call by name; wire up a typed registry later and every call is checked end to end, see [RPC](/guide/rpc).)
166
170
167
-
## Step 3 — Run it
171
+
## Step 3, Run it as Development
168
172
169
-
The two halves still need to meet. While developing, let Vite serve the UI and hand RPC traffic to devframe:
173
+
To try the tool we just built, we can let Vite serve the UI and hand RPC traffic to devframe:
Open the printed URL. Three keys with their types show up, and typing `config.port` or `users.0.name` and hitting **Query** prints the value. Button → `call` → your `handler` → back to the page: that's the whole app, working.
195
199
196
200
> [!WARNING]
197
-
> `auth: false` trusts anything that can reach the port — fine for localhost, but leave it out (devframe gates with a one-time code by default) for anything reachable from elsewhere. See [Security](/guide/security).
201
+
> `auth: false` trusts anything that can reach the port. We have it off to make the tutorial easier. But we strongly recommend enabling it if you are publishing it as a tool. See [Security](/guide/security).
198
202
199
203
Everything below reuses this exact `src/devframe.ts` and `client/`. We only change where they run.
200
204
201
-
## Step 4 — Dock it in a hub
205
+
## Step 4, Dock it in a hub
202
206
203
-
A [hub](/guide/hub) puts many devframes behind one interface, each a **dock** you switch between — the tool's own UI in an iframe. Since our client uses a bare `connectDevframe()`, it already works anywhere; the hub just needs the built UI. Point the definition at it:
207
+
A [hub](/guide/hub) puts many devframes behind one interface, each a **dock** you switch between, the tool's own UI in an iframe. Since our client uses a bare `connectDevframe()`, it already works anywhere; the hub just needs the built UI. Point the definition at it:
204
208
205
209
```ts [src/devframe.ts]
206
210
import { fileURLToPath } from'node:url'
@@ -217,88 +221,89 @@ Build the UI and stand up a one-devframe hub:
Your inspector now sits in the hub's rail as a dock. Drop more into `devframes: [...]` — your own or the [built-in plugins](/plugins) — and each gets its own. (The hub prints a code to authorize on first connect.)
247
+
Your inspector now sits in the hub's rail as a dock. Drop more into `devframes: [...]`, your own or the [built-in plugins](/plugins), and each gets its own. (The hub prints a code to authorize on first connect.)
245
248
246
-
## Step 5 — Build a static version
249
+
## Step 5, Build a static version
247
250
248
-
Some tools should work with no server at all — a report you can drop on any static host. `createBuild` renders the UI and **bakes in** the results of read-only calls. Opt one in with `snapshot: true`:
251
+
Some tools should work with no server at all, a report you can drop on any static host. `createBuild` renders the UI and **bakes in** the results of read-only calls. Opt one in with `snapshot: true`:
249
252
250
253
```ts
251
254
ctx.rpc.register({
252
255
name: 'data-inspector:get-meta',
253
256
type: 'query',
254
257
jsonSerializable: true,
255
258
snapshot: true, // bake this call's result into the build
Serve `dist-static/` anywhere and the meta list renders from the baked snapshot, no Node in sight. `query` takes an argument, so it needs the live server (next) — or bake specific inputs ([Client Assets](/guide/client-assets)).
277
+
Serve `dist-static/` anywhere and the meta list renders from the baked snapshot, no Node in sight. `query` takes an argument, so it needs the live server (next), or bake specific inputs ([Client Assets](/guide/client-assets)).
273
278
274
-
## Step 6 — Run it standalone
279
+
## Step 6, Run it standalone
275
280
276
281
The definition never depended on Vite. `createDevServer` runs the tool on its own, serving the UI from `clientAssets` and answering RPC live:
Same UI, same live calls — no bundler in the loop. This is what you'd drop into your own Node program.
295
+
Same UI, same live calls, no bundler in the loop. This is what you'd drop into your own Node program.
291
296
292
-
## Step 7 — Give it a CLI
297
+
## Step 7, Give it a CLI
293
298
294
-
Finally, wrap that server in a command shell. `createCac` hands you `dev`, `build`, and `mcp`for free:
299
+
Finally, if you want to provide a CLI for standalone use, which wraps that server in a command shell. `devframe/adapters/cac` made it easy to automatically turn a devframe into a CLI with `dev`, `build`, and `mcp`commands. For example:
@@ -309,11 +314,13 @@ node bin.mjs build # the static build from Step 5
309
314
node bin.mjs mcp # expose the tool to a coding agent over MCP
310
315
```
311
316
312
-
Publish it and the same binary runs with `npx data-inspector`. One definition, five ways to run it — and you never rewrote the tool.
317
+
While you can also build a CLI on your own with the functions provides above.
318
+
319
+
Let's all for this tutorial. If you want to see a full-featured server data inspector, we have it a as a ready-to-use [plugin](/plugins/data-inspector) that you can play with or reference to.
313
320
314
321
## What's next
315
322
316
-
-[RPC](/guide/rpc) —`action` and `event` calls, end-to-end types, schema validation
317
-
-[Shared State](/guide/shared-state) — push live changes to the UI without polling
318
-
-[Hub](/guide/hub) — docks, commands, terminals across many tools
319
-
-[Agent-Native](/guide/agent-native) — expose your tool to coding agents over MCP
323
+
-[RPC](/guide/rpc),`action` and `event` calls, end-to-end types, schema validation
324
+
-[Shared State](/guide/shared-state), push live changes to the UI without polling
325
+
-[Hub](/guide/hub), docks, commands, terminals across many tools
326
+
-[Agent-Native](/guide/agent-native), expose your tool to coding agents over MCP
0 commit comments