Encore is a platform that lets engineers and their agents build and verify complete backends using type-safe APIs and infrastructure primitives defined in application code, backed by local infrastructure during development and managed services in their AWS or GCP account when deployed. In this guide, those primitives define and connect the APIs, Pub/Sub ingestion, and scheduled retention workflow around SurrealDB's vector and graph capabilities.
The example application stores durable memory for AI agents. Each memory contains text and a vector embedding, with graph relationships connecting it to the entities it mentions. Memories are written asynchronously through Pub/Sub, recalled through vector similarity or graph traversal, and removed by a scheduled retention job.
Run the example
You need the Encore CLI, Docker and Node.js 22 or later.
Using a coding agent? Copy the prompt below to skip the manual setup.
Set up and run the Encore and SurrealDB agent memory example. First check whether the Encore CLI is installed and, if needed, follow https://encore.dev/docs/install. Then run `encore app create agent-memory --example=ts/surrealdb-agent-memory`, follow the example README to start SurrealDB and the Encore application locally, verify that storing and recalling a memory works, and run `encore check` and `encore test`.Start a local SurrealDB 3.3 instance:
docker run --rm --name encore-surrealdb-memory \
-p 8000:8000 \
surrealdb/surrealdb:v3.3.0 \
start --user root --pass secret memoryThis starts a temporary SurrealDB instance at http://localhost:8000. Its data is discarded when the container stops.
In another terminal, create and run the example application:
encore app create agent-memory --example=ts/surrealdb-agent-memory
cd agent-memory
npm install
encore runEncore starts the APIs and local Pub/Sub and cron infrastructure. The application connects to the SurrealDB container as root with the password secret.
How the integration works
The remember API accepts memory text, its embedding, extracted entity names and an optional expiry time. It publishes the request to an at-least-once Encore Pub/Sub topic, whose subscriber writes the memory and its mentions relationships to SurrealDB.
The API, topic and subscriber are defined together in the application code, allowing Encore to connect the ingestion flow locally and provision it using the corresponding managed services when deployed:
import { randomUUID } from "node:crypto";
import { api } from "encore.dev/api";
import { CronJob } from "encore.dev/cron";
import { Subscription, Topic } from "encore.dev/pubsub";
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 = `m_${randomUUID().replaceAll("-", "")}`;
await memoriesToStore.publish({
...params,
memoryID,
entities: normalizeEntities(params.entities ?? []),
});
return { memoryID, status: "queued" };
},
);
new Subscription(memoriesToStore, "store-memory-in-surrealdb", {
handler: async (event) => storeMemory(await getSurreal(), event),
});Stable record and relationship IDs make this write safe to repeat if Pub/Sub retries an event. Recall can then combine an HNSW vector search with graph traversal in one SurrealQL query:
SELECT id, text, created_at, expires_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;An Encore cron job calls the retention API every 24 hours to delete expired memories:
export const pruneExpired = api(
{ expose: true, method: "POST", path: "/memories/prune" },
async (): Promise<{ deleted: number }> => ({
deleted: await pruneExpiredMemories(await getSurreal()),
}),
);
new CronJob("prune-agent-memory", {
title: "Remove expired agent memories",
every: "24h",
endpoint: pruneExpired,
});The complete example contains the remaining API definitions, retry-safe transaction, schema and tests.
Store and recall a memory
Store a memory for an agent:
curl -X POST http://localhost:4000/agents/research-agent/memories \
-H 'Content-Type: application/json' \
-d '{
"text": "SurrealDB stores vector and graph context for the agent.",
"embedding": [0.94, 0.06, 0, 0, 0, 0, 0, 0],
"entities": ["SurrealDB", "Encore"]
}'The API returns a generated memoryID and the status queued. Once the subscriber has written the event, recall the nearest memories:
curl -X POST http://localhost:4000/agents/research-agent/recall \
-H 'Content-Type: application/json' \
-d '{
"embedding": [0.95, 0.05, 0, 0, 0, 0, 0, 0],
"limit": 5
}'The example uses eight-dimensional vectors to keep requests readable. Change both EMBEDDING_DIMENSIONS and the HNSW index dimension before connecting a production embedding model.
Run the checks and tests with:
encore check
encore testConnect to SurrealDB Cloud
Set the SurrealDB endpoint and database token as Encore secrets before deploying the application:
encore secret set --type dev,prod SurrealDBURL
encore secret set --type dev,prod SurrealDBTokenSurrealDBToken must be a database token with access to the encore namespace and agent_memory database. A personal access token beginning with sdbp_ authenticates the SurrealDB Cloud control plane and cannot be used as the application's database token.