Scaffold the dashboard workspace
Every track starts with a real project on disk. In this chapter you create my-dashboard/ — a
NetScript workspace with a Fresh frontend, an example service, and a Postgres database — then boot
the whole thing under Aspire so the rest of the track has a running system to build on.
- 1 · Scaffold
- 2 · Contract to service
- 3 · Cache-first query
- 4 · definePage + island
- 5 · Live stream
- 6 · Deploy
What you will build
A my-dashboard/ workspace on disk: shared contracts, an example orders service on port 3002,
a Fresh app, and a Postgres database — all orchestrated by Aspire.
By the end the Aspire dashboard at :18888 shows every resource running, and you understand which
generated directory does what. You will reuse this same workspace for every later chapter.
Before you begin
This is the first chapter; the only prerequisite is the toolchain from the track index. Confirm the CLI is installed and reachable:
netscript --help
You should see the public command groups: init, contract, db, deploy, generate,
marketplace, plugin, service, ui:add, and ui:init. If netscript is not found, make sure
Deno's install directory is on your PATH and open a fresh terminal.
Step 1 — Preview the scaffold with a dry run
Before writing files, ask the CLI what it would create. --dry-run plans the scaffold and prints
totals without touching disk:
netscript init my-dashboard --dry-run
netscript init validates your options, then runs an ordered pipeline that lays down the project
root, Aspire orchestration, contracts, the Fresh app workspace, an empty plugin registry, and
(optionally) a database workspace and an example service. The dry run reports file and directory
totals per phase, so you see the project's shape before committing to it.
Step 2 — Create the workspace
Scaffold for real, with an example service and a Postgres database so the dashboard has live data to
read later. This track names the example service orders and runs it on port 3002:
netscript init my-dashboard --service --service-name orders --service-port 3002 --db postgres
cd my-dashboard
This writes my-dashboard/, formats the output with deno fmt, and initializes a git repository. On
completion the CLI prints a next steps summary tailored to your options — keep it handy.
| Name | Type | Description |
|---|---|---|
--service --service-name orders --service-port 3002 |
flags |
Include an example oRPC service named orders on port 3002 — the read-model the dashboard consumes. |
--db postgres |
flag |
Scaffold a Postgres database workspace. The orders read-model is persisted here. Postgres is this track's stack; swap --db postgres for mysql, mssql, or sqlite for a different engine. |
--dry-run |
flag |
Plan the scaffold and print totals without writing any files (Step 1). |
Step 3 — Tour what you got
Open my-dashboard/. The shape that matters for this track:
-
apps/
- dashboard/ # The Fresh frontend — defineFreshApp, your dashboard UI lives here
- contracts/ # Shared oRPC + Zod contracts, versioned under versions/v1/
-
services/
- orders/ # The example oRPC service (src/main.ts, router.ts, routers/)
- plugins/ # Plugin registry + manifests — empty until you add one
-
aspire/
- apphost.mts # Entry point for aspire start (TypeScript/Node, not C#)
- appsettings.json # Infrastructure manifest Aspire reads
- deno.json # Workspace root: members, tasks, dependency catalog
- netscript.config.ts # Framework config (defineConfig)
The two directories this track lives in:
apps/dashboard/— your Fresh app. Its entry point ismain.ts, which callsdefineFreshApp; its build is configured byvite.config.ts. Chapters 4 and 5 add routes and islands here. This is the heart of the track.services/orders/andcontracts/— the typed read-model your dashboard reads. You shape these in chapter 2, then consume them from the Fresh app in chapters 3–5.
For a guided walk through every generated directory, see the Storefront scaffold chapter — this track keeps the tour short on purpose.
Step 4 — Look at the Fresh app entry point
Before booting, read the two files that define how the Fresh app starts. You do not edit them yet — this is orientation for chapters 4 and 5.
The app entry point registers the KV adapter and creates the app. The order matters: the Redis/Garnet KV adapter import must run before any code that reaches for KV, so it sits at the very top of the file:
// apps/dashboard/main.ts
// Register Redis/Garnet KV adapter — must run before any getKv() call.
import '@netscript/kv/redis';
import { defineFreshApp } from '@netscript/fresh/server';
import type { State } from '@app/utils.ts';
export const app = defineFreshApp<State>({ name: 'dashboard' });
The Vite config wires NetScript's Fresh build plugin alongside the Fresh and Tailwind plugins.
createNetScriptVitePlugin from @netscript/fresh/vite is what teaches Vite about NetScript's route
manifest, workspace aliases, and watch paths:
// apps/dashboard/vite.config.ts (trimmed to the NetScript-specific wiring)
import { fresh } from '@fresh/plugin-vite';
import tailwindCSS from '@tailwindcss/vite';
import { createNetScriptVitePlugin } from '@netscript/fresh/vite';
import { defineConfig } from 'vite';
export default defineConfig(() => ({
plugins: [
fresh(),
tailwindCSS(),
createNetScriptVitePlugin({
appRoot,
workspaceRoot,
aliasEntries,
watchPaths: [/* packages, contracts, plugins */],
routeManifest: {},
}),
],
}));
Step 5 — Bring up orchestration
This is the step that turns a folder of files into a running system. Aspire provisions your
database and cache; you run it before any netscript db command. Run it from the aspire/
subfolder so the CLI finds apphost.mts:
cd aspire
aspire restore # once: restores the Aspire SDK modules into .aspire/
aspire start # starts the AppHost and every declared resource
aspire start brings up the Postgres database, the Redis cache, your orders service, and the Fresh
app together, then prints a URL and login token for the Aspire dashboard:
http://localhost:18888
Leave aspire start running in this terminal; it is your control plane for the rest of the track.
Verify your progress
In a second terminal (leave aspire start going in the first), confirm the orders service answers
its health endpoint:
curl http://localhost:3002/health
You should get a healthy JSON response. Then type-check the whole workspace from the project root:
deno task check
A clean check confirms the scaffold, contracts, and service line up.
- [ ]
netscript --helplists the public command groups. - [ ]
my-dashboard/exists withapps/dashboard/,contracts/, andservices/orders/. - [ ]
aspire startis up; the dashboard at:18888listspostgres,redis, andorders. - [ ]
curl http://localhost:3002/healthreturns a healthy response. - [ ]
deno task checkis clean.
What you built
A real NetScript workspace — my-dashboard/ — with a Fresh app, an orders service on :3002, and
a Postgres database, all orchestrated by Aspire and visible at :18888. Next you will shape the
orders read-model the dashboard consumes.