Skip to content

Commit 3df5231

Browse files
committed
Update featured sample template
1 parent b190c9c commit 3df5231

7 files changed

Lines changed: 377 additions & 159 deletions

File tree

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
# Next.js + MooseStack Monorepo Setup Guide
2+
3+
Import compiled MooseStack objects (OlapTables, IngestPipelines, types) into a Next.js app via pnpm workspaces.
4+
5+
## Final Structure
6+
7+
```
8+
my-project/
9+
├── pnpm-workspace.yaml
10+
├── package.json # root workspace config
11+
├── web/ # Next.js app (create-next-app)
12+
│ ├── package.json # "moose": "workspace:*"
13+
│ ├── next.config.ts # serverExternalPackages
14+
│ └── app/
15+
│ ├── actions.ts # server actions importing from "moose"
16+
│ └── page.tsx
17+
└── moose/ # MooseStack project (moose-cli init)
18+
├── package.json # main/types -> dist/app/
19+
├── tsconfig.json
20+
├── moose.config.toml
21+
└── app/
22+
├── index.ts # barrel export
23+
└── ingest/models.ts # OlapTables, IngestPipelines
24+
```
25+
26+
## Step 1: Create root workspace
27+
28+
```bash
29+
mkdir my-project && cd my-project
30+
```
31+
32+
Create `pnpm-workspace.yaml`:
33+
34+
```yaml
35+
packages:
36+
- web
37+
- moose
38+
```
39+
40+
Create root `package.json`:
41+
42+
```json
43+
{
44+
"private": true,
45+
"scripts": {
46+
"dev:moose": "pnpm -C moose dev",
47+
"dev:web": "pnpm -C web dev",
48+
"build:moose": "pnpm -C moose run build",
49+
"build:web": "pnpm -C web build"
50+
},
51+
"pnpm": {
52+
"onlyBuiltDependencies": [
53+
"@514labs/kafka-javascript",
54+
"@confluentinc/kafka-javascript",
55+
"@swc/core",
56+
"protobufjs",
57+
"sharp"
58+
]
59+
}
60+
}
61+
```
62+
63+
`onlyBuiltDependencies` is required because pnpm 10 blocks native addon builds by default. `@514labs/kafka-javascript` is a native Node addon used by moose-lib.
64+
65+
## Step 2: Create Next.js app
66+
67+
```bash
68+
npx create-next-app@latest web --yes --ts --app
69+
```
70+
71+
Remove the npm lockfile (we use pnpm):
72+
73+
```bash
74+
rm web/package-lock.json
75+
```
76+
77+
## Step 3: Init MooseStack project from template
78+
79+
```bash
80+
npx @514labs/moose-cli@0.6.399 init moose typescript
81+
```
82+
83+
Remove artifacts that conflict with the monorepo:
84+
85+
```bash
86+
rm -rf moose/.git moose/.npmrc
87+
```
88+
89+
## Step 4: Fix template defaults
90+
91+
The template needs three changes to work as a workspace package.
92+
93+
### 4a. Fix export paths in `moose/package.json`
94+
95+
The template sets `"main": "dist/index.js"`, but `moose-tspc` compiles `app/` source to `dist/app/`. Update `main` and `types` to match the actual output path, upgrade the moose-lib/cli versions, and move `@faker-js/faker` to dependencies (the template workflow imports it but lists it as a devDependency):
96+
97+
```json
98+
{
99+
"name": "moose",
100+
"private": true,
101+
"version": "0.0.1",
102+
"engines": {
103+
"node": ">=20 <25"
104+
},
105+
"main": "dist/app/index.js",
106+
"types": "dist/app/index.d.ts",
107+
"scripts": {
108+
"moose": "moose-cli",
109+
"build": "moose-tspc",
110+
"dev": "moose-cli dev"
111+
},
112+
"dependencies": {
113+
"@514labs/moose-lib": "0.6.399",
114+
"@faker-js/faker": "^10.3.0",
115+
"ts-patch": "^3.3.0",
116+
"typia": "^9.6.1"
117+
},
118+
"devDependencies": {
119+
"@514labs/moose-cli": "0.6.399",
120+
"@types/node": "^20.12.12"
121+
},
122+
"pnpm": {
123+
"onlyBuiltDependencies": [
124+
"@514labs/kafka-javascript",
125+
"@confluentinc/kafka-javascript"
126+
]
127+
}
128+
}
129+
```
130+
131+
Changes from template:
132+
- `main`/`types`: `dist/index.js` -> `dist/app/index.js` (matches moose-tspc output)
133+
- `build` script: `moose-cli build --docker` -> `moose-tspc` (workspace compilation vs Docker packaging)
134+
- `@514labs/moose-lib` and `@514labs/moose-cli`: `0.6.309` -> `0.6.399` (template hardcodes an older version)
135+
- `@faker-js/faker`: moved from devDependencies to dependencies
136+
137+
### 4b. Switch to pnpm in `moose/moose.config.toml`
138+
139+
Change the `package_manager` line:
140+
141+
```toml
142+
[typescript_config]
143+
package_manager = "pnpm"
144+
```
145+
146+
## Step 5: Wire Next.js to the moose workspace package
147+
148+
Add `"moose": "workspace:*"` to `web/package.json` dependencies:
149+
150+
```json
151+
{
152+
"dependencies": {
153+
"moose": "workspace:*",
154+
"next": "...",
155+
"react": "...",
156+
"react-dom": "..."
157+
}
158+
}
159+
```
160+
161+
Add `serverExternalPackages` to `web/next.config.ts`:
162+
163+
```typescript
164+
import type { NextConfig } from "next";
165+
166+
const nextConfig: NextConfig = {
167+
serverExternalPackages: ["moose", "@514labs/moose-lib"],
168+
};
169+
170+
export default nextConfig;
171+
```
172+
173+
This tells Next.js to `require()` these at runtime instead of bundling them. Required because moose-lib depends on `@514labs/kafka-javascript`, a native Node addon that cannot be bundled.
174+
175+
## Step 6: Install and compile
176+
177+
```bash
178+
pnpm install
179+
pnpm build:moose
180+
```
181+
182+
Verify the compiled output:
183+
184+
```bash
185+
ls moose/dist/app/index.js moose/dist/app/index.d.ts
186+
```
187+
188+
## Step 7: Import moose objects in Next.js
189+
190+
The template's `app/index.ts` barrel-exports everything. Import in your Next.js server actions:
191+
192+
```typescript
193+
// web/app/actions.ts
194+
"use server";
195+
196+
import { BarPipeline, FooPipeline } from "moose";
197+
import type { Bar, Foo } from "moose";
198+
199+
export async function getPipelines() {
200+
return {
201+
foo: { name: FooPipeline.name, config: FooPipeline.config },
202+
bar: { name: BarPipeline.name, config: BarPipeline.config },
203+
};
204+
}
205+
```
206+
207+
```typescript
208+
// web/app/page.tsx
209+
import { getPipelines } from "./actions";
210+
211+
export default async function Home() {
212+
const pipelines = await getPipelines();
213+
214+
return (
215+
<main>
216+
<h1>Next.js + MooseStack</h1>
217+
<pre>{JSON.stringify(pipelines, null, 2)}</pre>
218+
</main>
219+
);
220+
}
221+
```
222+
223+
Build the Next.js app to verify the import resolves:
224+
225+
```bash
226+
pnpm build:web
227+
```
228+
229+
## Development Workflow
230+
231+
Run in two terminals:
232+
233+
```bash
234+
# Terminal 1: MooseStack dev server (starts ClickHouse, compiles with tspc --watch)
235+
pnpm dev:moose
236+
237+
# Terminal 2: Next.js dev server
238+
pnpm dev:web
239+
```
240+
241+
`moose dev` uses `tspc --watch` for incremental compilation. When you edit a `.ts` file in `moose/app/`, it recompiles to `dist/app/` automatically. The Next.js dev server picks up the changes.
242+
243+
## Production Deployment
244+
245+
Two things deploy separately:
246+
247+
1. **Moose backend** (ClickHouse, Kafka, the moose HTTP server) -- to Boreal or self-hosted Docker
248+
2. **Next.js frontend** -- to Vercel (or any Node.js host)
249+
250+
### Deploy Moose backend
251+
252+
```bash
253+
# Option A: Boreal (managed hosting from the MooseStack team)
254+
cd moose && moose-cli deploy
255+
256+
# Option B: Docker
257+
cd moose && moose-cli build --docker
258+
# Push and deploy the image from .moose/
259+
```
260+
261+
### Deploy Next.js to Vercel
262+
263+
1. Push the monorepo to GitHub
264+
2. Import in Vercel, set root directory to `web/`
265+
3. Set the build command to compile moose first:
266+
267+
```
268+
cd .. && pnpm build:moose && cd web && pnpm build
269+
```
270+
271+
4. Set environment variables for the production ClickHouse connection:
272+
273+
```
274+
MOOSE_CLICKHOUSE_CONFIG__HOST=your-clickhouse-host.com
275+
MOOSE_CLICKHOUSE_CONFIG__PORT=8443
276+
MOOSE_CLICKHOUSE_CONFIG__USER=default
277+
MOOSE_CLICKHOUSE_CONFIG__PASSWORD=your-password
278+
MOOSE_CLICKHOUSE_CONFIG__DB_NAME=your-database
279+
MOOSE_CLICKHOUSE_CONFIG__USE_SSL=true
280+
```
281+
282+
### ClickHouse client for server actions
283+
284+
To query ClickHouse from Next.js server actions, create a client in your moose package that reads connection details from environment variables:
285+
286+
```typescript
287+
// moose/app/client.ts
288+
import { getMooseClients, QueryClient } from "@514labs/moose-lib";
289+
290+
async function getClickhouseClient(): Promise<QueryClient> {
291+
const { client } = await getMooseClients({
292+
host: process.env.MOOSE_CLICKHOUSE_CONFIG__HOST ?? "localhost",
293+
port: process.env.MOOSE_CLICKHOUSE_CONFIG__PORT ?? "18123",
294+
username: process.env.MOOSE_CLICKHOUSE_CONFIG__USER ?? "panda",
295+
password: process.env.MOOSE_CLICKHOUSE_CONFIG__PASSWORD ?? "pandapass",
296+
database: process.env.MOOSE_CLICKHOUSE_CONFIG__DB_NAME ?? "local",
297+
useSSL:
298+
(process.env.MOOSE_CLICKHOUSE_CONFIG__USE_SSL ?? "false") === "true",
299+
});
300+
301+
return client.query;
302+
}
303+
304+
export const db = () => getClickhouseClient();
305+
```
306+
307+
Export it from `app/index.ts` and use in server actions:
308+
309+
```typescript
310+
// web/app/actions.ts
311+
"use server";
312+
import { db } from "moose";
313+
314+
export async function getEvents() {
315+
const client = await db();
316+
const result = await client.execute`SELECT * FROM Bar LIMIT 10`;
317+
return result.json();
318+
}
319+
```
320+
321+
In development, `moose dev` provides local ClickHouse on port 18123. In production, the env vars point to your deployed ClickHouse instance.
322+
323+
## Known Issues
324+
325+
| Issue | Workaround |
326+
|---|---|
327+
| `moose-tspc` outputs to `dist/app/` but template sets `main: dist/index.js` | Change `main`/`types` to `dist/app/index.js` (Step 4a) |
328+
| Template hardcodes `@514labs/moose-lib@0.6.309` regardless of CLI version | Manually upgrade version in `moose/package.json` |
329+
| Template lists `@faker-js/faker` as devDependency but workflow source imports it | Move to dependencies |
330+
| pnpm 10 blocks native addon builds | Add `onlyBuiltDependencies` in root `package.json` |
331+
| Next.js tries to bundle kafka-javascript native addon | Add `serverExternalPackages` in `next.config.ts` |
332+
| `moose-cli build --docker` does not populate project `dist/` | Use `moose-tspc` for workspace builds, `moose-cli build` for Docker packaging |

examples/nextjs-moose/moose/moose.config.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ host = "localhost"
1515
port = 4000
1616
management_port = 5001
1717

18-
# Key dev workflow: rebuild dist/ after moose reloads
19-
on_reload_complete_script = "pnpm build"
2018

2119
[features]
2220
streaming_engine = false
23-
data_model_v2 = true
24-
ddl_plan = true
21+
ddl_plan = true
22+
23+

examples/nextjs-moose/moose/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
"seed": "moose query -f seed.sql"
2222
},
2323
"dependencies": {
24-
"@514labs/moose-lib": "^0.6.293",
24+
"@514labs/moose-lib": "^0.6.399",
2525
"ts-patch": "^3.3.0",
26-
"typia": "^10.1.0"
26+
"typia": "^9.6.1"
2727
},
2828
"devDependencies": {
29-
"@514labs/moose-cli": "^0.6.293",
29+
"@514labs/moose-cli": "^0.6.399",
3030
"@types/node": "^20",
3131
"typescript": "^5.9.3"
3232
}

examples/nextjs-moose/moose/src/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getMooseClients, Sql, QueryClient, sql } from "@514labs/moose-lib";
1+
import { getMooseClients, Sql, QueryClient } from "@514labs/moose-lib";
22

33
async function getClickhouseClient(): Promise<QueryClient> {
44
const { client } = await getMooseClients({

0 commit comments

Comments
 (0)