Skip to content

Integrating EVK with your backend

EVK never retrieves anything by itself. A host gives the controller a SearchAdapter, and the controller validates everything the adapter returns. Two adapters ship as references:

  • createFixtureAdapter (integration) answers from the synthetic corpus, with fault controls for demos and tests.
  • createJsonHttpAdapter (integration/src/ts/adapters/json-http-adapter.ts) moves JSON between the controller and your endpoint, using a small protocol. Copy it into your application, or implement SearchAdapter directly. It is not a package export: the protocol is a suggestion, and your endpoint is yours.

/host-harness.html runs the ordinary components over the JSON adapter. It points at a synthetic in-page endpoint and shows every exchange in a protocol inspector.

Status: EVK has not yet been embedded in the owner’s real application; that check is blocked on the owner (integration-r-0084). The harness and the adapter contract tests are what is verified here.

import { createController } from 'entity-viz-kit/core';
import { createJsonHttpAdapter } from './evk/json-http-adapter.js'; // copied from EVK's integration sources
const controller = createController(createJsonHttpAdapter({
baseUrl: '/api/evk/v1',
// Authorization belongs in your fetch wrapper (cookies, headers), never in URLs or EVK state.
fetch: (url, init) => window.fetch(url, { ...init, credentials: 'same-origin' }),
capabilities: { profile: true, relation: true, evidence: true, source: true } // omit what you don't serve
}));
await controller.start();

Hand controller to EntitySearchControls, SearchResults, InspectionPanel, ControllerGraph, the workspace, the sidebar, or the operator page. Optional capabilities you leave out become “unavailable” states in the UI, never errors.

Every operation is POST {baseUrl}/{operation} with content-type: application/json. The request body is the core request object, unchanged. The response body is the core response object, and the controller validates it (unknown fields, bad IDs, unsafe URLs and executable-looking values are rejected). A non-2xx status becomes a failed state with a retry path.

OperationRequest body (core type)Response body (core type)
searchSearchRequest { requestId, revision, query: { text, entityIds }, context }SearchResponse { requestId, revision, status: 'ok'|'partial'|'unavailable', documents, entities, recommendations, message? }
profileLoadRequest<EntityId> { requestId, revision, key, context }ResourceResponse<Entity> { requestId, revision, status: 'ok'|'unavailable', value }
relationLoadRequest<PairId>ResourceResponse<PairRelation>
evidenceLoadRequest<EvidenceId>ResourceResponse<Evidence>
sourceLoadRequest<DocumentId>ResourceResponse<SourceDocument>

Echo requestId and revision exactly. The controller uses them to drop stale responses, and aborts superseded requests through the signal passed to your fetch.

Example search exchange, produced by the synthetic endpoint and abridged (…). The real response has 3 documents, 7 entities and several recommendations:

{ "requestId": "evk:example:3", "revision": 2, "query": { "text": "", "entityIds": ["org:northstar"] }, "context": null }
{ "requestId": "evk:example:3", "revision": 2, "status": "ok",
"documents": [{ "id": "doc:field", "version": "fixture-v1", "title": "Field report: observatory instruments",
"snippet": { "text": "Alex Morgan at Northstar Research calibrated …",
"mentions": [{ "id": "doc:field:mention:1", "entityIds": ["org:northstar"], "range": { "start": 15, "end": 33, "unit": "utf16" } }] },
"entityIds": ["person:alex-engineer", "org:northstar", "building:observatory"] }],
"entities": [{ "id": "person:alex-engineer", "type": "person", "label": "Alex Morgan", "subtitle": "Engineer · Northstar Research", "aliases": ["A. Morgan"] }],
"recommendations": [{ "entityId": "person:alex-engineer", "reason": "Mentioned in the current synthetic results" }] }

Identity and authorization rules for your endpoint

Section titled “Identity and authorization rules for your endpoint”
  • Entity, document, mention and evidence IDs are opaque strings that you choose. EVK never derives identity from labels. Send the same ID for the same thing every time.
  • Offsets are canonical-text UTF-16 offsets unless unit: "codepoint" is stated (see the canonical text guide).
  • Authorize before you answer. EVK renders what it receives, and the operator page publishes only what the host approves. Neither is an authorization system.
  • Keep secrets out of payloads. Responses end up in controller state, the UI and (for operator publications) audience snapshots.