Yak Docs
Chat Integration

JavaScript SDK

The @yak-io/javascript package provides the core SDK and server handlers. It works with any JavaScript runtime that supports the Fetch API.

When to Use

  • Building with non-React frameworks (Vue, Svelte, Solid, etc.)
  • Need server handlers for custom runtimes (Cloudflare Workers, Deno, Bun)
  • Building a custom integration without React components
  • Embedding Yak in a vanilla JavaScript application

For React apps, use @yak-io/react. For Next.js, use @yak-io/nextjs.

Installation

npm install @yak-io/javascript
pnpm add @yak-io/javascript
yarn add @yak-io/javascript
bun add @yak-io/javascript

Client SDK

The YakClient class manages iframe communication and is used internally by the React components. For most use cases, we recommend using @yak-io/react or @yak-io/nextjs instead.

import { YakClient } from "@yak-io/javascript";

const client = new YakClient({
  appId: "your-app-id",
  onToolCall: async (name, args) => {
    const res = await fetch("/api/yak", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name, args }),
    });
    const data = await res.json();
    if (!data.ok) throw new Error(data.error);
    return data.result;
  },
  onReady: () => {
    console.log("Chat widget ready");
  },
  onClose: () => {
    console.log("Chat closed");
  },
});

// Lifecycle methods (typically managed by React components)
client.mount();           // Start listening for messages
client.setWidgetOpen(true);  // Open the widget
client.sendPrompt("Help me"); // Send a prompt when ready
client.setWidgetOpen(false); // Close the widget
client.unmount();         // Stop listening and clean up

Configuration Options

OptionTypeDescription
appIdstringYour Yak application ID
onToolCall(name, args) => Promise<unknown>Tool execution handler
onToolCallComplete(event: ToolCallEvent) => voidCalled after each tool call completes
onRedirect(path: string) => voidNavigation handler
onReady() => voidCalled when iframe is ready
onClose() => voidCalled when widget is closed
themeThemeWidget theming options

Cache Invalidation

Use onToolCallComplete to invalidate caches when tools modify data:

const client = new YakClient({
  appId: "your-app-id",
  onToolCall: async (name, args) => {
    // Execute the tool
    return executeToolCall(name, args);
  },
  onToolCallComplete: (event) => {
    // Invalidate relevant caches after successful mutations
    if (event.ok && event.name.startsWith("order.")) {
      queryClient.invalidateQueries({ queryKey: ["orders"] });
    }
  },
});

The ToolCallEvent contains:

PropertyTypeDescription
namestringThe tool name that was called
argsunknownThe arguments passed to the tool
okbooleanWhether the call succeeded
resultunknownThe result (if ok is true)
errorstringThe error message (if ok is false)

Server Handlers

The SDK provides Fetch-style handlers for serving routes and executing tools.

import { createYakHandler } from "@yak-io/javascript/server";

const { GET, POST } = createYakHandler({
  routes: [
    { path: "/", title: "Home" },
    { path: "/pricing", title: "Pricing" },
  ],
  tools: [
    {
      id: "custom",
      getTools: async () => [
        {
          name: "greet",
          description: "Greet a user by name",
          input_schema: {
            type: "object",
            properties: { name: { type: "string" } },
            required: ["name"],
          },
        },
      ],
      executeTool: async (name, args) => {
        if (name === "greet") {
          return { message: `Hello, ${args.name}!` };
        }
        throw new Error(`Unknown tool: ${name}`);
      },
    },
  ],
});

export { GET, POST };

Route Schema

PropertyTypeRequiredDescription
pathstringURL path
titlestringHuman-readable title
descriptionstringBrief description

Dynamic Route Sources

const cmsRoutes = {
  id: "cms",
  getRoutes: async () => {
    const res = await fetch("https://cms.example.com/api/pages");
    return res.json();
  },
};

const { GET, POST } = createYakHandler({
  routes: [staticRoutes, cmsRoutes],
});

Runtime Examples

Cloudflare Workers

import { createYakHandler } from "@yak-io/javascript/server";

const { GET, POST } = createYakHandler({
  routes: [{ path: "/", title: "Home" }],
});

export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    
    if (url.pathname === "/api/yak") {
      if (request.method === "GET") return GET(request);
      if (request.method === "POST") return POST(request);
    }
    
    return new Response("Not Found", { status: 404 });
  },
};

Deno

import { createYakHandler } from "@yak-io/javascript/server";

const { GET, POST } = createYakHandler({
  routes: [{ path: "/", title: "Home" }],
});

Deno.serve(async (request) => {
  const url = new URL(request.url);
  
  if (url.pathname === "/api/yak") {
    if (request.method === "GET") return GET(request);
    if (request.method === "POST") return POST(request);
  }
  
  return new Response("Not Found", { status: 404 });
});

Bun

import { createYakHandler } from "@yak-io/javascript/server";

const { GET, POST } = createYakHandler({
  routes: [{ path: "/", title: "Home" }],
});

Bun.serve({
  port: 3000,
  async fetch(request) {
    const url = new URL(request.url);
    
    if (url.pathname === "/api/yak") {
      if (request.method === "GET") return GET(request);
      if (request.method === "POST") return POST(request);
    }
    
    return new Response("Not Found", { status: 404 });
  },
});

Express / Node.js

import express from "express";
import { createYakHandler } from "@yak-io/javascript/server";

const app = express();
app.use(express.json());

const { GET, POST } = createYakHandler({
  routes: [{ path: "/", title: "Home" }],
});

app.all("/api/yak", async (req, res) => {
  const url = `${req.protocol}://${req.get("host")}${req.originalUrl}`;
  
  const request = new Request(url, {
    method: req.method,
    headers: req.headers as HeadersInit,
    body: req.method !== "GET" ? JSON.stringify(req.body) : undefined,
  });
  
  const response = await (req.method === "POST" ? POST(request) : GET(request));
  
  res.status(response.status);
  response.headers.forEach((value, key) => res.setHeader(key, value));
  res.send(Buffer.from(await response.arrayBuffer()));
});

app.listen(3000);

Hono

import { Hono } from "hono";
import { createYakHandler } from "@yak-io/javascript/server";

const app = new Hono();

const { GET, POST } = createYakHandler({
  routes: [{ path: "/", title: "Home" }],
});

app.get("/api/yak", (c) => GET(c.req.raw));
app.post("/api/yak", (c) => POST(c.req.raw));

export default app;

TypeScript

Import types for your integrations:

// Client-side types
import type {
  YakClientConfig,
  Theme,
  ThemeColors,
  ButtonColors,
  ChatConfig,
  ChatConfigProvider,
  ToolCallHandler,
  ToolCallEvent,
  ToolCallEventListener,
  GraphQLSchemaHandler,
  RESTSchemaHandler,
  GraphQLRequest,
  RESTRequest,
  SchemaSource,
  GraphQLSchemaSource,
  OpenAPISchemaSource,
} from "@yak-io/javascript";

// Server-side types
import type {
  RouteInfo,
  RouteManifest,
  RouteSource,
  RouteSourceInput,
  ToolDefinition,
  ToolManifest,
  ToolSource,
  ToolSourceInput,
  ToolExecutor,
  ToolCallPayload,
  ToolCallResult,
} from "@yak-io/javascript/server";

On this page