Skip to content

appx-org/next-app-example

Repository files navigation

next-app-example

A minimal LLM chat app built on @appx-org/agent-client and agent-server, using Next.js 15 (App Router).

It exists for two reasons:

  1. A runnable example of consuming agent-client from Next.js — streaming chat, sessions, tool-call cards, and the provider-credential settings panel.
  2. A reference for the Next.js consumer requirements (see agent-client#3): the package ships TypeScript source, so a Next consumer needs transpilePackages, the GitHub Packages scope registry, and React deduping.

What it does

  • Talks to agent-server through a same-origin proxy route (src/app/api/agent/[...path]/route.ts). The browser only sees /api/agent/...; the agent-server origin and bearer token stay server-side.
  • Ensures a default project exists, then renders the batteries-included AgentChat (session sidebar + streaming chat + model controls).
  • Includes the AgentSettings panel (the Settings button) so you can add an LLM provider API key, then chat.

Run it

You need agent-server running first.

# 1. start agent-server (in ../../agent-server)
cd ../../agent-server
npm install && npm run build
WORKSPACE_DIR=/tmp/agent-ws npm start          # → http://127.0.0.1:4001

# 2. start this app
cd ../apps/next-app-example
cp .env.example .env            # defaults point at http://127.0.0.1:4001
npm install
npm run dev                     # → http://localhost:3000

Open http://localhost:3000, click Settings, add a provider API key (or set ANTHROPIC_API_KEY when launching agent-server), then go Back to chat, create a session, and start chatting.

Env var Default Notes
AGENT_SERVER_URL http://127.0.0.1:4001 Origin the proxy forwards to.
AGENT_SERVER_TOKEN (empty) Bearer token, if agent-server runs with AGENT_SERVER_TOKEN. Injected server-side; never exposed to the browser.

Consuming agent-client from Next.js

These are the gotchas this example encodes (issue #3).

1. transpilePackages

agent-client's exports map points at src/ (raw .ts/.tsx), so Next must transpile it like first-party code:

// next.config.ts
const nextConfig: NextConfig = {
  transpilePackages: ["@appx-org/agent-client"],
};

2. 'use client' boundary

Every agent-client component uses hooks, so mount them under a client component. Here, app/page.tsx (server) renders app/Chat.tsx which starts with 'use client'. Global CSS (import '@appx-org/agent-client/styles.css') is imported in app/layout.tsx, the only place the App Router allows it.

3. Installing from GitHub Packages (CI / prod)

The published package lives in GitHub Packages under the @appx-org scope. Add the scope registry to .npmrc:

@appx-org:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

and swap the dependency from the local link to a version range:

// package.json
"@appx-org/agent-client": "^0.1.0"   // instead of "file:../../agent-client"

${GITHUB_TOKEN} is expanded from the environment at install time, so the token is never committed.

Docker / CI: pass the token as a secret, never a layer

Do not bake the token into an image layer (it stays in history). Use a BuildKit secret mount so it exists only for the npm ci step:

# syntax=docker/dockerfile:1
FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json .npmrc ./
RUN --mount=type=secret,id=github_token \
    GITHUB_TOKEN="$(cat /run/secrets/github_token)" npm ci
docker build --secret id=github_token,env=GITHUB_TOKEN .

In GitHub Actions, npm can read secrets.GITHUB_TOKEN (which has read access to packages in the same org) via the same GITHUB_TOKEN env var.

4. React deduping under file: linking

This example links the package locally (file:../../agent-client) for live editing. A local install materialises the package's own node_modules/react (React is a peer dep, but file: installs still pull the devDependency), and two React copies break hooks ("invalid hook call").

The package README's resolve.dedupe tip is Vite-specific and doesn't apply to Next. Instead, alias React to this app's single copy — scoped to the client bundle so Next's server/RSC React internals are left alone:

// next.config.ts
webpack(config, { isServer }) {
  if (!isServer) {
    config.resolve.alias = {
      ...config.resolve.alias,
      react: path.resolve(process.cwd(), "node_modules/react"),
      "react-dom": path.resolve(process.cwd(), "node_modules/react-dom"),
    };
  }
  return config;
}

When installing the published package (not a file: link), React resolves once from the app as a normal peer dep, so this alias is harmless but unnecessary.

Layout

src/app/
├── layout.tsx                    # imports agent-client styles.css + globals.css
├── globals.css                   # page shell layout
├── page.tsx                      # server component → <Chat/>
├── Chat.tsx                      # 'use client' — ensures project, renders AgentChat/AgentSettings
└── api/agent/[...path]/route.ts  # same-origin proxy → agent-server /v1 (token stays server-side)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors