Oct 6, 20264 min read

Introducing Encore × SurrealDB

Combine vector search and graph relationships with a backend agents can build and verify end-to-end.

SurrealDB brings documents, graph relationships, and vector search into one database for context that AI agents can retrieve by meaning or by connection. Using its vector and graph features, we'll build a durable memory service with Encore that ingests new memories, scopes recall to the right agent, and enforces retention, then test the complete workflow locally.

Building the memory service

We'll accept text, an embedding, and the entities found in it through a typed API, then publish the memory to pub/sub. The API returns once the event has been queued, and a subscriber writes the vector and its entity relationships to SurrealDB. A scheduled job removes memories after their retention period.

The model producing the embedding remains a choice for the agent or application calling the service, with the memory workflow kept behind its own API boundary so storage and retrieval can be tested independently.

We'll configure the SurrealDB connection through Encore secrets, keeping its credentials out of the application code:

import { secret } from "encore.dev/config"; import { Surreal } from "surrealdb"; const surrealURL = secret("SurrealDBURL"); const surrealToken = secret("SurrealDBToken"); async function connectSurreal(): Promise<Surreal> { const db = new Surreal(); await db.connect(surrealURL(), { authentication: surrealToken(), }); await db.use({ namespace: "encore", database: "agent_memory" }); return db; }

We'll publish a complete memory event to an at-least-once topic and return once it has been queued. If ingestion fails, pub/sub can retry it without holding open the agent's request:

import { api } from "encore.dev/api"; import { Topic } from "encore.dev/pubsub"; interface RememberParams { agentID: string; text: string; embedding: number[]; entities?: string[]; expiresAt?: Date; } interface MemoryEvent extends Omit<RememberParams, "entities"> { memoryID: string; entities: Array<{ name: string; slug: string }>; } const memoriesToStore = new Topic<MemoryEvent>("memories-to-store", { deliveryGuarantee: "at-least-once", }); export const remember = api( { expose: true, method: "POST", path: "/agents/:agentID/memories" }, async (params: RememberParams): Promise<{ memoryID: string; status: string }> => { const memoryID = createMemoryID(); await memoriesToStore.publish({ ...params, memoryID, entities: normalizeEntities(params.entities ?? []), }); return { memoryID, status: "queued" }; }, );

The API and topic declarations form Encore's application model, which encore run uses to start them together on a developer's machine. Production configuration lives in the Encore dashboard or the team's existing AWS/GCP tooling.

Encore turns application code into infrastructure provisioned in your own AWS or GCP account.

The backend is built against local infrastructure, then deployed from the same application model into your AWS or GCP account.

Writing vector and graph memory

We'll keep the semantic and relational sides of each memory in one SurrealDB model. The subscriber upserts the memory under a stable ID, then creates mentions relationships to the entities extracted by the caller:

BEGIN; UPSERT $memory CONTENT { agent_id: $agent_id, text: $text, embedding: $embedding, created_at: time::now(), expires_at: $expires_at }; FOR $entity_data IN $entities { LET $entity = type::record("entity", $entity_data.slug); LET $edge = type::record("mentions", [$memory_id, $entity_data.slug]); UPSERT $entity MERGE { name: $entity_data.name }; RELATE OR UPDATE $memory->$edge->$entity SET linked_at = time::now(); }; COMMIT;

The stable record and relationship IDs make the transaction safe to repeat after a pub/sub retry. The graph is stored with the memory, so there is no second store or synchronization process to maintain.

For recall, we'll use an HNSW vector index to find the memories nearest to the current query. The same SurrealQL query traverses the graph and returns the entities attached to each result:

SELECT id, text, created_at, vector::distance::knn() AS distance, ->mentions->entity.name AS entities FROM memory WHERE embedding <|10, 100|> $embedding AND agent_id = $agent_id AND (expires_at = NONE OR expires_at > time::now()) ORDER BY distance LIMIT $limit;

The service can also start from an entity and follow incoming mentions relationships back to every active memory, which is useful when an agent needs context about a person, project, or customer rather than the closest semantic match.

Verifying memory with a coding agent

encore run starts the APIs and pub/sub locally, with SurrealDB running in memory alongside them. From there, we'll store several memories, wait for the subscriber, and recall them by meaning and by entity. A passing check returns the nearest memories in order, keeps one agent's context out of another's results, and removes expired records.

An agent can carry out the same loop through the Encore MCP server, calling the APIs and opening the request traces to verify the running application. The included integration tests use a real SurrealDB instance and replay the same event to confirm that retries leave one memory and one set of relationships.

Prompt
Run encore check and the test suite, then verify the agent memory flow end-to-end against the locally running application and SurrealDB.
Please note

Cron jobs do not fire automatically in local or preview environments, so call the retention API directly during verification to exercise the same handler used by the deployed schedule.

Try it today

You can run the complete example, start with an existing Encore application, or follow the TypeScript quickstart to create one, then give an agent the prompt below:

Prompt
Build me a durable agent memory service with Encore and SurrealDB.

Read the SurrealDB JavaScript SDK documentation for connection and query options, and Encore's application model to see how the APIs and infrastructure around it work across local development and deployment.

Encore

This blog is presented by Encore, automated infrastructure for humans and agents. Let agents build and validate features with real infrastructure in the dev loop, from local dev to production in your cloud on AWS/GCP.

Like this article? Get future ones straight to your mailbox.

You can unsubscribe at any time.