Quickstart
This quickstart follows RFC-002 §9.3.8: a developer with Postgres and Node 20+ should reach a streamed answer in under 10 minutes of wall-clock time (user experience expectation; CI uses a Puppeteer harness with a 10-minute test timeout).
Prerequisites
Section titled “Prerequisites”- Node.js 20+ and pnpm
- Postgres 14+ (local or Docker)
- Optional:
GOOGLE_GENERATIVE_AI_API_KEY(Gemini is the documented default; without any model key the example uses a deterministic mock)
Sixty-second install (no Next.js needed)
Section titled “Sixty-second install (no Next.js needed)”If you just want a working Arivie agent without scaffolding a Next.js app, the bare minimum is six commands:
mkdir my-arivie-app && cd my-arivie-apppnpm initpnpm add @arivie/core @arivie/db-postgres @arivie/workspace @ai-sdk/googlepnpm add -D @arivie/clipnpm dlx @arivie/cli add skill cohort-analysispnpm dlx @arivie/cli add skill funnel-conversionThat gives you the core framework plus two SOP skill playbooks dropped into ./skills/. The CLI ships with six skills bundled (cohort-analysis, funnel-conversion, revenue-attribution, dau-mau-ratio, churn-investigation, anomaly-detection) and eight UI scaffolds — no extra package installs needed; everything resolves from the CLI’s own tarball.
From there, jump to the Build your first BI/BA agent tutorial for the full schema → semantic-layer → agent → file-artifact walkthrough.
The Next.js flow below is the production-shaped scaffold (HTTP route + chat UI + middleware-friendly handler shape). Pick whichever matches what you’re building.
1. Create a Next.js app
Section titled “1. Create a Next.js app”pnpm create next-app my-arivie-app --typescript --app --eslintcd my-arivie-app2. Install Arivie packages
Section titled “2. Install Arivie packages”pnpm add @arivie/core @arivie/db-postgres @arivie/react @arivie/mcppnpm add -D @arivie/cli3. Initialize the project
Section titled “3. Initialize the project”pnpm dlx arivie initThis scaffolds arivie.config.ts, semantic directory stubs, and CLI wiring.
4. Configure environment and database
Section titled “4. Configure environment and database”Copy the example env template and set DATABASE_URL:
# Arivie with-nextjs example — copy to .env.local and fill in.DATABASE_URL=postgresql://localhost:5432/arivieARIVIE_OWNER_ID=with-nextjs-owner
# Model selection: first non-empty key wins (Gemini → Anthropic → OpenAI → mock).GOOGLE_GENERATIVE_AI_API_KEY=GOOGLE_MODEL=gemini-2.5-flash# ANTHROPIC_API_KEY=# OPENAI_API_KEY=# OPENAI_MODEL=gpt-5
# Mixpanel (optional — omit for mock-Plan-B cross-source demo per RFC-003 §13 D5)# MIXPANEL_PROJECT_TOKEN=# MIXPANEL_PROJECT_ID=# MIXPANEL_REGION=mixpanelpnpm dlx arivie setupsetup creates the read-only DB role, runs Mastra Memory migrations, and performs the owner-identity smoke check (RFC-002 §4.5).
Seed the dogfood schema (matches examples/with-nextjs/seed.sql):
psql "$DATABASE_URL" -f path/to/seed.sql5. Set a model provider key
Section titled “5. Set a model provider key”Add to .env.local:
GOOGLE_GENERATIVE_AI_API_KEY=your-keyGOOGLE_MODEL=gemini-2.5-flashPreference order in the reference config: Gemini → Anthropic → OpenAI → mock.
6. Add an entity and UI
Section titled “6. Add an entity and UI”pnpm dlx arivie add entity orderspnpm dlx arivie add ui agent-chat7. Wire the API route
Section titled “7. Wire the API route”Create app/api/arivie/route.ts (canonical example):
/* SPDX-License-Identifier: Apache-2.0 */import { getArivieRuntime } from "../../../arivie.config";
export async function POST(req: Request): Promise<Response> { const { arivie } = await getArivieRuntime(); return arivie.next.POST(req);}Your arivie.config.ts should match the reference resolver and defineArivie block:
/* SPDX-License-Identifier: Apache-2.0 */import { dirname, join } from "node:path";import { fileURLToPath } from "node:url";import { defineArivie, type ArivieInstance } from "@arivie/core";import { postgresAdapter } from "@arivie/db-postgres";import { resolveMixpanelSource } from "./lib/mixpanel-source.js";import { makeMcpServer } from "@arivie/mcp";import type { MCPServer } from "@mastra/mcp";import { loadSemanticLayerSync, type SemanticLayer } from "@arivie/semantic";import { anthropic } from "@ai-sdk/anthropic";import { createGoogleGenerativeAI } from "@ai-sdk/google";import { createOpenAI } from "@ai-sdk/openai";import type { LanguageModel } from "ai";import { MockLanguageModelV3 } from "ai/test";
const __dirname = dirname(fileURLToPath(import.meta.url));const semanticPath = join(__dirname, "semantic");
function requireDatabaseUrl(): string { const url = process.env.DATABASE_URL; if (url == null || url.length === 0) { throw new Error( "DATABASE_URL is required — copy .env.example to .env and set your Postgres URL", ); } return url;}
function resolveModel(): LanguageModel { // Preference order: Gemini (cheapest) → Anthropic → OpenAI → mock. // Override the Gemini default via GOOGLE_MODEL (e.g. "gemini-2.5-flash"). const googleKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY; if (googleKey != null && googleKey.length > 0) { const google = createGoogleGenerativeAI({ apiKey: googleKey }); const modelId = process.env.GOOGLE_MODEL ?? "gemini-2.5-flash"; return google(modelId) as LanguageModel; } const anthropicKey = process.env.ANTHROPIC_API_KEY; if (anthropicKey != null && anthropicKey.length > 0) { return anthropic("claude-sonnet-4-20250514"); } const openaiKey = process.env.OPENAI_API_KEY; if (openaiKey != null && openaiKey.length > 0) { const openai = createOpenAI({ apiKey: openaiKey }); return openai(process.env.OPENAI_MODEL ?? "gpt-5") as LanguageModel; } console.warn( "[with-nextjs] No model key set (GOOGLE_GENERATIVE_AI_API_KEY|ANTHROPIC_API_KEY|OPENAI_API_KEY) — using deterministic mock model.", ); return new MockLanguageModelV3({ provider: "mock", modelId: "mock", doGenerate: { content: [ { type: "text", text: "Example mock response (set GOOGLE_GENERATIVE_AI_API_KEY for a live Gemini run).", }, ], finishReason: "stop", usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }, warnings: [], }, } as unknown as ConstructorParameters<typeof MockLanguageModelV3>[0]) as LanguageModel;}
let cached: { arivie: ArivieInstance; mcp: MCPServer; semantic: SemanticLayer;} | undefined;
export async function getArivieRuntime(): Promise<{ arivie: ArivieInstance; mcp: MCPServer;}> { if (cached == null) { const postgres = postgresAdapter({ url: requireDatabaseUrl(), readOnlyRole: "arivie_reader", }); const { adapter: mixpanel, label: mixpanelLabel } = resolveMixpanelSource(); if (mixpanelLabel.includes("mock-plan-b")) { console.warn(`[with-nextjs] ${mixpanelLabel}`); } const semantic = loadSemanticLayerSync(semanticPath); const arivie = await defineArivie({ owner: { id: process.env.ARIVIE_OWNER_ID ?? "with-nextjs-owner", name: "With Next.js Example", }, model: resolveModel(), workspace: { rootDir: semanticPath }, sources: { postgres, mixpanel }, semantic: { path: semanticPath, mode: "preload" }, compileMetric: true, // JSON-IR routing for declared measures (arxiv 2502.00032) resolveUser: async () => ({ userId: "demo-user", permissions: ["analytics:read"], dbRole: "arivie_reader", }), }); const mcp = makeMcpServer({ agent: arivie.agent, semantic, db: postgres, ownerId: process.env.ARIVIE_OWNER_ID ?? "with-nextjs-owner", ownerName: "With Next.js Example", }); cached = { arivie, mcp, semantic }; } return { arivie: cached.arivie, mcp: cached.mcp };}8. Mount the chat UI
Section titled “8. Mount the chat UI”/* SPDX-License-Identifier: Apache-2.0 */import { AgentChat } from "./agent-chat-client";
export default function HomePage() { return ( <main className="page"> <h1>Arivie + Next.js</h1> <p className="lede"> Chat surface powered by <code>useAgent</code> via the registry{" "} <code>AgentChat</code> component. </p> <AgentChat endpoint="/api/arivie" title="Ask your data" /> </main> );}/* SPDX-License-Identifier: Apache-2.0 */"use client";
export { AgentChat } from "@arivie/registry/agent-chat";9. Run dev and ask a question
Section titled “9. Run dev and ask a question”pnpm devOpen http://localhost:3000, type How many customers?, and submit. You should see a streamed assistant message. Without API keys the mock model returns text containing Example mock response.
Verify in CI
Section titled “Verify in CI”The monorepo runs pnpm test:quickstart (Puppeteer + testcontainers Postgres) on every PR after examples boot. See arivie/tests/e2e/quickstart.test.ts.
Next steps
Section titled “Next steps”- Want a deeper walkthrough? Build your first BI/BA agent — 60-minute tutorial that adds a semantic layer, an SOP skill, and demonstrates writing a Markdown report directly from the agent. Each step has a checkpoint.
- SQL is the calculator — research-backed convention that prevents LLM arithmetic errors. Adopt this before your skill count grows.
- The boundary —
owner.idandarivie_owner_identity - Auth integrations — Clerk, WorkOS, Better Auth, Auth.js, custom JWT
- Full runnable project:
arivie/examples/with-nextjs/(basic) orarivie/examples/with-pos-fnb/(full BI/BA example — multi-outlet F&B chain, 10 SOP skills, single agent producing Markdown / HTML artifacts via workspace tools)
Calling the agent
Section titled “Calling the agent”The simplest path is the typed instance.ask() facade:
const result = await instance.ask({ prompt: "How many customers did we get last week?", user: { userId: "u1", permissions: ["analytics:read"], dbRole: "arivie_reader" },});
console.log(result.text); // stringconsole.log(result.toolCalls); // typed tool-call traceconsole.log(result.sql); // string[] of execute_<source> statementsconsole.log(result.artifacts); // file paths written via mastra_workspace_write_fileask() sets the AsyncLocalStorage user context the execute_<source> tool reads, manages the Mastra Memory { thread, resource } thread, and walks the response into a strict AskResult shape. For streaming or other Mastra-specific options, instance.agent.generate(...) remains the escape hatch.