GridCue
API reference

Server

gridcue/server — the Server Handler, the Node wrapper, the Jev provider exports, and the runtime export conditions.

gridcue/server holds everything that must run on a server: the Server Handler that resolves requests with a server-side provider, a wrapper that mounts it in Node frameworks, and the Jev provider. The browser calls the handler through createRemoteProvider, so the provider key never reaches the browser.

import {
  createGridCueHandler,
  toNodeHandler,
  createJevProvider,
  DEFAULT_JEV_MODEL,
  JEV_SIGNALS,
  JEV_STRATEGIES,
} from "gridcue/server";
import type { GridCueHandler, HandlerOptions, JevClient, JevProviderOptions, JevSignal, JevStrategy } from "gridcue/server";

createGridCueHandler

function createGridCueHandler(options: HandlerOptions): GridCueHandler;

interface HandlerOptions {
  provider: IntentProvider;
  maxBodyBytes?: number;
}

type GridCueHandler = (request: Request) => Promise<Response>;

A Fetch-standard endpoint that resolves requests with a server-side provider. It takes a Request and returns a Response, so it runs anywhere a standard Request to Response function runs, such as a Next.js route handler. It never logs requests, payloads or keys.

NameTypeDefaultDescription
providerIntentProviderrequiredThe provider that resolves each request, usually createJevProvider.
maxBodyBytesnumber32768 (32 KB)Largest accepted request body, in bytes. The body is read as a stream and reading stops as soon as it goes over.

It checks each request in this order and answers with JSON of the form { error: { code, message } } on failure:

StatusCodeWhen
405INPUT_METHODThe method is not POST.
415INPUT_CONTENT_TYPEThe content-type header does not include application/json.
413INPUT_TOO_LARGEcontent-length or the body read so far is over maxBodyBytes.
400INPUT_INVALIDThe body is not JSON, or not a valid ResolutionRequest.
502PROVIDER_MALFORMEDThe provider returned something that is not a valid ResolutionResult.
502the provider's PROVIDER_ code, else PROVIDER_FAILEDThe provider threw.
200noneThe ResolutionResult as JSON.

Every response carries cache-control: no-store. The request's abort signal is passed to the provider, so a provider that honors it stops when the request is aborted. Provider failures always answer with the generic message "The intent provider failed."; the provider's own message is never sent.

The handler has no authentication or rate limiting of its own. Put it behind your app's auth and limits; see Protect the endpoint.

// app/api/gridcue/route.ts (Next.js)
import { createGridCueHandler, createJevProvider } from "gridcue/server";

export const POST = createGridCueHandler({
  provider: createJevProvider({ apiKey: process.env.JEV_API_KEY }),
});

toNodeHandler

function toNodeHandler(
  handler: GridCueHandler,
  options?: { maxBodyBytes?: number },
): (req: IncomingMessage, res: ServerResponse) => Promise<void>;

Wraps a Fetch-standard handler as a Node (req, res) listener. It mounts in Express, Fastify, Connect, Vite's dev server or node:http.

NameTypeDefaultDescription
handlerGridCueHandlerrequiredThe handler from createGridCueHandler.
options.maxBodyBytesnumber32768Largest body the wrapper reads from the Node stream. Over it, it answers 413 with INPUT_TOO_LARGE without calling the handler.
  • If a body parser already ran (req.body is set), the wrapper uses that body instead of reading the stream: a string as is, anything else serialized with JSON.stringify. The handler's own maxBodyBytes still applies.
  • It copies the request headers, builds the URL from req.originalUrl or req.url, and aborts the handler's signal if the client disconnects before the response is sent.
import express from "express";
import { createGridCueHandler, createJevProvider, toNodeHandler } from "gridcue/server";

const app = express();
app.post(
  "/api/gridcue",
  toNodeHandler(createGridCueHandler({ provider: createJevProvider({ apiKey: process.env.JEV_API_KEY }) })),
);

Jev exports

gridcue/server also exports the Jev provider and its constants:

ExportKindDescription
createJevProviderfunctionThe Jev Intent Provider.
DEFAULT_JEV_MODEL"jev-1.13.0"The default model.
JEV_SIGNALSreadonly JevSignal[]["roles", "kind", "adds", "outer", "values", "reading"].
JEV_STRATEGIESRecord<JevStrategy, readonly JevSignal[]>The signals each strategy turns on.
JevClient, JevProviderOptions, JevSignal, JevStrategytypesThe provider's option and client types.

They are documented on the Providers page. createJevProvider uses @typesafe-ai/sdk, an optional peer dependency: install it on the server when you use Jev. It loads on the first Jev request, so gridcue/server works without it for the Mock or your own provider; a Jev request without it fails with INPUT_CONFIG.

Export conditions

gridcue/server resolves to different files depending on where it is bundled, so a provider key can never be shipped to a browser by mistake:

"./server": {
  "types": "./dist/server.d.ts",
  "workerd": "./dist/server.js",
  "edge-light": "./dist/server.js",
  "browser": "./dist/server-browser.js",
  "default": "./dist/server.js"
}
ConditionResolves toUsed by
workerdthe real moduleCloudflare Workers (wrangler)
edge-lightthe real moduleVercel's Edge Runtime
browsera stub that throws on importbrowser bundles
defaultthe real moduleNode and everything else

The browser stub throws as soon as it is imported:

gridcue/server runs only on a server. Import createRemoteProvider from "gridcue" in the browser instead.

workerd and edge-light are listed before browser, because wrangler resolves with workerd, worker and browser together; without them a Worker would get the stub. The worker condition is deliberately not listed: bundlers use it for browser Web Workers, and resolving the real module there would put the key inside a page's own Web Worker. See ADR 0011 in the repository. @typesafe-ai/sdk, which the Jev provider uses, runs under workerd too: a live Jev request was checked under wrangler dev, with and without nodejs_compat.

To call the handler from the browser, use createRemoteProvider from gridcue. For a full walkthrough, see Server Handler.

On this page