Skip to content

Commit 41d793f

Browse files
authored
Deno quickstart (#4191)
# Description of Changes Adds a Deno quickstart template and documentation. **Template (`templates/deno-ts/`):** - `deno.json` with tasks and import map for spacetimedb - `src/main.ts` using Deno-native APIs (`Deno.stdin`, `Deno.readTextFile`, `Deno.writeTextFile`, `Deno.env`) - `spacetimedb/` server module (TypeScript) - Pre-generated `module_bindings/` **Documentation (`docs/docs/00100-intro/00200-quickstarts/00275-deno.md`):** - Step-by-step quickstart following existing pattern - Covers project structure, tables/reducers, running the client, CLI usage - Documents Deno-specific features: permissions, built-in TypeScript, import maps, no node_modules - Explains `--unstable-sloppy-imports` flag (required because generated bindings use extensionless imports) # API and ABI breaking changes None. This is a new template and documentation addition. # Expected complexity level and risk **1** - Trivial addition. New template files and documentation only, no changes to existing code or generator. # Testing - [x] Server module publishes successfully - [x] Bindings generate correctly - [x] Run `deno task start` and verify connection to SpacetimeDB - [x] Test interactive CLI commands: add names, `list`, `hello`
1 parent 132112b commit 41d793f

20 files changed

Lines changed: 836 additions & 0 deletions
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
---
2+
title: Deno Quickstart
3+
sidebar_label: Deno
4+
slug: /quickstarts/deno
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 Deno app running in under 5 minutes.
12+
13+
## Prerequisites
14+
15+
- [Deno](https://deno.land/) 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 Deno 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 deno-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 Deno 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 # Deno client script
54+
│ └── module_bindings/ # Auto-generated types
55+
└── deno.json # Deno config and tasks
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 Deno 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+
deno task dev
106+
107+
# Or run once
108+
109+
deno task 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: deno-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 Deno client. It uses `DbConnection.builder()` to connect to SpacetimeDB, subscribes to tables, and sets up the interactive CLI using Deno's native APIs.
160+
161+
Unlike browser apps, Deno stores the authentication token in a file using `Deno.readTextFile()` and `Deno.writeTextFile()`.
162+
</StepText>
163+
<StepCode>
164+
```typescript
165+
import { DbConnection } from './module_bindings/index.ts';
166+
167+
// Build and establish connection
168+
DbConnection.builder()
169+
.withUri(HOST)
170+
.withModuleName(DB_NAME)
171+
.withToken(await 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 Deno 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="Deno-specific features">
233+
<StepText>
234+
**Permissions:** Deno requires explicit permissions. The template uses `--allow-net` for WebSocket connections, `--allow-read` and `--allow-write` for token persistence, and `--allow-env` for configuration.
235+
236+
**Sloppy imports:** The `--unstable-sloppy-imports` flag is required because the generated module bindings use extensionless imports (Node.js convention), while Deno requires explicit file extensions. This flag enables Node.js-style module resolution.
237+
238+
**Built-in TypeScript:** Deno runs TypeScript directly without transpilation, making startup faster and eliminating the need for build tools.
239+
240+
**Import maps:** The `deno.json` file defines import maps, allowing you to import `spacetimedb` directly without `npm:` prefix in your code.
241+
242+
**No node_modules:** Deno caches dependencies globally, so there's no `node_modules` folder to manage.
243+
</StepText>
244+
<StepCode>
245+
```bash
246+
# Configure via environment variables
247+
SPACETIMEDB_HOST=ws://localhost:3000 \
248+
SPACETIMEDB_DB_NAME=my-app \
249+
deno task start
250+
251+
# Or run with explicit permissions
252+
deno run --allow-net --allow-read --allow-write --allow-env --unstable-sloppy-imports src/main.ts
253+
254+
# The deno.json configures tasks and imports
255+
cat deno.json
256+
{
257+
"tasks": {
258+
"dev": "deno run --watch --allow-net --allow-read --allow-write --allow-env --unstable-sloppy-imports src/main.ts",
259+
"start": "deno run --allow-net --allow-read --allow-write --allow-env --unstable-sloppy-imports src/main.ts"
260+
},
261+
"imports": {
262+
"spacetimedb": "npm:spacetimedb@1.*"
263+
}
264+
}
265+
````
266+
267+
</StepCode>
268+
269+
</Step>
270+
</StepByStep>
271+
272+
## Next steps
273+
274+
- See the [Chat App Tutorial](/tutorials/chat-app) for a complete example
275+
- Read the [TypeScript SDK Reference](/sdks/typescript) for detailed API docs

templates/deno-ts/.template.json

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

templates/deno-ts/LICENSE

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

templates/deno-ts/deno.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"tasks": {
3+
"dev": "deno run --watch --allow-net --allow-read --allow-write --allow-env --unstable-sloppy-imports src/main.ts",
4+
"start": "deno run --allow-net --allow-read --allow-write --allow-env --unstable-sloppy-imports src/main.ts",
5+
"spacetime:generate": "spacetime generate --lang typescript --out-dir src/module_bindings --project-path spacetimedb"
6+
},
7+
"imports": {
8+
"spacetimedb": "npm:spacetimedb@1.*"
9+
}
10+
}
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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* This tsconfig is used for TypeScript projects created with `spacetimedb init
3+
* --lang typescript`. You can modify it as needed for your project, although
4+
* some options are required by SpacetimeDB.
5+
*/
6+
{
7+
"compilerOptions": {
8+
"strict": true,
9+
"skipLibCheck": true,
10+
"moduleResolution": "bundler",
11+
"jsx": "react-jsx",
12+
13+
/* The following options are required by SpacetimeDB
14+
* and should not be modified
15+
*/
16+
"target": "ESNext",
17+
"lib": ["ES2021", "dom"],
18+
"module": "ESNext",
19+
"isolatedModules": true,
20+
"noEmit": true
21+
},
22+
"include": ["./**/*"]
23+
}

0 commit comments

Comments
 (0)