Skip to content

Commit 0ed052e

Browse files
authored
Quickstart nodejs (#4112)
# Description of Changes Adds a Node.js quickstart template and documentation for SpacetimeDB. ## Template (`templates/nodejs-ts/`) - SpacetimeDB module with TypeScript (`spacetimedb/src/index.ts`) - Node.js client with interactive CLI (`src/main.ts`) - File-based token persistence (`.spacetimedb-token`) instead of localStorage - Table change callbacks (`onInsert`, `onDelete`) - Graceful shutdown handlers for `SIGINT`/`SIGTERM` - Pre-generated module bindings in `src/module_bindings/` ## Documentation (`docs/docs/00100-intro/00200-quickstarts/00300-nodejs.md`) - Step-by-step quickstart guide following existing pattern - Covers project creation with `spacetime dev --template nodejs-ts` - Explains project structure and client code - Documents interactive CLI commands (`list`, `hello`, `<name>`) - Covers Node.js-specific considerations (WebSocket support, token storage) - Documents environment variable configuration (`SPACETIMEDB_HOST`, `SPACETIMEDB_DB_NAME`) ## Consistency - Environment variables follow the same `HOST`/`DB_NAME` naming convention as other quickstarts - Documentation structure mirrors React and Next.js quickstarts # API and ABI breaking changes None. # Expected complexity level and risk 1 - Additive change only. New template and documentation, no modifications to existing functionality. # Testing - [ ] Run `spacetime dev --template nodejs-ts` and verify project initializes - [ ] Run `npm run dev` and verify connection to SpacetimeDB - [ ] Test adding a person by typing a name - [ ] Test `list` command shows all people - [ ] Test `hello` command calls reducer (check server logs) - [ ] Verify token persistence works across restarts - [ ] Test graceful shutdown with Ctrl+C - [ ] Check documentation renders correctly
1 parent 3f5c864 commit 0ed052e

21 files changed

Lines changed: 842 additions & 0 deletions
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
---
2+
title: Node.js Quickstart
3+
sidebar_label: Node.js
4+
slug: /quickstarts/nodejs
5+
hide_table_of_contents: true
6+
---
7+
8+
import { InstallCardLink } from "@site/src/components/InstallCardLink";
9+
import { StepByStep, Step, StepText, StepCode } from "@site/src/components/Steps";
10+
11+
Get a SpacetimeDB Node.js app running in under 5 minutes.
12+
13+
## Prerequisites
14+
15+
- [Node.js](https://nodejs.org/) 18+ installed
16+
- [SpacetimeDB CLI](https://spacetimedb.com/install) installed
17+
18+
<InstallCardLink />
19+
20+
---
21+
22+
<StepByStep>
23+
<Step title="Create your project">
24+
<StepText>
25+
Run the `spacetime dev` command to create a new project with a SpacetimeDB module and Node.js client.
26+
27+
This will start the local SpacetimeDB server, publish your module, and generate TypeScript bindings.
28+
</StepText>
29+
<StepCode>
30+
31+
```bash
32+
spacetime dev --template nodejs-ts
33+
```
34+
35+
</StepCode>
36+
37+
</Step>
38+
39+
<Step title="Explore the project structure">
40+
<StepText>
41+
Your project contains both server and client code.
42+
43+
Edit `spacetimedb/src/index.ts` to add tables and reducers. Edit `src/main.ts` to build your Node.js client.
44+
</StepText>
45+
<StepCode>
46+
47+
```
48+
my-spacetime-app/
49+
├── spacetimedb/ # Your SpacetimeDB module
50+
│ └── src/
51+
│ └── index.ts # Server-side logic
52+
├── src/
53+
│ ├── main.ts # Node.js client script
54+
│ └── module_bindings/ # Auto-generated types
55+
└── package.json
56+
```
57+
58+
</StepCode>
59+
60+
</Step>
61+
62+
<Step title="Understand tables and reducers">
63+
<StepText>
64+
Open `spacetimedb/src/index.ts` to see the module code. The template includes a `person` table and two reducers: `add` to insert a person, and `say_hello` to greet everyone.
65+
66+
Tables store your data. Reducers are functions that modify data — they're the only way to write to the database.
67+
</StepText>
68+
<StepCode>
69+
70+
```typescript
71+
import { schema, table, t } from 'spacetimedb/server';
72+
73+
export const spacetimedb = schema(
74+
table(
75+
{ name: 'person', public: true },
76+
{
77+
name: t.string(),
78+
}
79+
)
80+
);
81+
82+
spacetimedb.reducer('add', { name: t.string() }, (ctx, { name }) => {
83+
ctx.db.person.insert({ name });
84+
});
85+
86+
spacetimedb.reducer('say_hello', ctx => {
87+
for (const person of ctx.db.person.iter()) {
88+
console.info(`Hello, ${person.name}!`);
89+
}
90+
console.info('Hello, World!');
91+
});
92+
```
93+
94+
</StepCode>
95+
96+
</Step>
97+
98+
<Step title="Run the client">
99+
<StepText>
100+
In a new terminal, run the Node.js client. It will connect to SpacetimeDB and start an interactive CLI where you can add people and query the database.
101+
</StepText>
102+
<StepCode>
103+
```bash
104+
# Run with auto-reload during development
105+
npm run dev
106+
107+
# Or run once
108+
109+
npm run start
110+
111+
```
112+
</StepCode>
113+
</Step>
114+
115+
<Step title="Use the interactive CLI">
116+
<StepText>
117+
The client provides a command-line interface to interact with your SpacetimeDB module. Type a name to add a person, or use the built-in commands.
118+
</StepText>
119+
<StepCode>
120+
```
121+
122+
Connecting to SpacetimeDB...
123+
URI: ws://localhost:3000
124+
Module: nodejs-ts
125+
126+
Connected to SpacetimeDB!
127+
Identity: abc123def456...
128+
129+
Current people (0):
130+
(none yet)
131+
132+
Commands:
133+
<name> - Add a person with that name
134+
list - Show all people
135+
hello - Greet everyone (check server logs)
136+
Ctrl+C - Quit
137+
138+
> Alice
139+
> [Added] Alice
140+
141+
> Bob
142+
> [Added] Bob
143+
144+
> list
145+
> People in database:
146+
147+
- Alice
148+
- Bob
149+
150+
> hello
151+
> Called say_hello reducer (check server logs)
152+
153+
````
154+
</StepCode>
155+
</Step>
156+
157+
<Step title="Understand the client code">
158+
<StepText>
159+
Open `src/main.ts` to see the Node.js client. It uses `DbConnection.builder()` to connect to SpacetimeDB, subscribes to tables, and sets up the interactive CLI using Node's `readline` module.
160+
161+
Unlike browser apps, Node.js stores the authentication token in a file instead of localStorage.
162+
</StepText>
163+
<StepCode>
164+
```typescript
165+
import { DbConnection } from './module_bindings/index.js';
166+
167+
// Build and establish connection
168+
DbConnection.builder()
169+
.withUri(HOST)
170+
.withModuleName(DB_NAME)
171+
.withToken(loadToken()) // Load saved token from file
172+
.onConnect((conn, identity, token) => {
173+
console.log('Connected! Identity:', identity.toHexString());
174+
saveToken(token); // Save token for future connections
175+
176+
// Subscribe to all tables
177+
conn.subscriptionBuilder()
178+
.onApplied((ctx) => {
179+
// Show current data, start CLI
180+
setupCLI(conn);
181+
})
182+
.subscribeToAllTables();
183+
184+
// Listen for table changes
185+
conn.db.person.onInsert((ctx, person) => {
186+
console.log(`[Added] ${person.name}`);
187+
});
188+
})
189+
.build();
190+
````
191+
192+
</StepCode>
193+
194+
</Step>
195+
196+
<Step title="Test with the SpacetimeDB CLI">
197+
<StepText>
198+
You can also use the SpacetimeDB CLI to call reducers and query your data directly. Changes made via the CLI will appear in your Node.js client in real-time.
199+
</StepText>
200+
<StepCode>
201+
```bash
202+
# Call the add reducer to insert a person
203+
spacetime call <database-name> add Charlie
204+
205+
# Query the person table
206+
207+
spacetime sql <database-name> "SELECT \* FROM person"
208+
name
209+
210+
---
211+
212+
"Alice"
213+
"Bob"
214+
"Charlie"
215+
216+
# Call say_hello to greet everyone
217+
218+
spacetime call <database-name> say_hello
219+
220+
# View the module logs
221+
222+
spacetime logs <database-name>
223+
2025-01-13T12:00:00.000000Z INFO: Hello, Alice!
224+
2025-01-13T12:00:00.000000Z INFO: Hello, Bob!
225+
2025-01-13T12:00:00.000000Z INFO: Hello, Charlie!
226+
2025-01-13T12:00:00.000000Z INFO: Hello, World!
227+
228+
````
229+
</StepCode>
230+
</Step>
231+
232+
<Step title="Node.js considerations">
233+
<StepText>
234+
**WebSocket support:** Node.js 22+ has native WebSocket support. For Node.js 18-21, the SDK automatically uses the `undici` package (included in devDependencies).
235+
236+
**Environment variables:** Configure the connection using `SPACETIMEDB_HOST` and `SPACETIMEDB_DB_NAME` environment variables.
237+
238+
**Graceful shutdown:** The template includes signal handlers for `SIGINT` and `SIGTERM` to cleanly disconnect when stopping the process.
239+
</StepText>
240+
<StepCode>
241+
```bash
242+
# Configure via environment variables
243+
SPACETIMEDB_HOST=ws://localhost:3000 \
244+
SPACETIMEDB_DB_NAME=my-app \
245+
npm run start
246+
247+
# Or use a .env file with dotenv
248+
````
249+
250+
</StepCode>
251+
252+
</Step>
253+
</StepByStep>
254+
255+
## Next steps
256+
257+
- See the [Chat App Tutorial](/tutorials/chat-app) for a complete example
258+
- Read the [TypeScript SDK Reference](/sdks/typescript) for detailed API docs

templates/nodejs-ts/.template.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"description": "Node.js TypeScript client and server template",
3+
"client_lang": "typescript",
4+
"server_lang": "typescript"
5+
}

templates/nodejs-ts/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../licenses/apache2.txt

templates/nodejs-ts/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@clockworklabs/nodejs-ts",
3+
"private": true,
4+
"version": "0.0.1",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "tsx watch src/main.ts",
8+
"start": "tsx src/main.ts",
9+
"build": "tsc",
10+
"spacetime:generate": "spacetime generate --lang typescript --out-dir src/module_bindings --project-path spacetimedb"
11+
},
12+
"dependencies": {
13+
"spacetimedb": "workspace:*"
14+
},
15+
"devDependencies": {
16+
"@types/node": "^20.0.0",
17+
"tsx": "^4.19.0",
18+
"typescript": "~5.6.2",
19+
"undici": "^6.19.2"
20+
}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "spacetime-module",
3+
"version": "1.0.0",
4+
"description": "",
5+
"scripts": {
6+
"build": "spacetime build",
7+
"publish": "spacetime publish"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"spacetimedb": "1.*"
14+
}
15+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { schema, table, t } from 'spacetimedb/server';
2+
3+
export const spacetimedb = schema(
4+
table(
5+
{ name: 'person', public: true },
6+
{
7+
name: t.string(),
8+
}
9+
)
10+
);
11+
12+
spacetimedb.init(_ctx => {
13+
// Called when the module is initially published
14+
});
15+
16+
spacetimedb.clientConnected(_ctx => {
17+
// Called every time a new client connects
18+
});
19+
20+
spacetimedb.clientDisconnected(_ctx => {
21+
// Called every time a client disconnects
22+
});
23+
24+
spacetimedb.reducer('add', { name: t.string() }, (ctx, { name }) => {
25+
ctx.db.person.insert({ name });
26+
});
27+
28+
spacetimedb.reducer('say_hello', ctx => {
29+
for (const person of ctx.db.person.iter()) {
30+
console.info(`Hello, ${person.name}!`);
31+
}
32+
console.info('Hello, World!');
33+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
/*
3+
* This tsconfig is used for TypeScript projects created with `spacetimedb init
4+
* --lang typescript`. You can modify it as needed for your project, although
5+
* some options are required by SpacetimeDB.
6+
*/
7+
{
8+
"compilerOptions": {
9+
"strict": true,
10+
"skipLibCheck": true,
11+
"moduleResolution": "bundler",
12+
"jsx": "react-jsx",
13+
14+
/* The following options are required by SpacetimeDB
15+
* and should not be modified
16+
*/
17+
"target": "ESNext",
18+
"lib": ["ES2021", "dom"],
19+
"module": "ESNext",
20+
"isolatedModules": true,
21+
"noEmit": true
22+
},
23+
"include": ["./**/*"]
24+
}

0 commit comments

Comments
 (0)