Tutorial – Managing entities programmatically

Goal for this step 🏁: Use the Dossier client to create and publish Message entities during startup of the backend and use the published client to deliver a random Message entity to the frontend.

Before creating some entities, we use the API call getEntitiesTotalCount() to check how many messages exist already so we don't create too many messages.

When creating an entity we provide the entity type, auth key, name and message field. We also specify that we want to publish the entity as an atomic operation when creating the entity (if the publishing would fail, e.g. if we forget to provide the required message field, no entity would be created). In backend/server.ts:

async function createMessages(logger: Logger, client: AppDossierClient) {
const totalMessageCountResult = await client.getEntitiesTotalCount({
entityTypes: ['Message'],
authKeys: ['none'],
});
if (totalMessageCountResult.isError()) return totalMessageCountResult;

const desiredMessageCount = 10;
for (let i = totalMessageCountResult.value; i < desiredMessageCount; i++) {
const message = `Message ${i}!`;
const createResult = await client.createEntity(
{
info: { type: 'Message', authKey: 'none', name: message },
fields: { message },
},
{ publish: true }
);
if (createResult.isError()) return createResult;
const entity = createResult.value.entity;

logger.info('Created message, id=%s', entity.id);
}

return ok(undefined);
}

export async function initialize(logger: Logger) {
const databaseResult = await initializeDatabase(logger);
if (databaseResult.isError()) return databaseResult;

const serverResult = await initializeServer(logger, databaseResult.value);
if (serverResult.isError()) return serverResult;
const server = serverResult.value;

const initSession = server.createSession({
provider: 'sys',
identifier: 'init',
defaultAuthKeys: [],
logger: null,
databasePerformance: null,
});
const client = server.createDossierClient<AppDossierClient>(() => initSession);

const schemaResult = await updateSchema(client);
if (schemaResult.isError()) return schemaResult;

const messageCreateResult = await createMessages(logger, client);
if (messageCreateResult.isError()) return messageCreateResult;

return ok({ server });
}

Next time the server start (which nodemon does automatically) ten entities are created and published:

[be] info: Created message, id=d3cd4330-ca5a-420e-b9ba-faf93f34f085
[be] info: Created message, id=6320e6c4-8cf5-4888-90d7-946519f8c035
[be] info: Created message, id=758243d8-39bb-4a25-beaf-20e0cf7e1599
[be] info: Created message, id=92beb4d9-b98c-40bc-bab7-8aba82c31e6f
[be] info: Created message, id=e5b9d8ca-a116-4edd-ad45-b73a90c678fd
[be] info: Created message, id=872c2143-977f-443d-8ba2-b8615940b7a6
[be] info: Created message, id=7187bf7d-80a3-4d24-bac4-58838f98e994
[be] info: Created message, id=e2d9c0a8-e183-4fe5-93ee-ec3b16552ea2
[be] info: Created message, id=7ea15e3a-2b1b-4503-b5e5-de7737ebfb56
[be] info: Created message, id=231e525a-8dda-48ff-a3fa-cf3015c26257

We can now change /api/message to return a random published message, using a published client. Later on we will add proper authentication, but for now we create a session for an anonymous user for each HTTP request. In backend/server.ts:

import type { AppPublishedDossierClient } from '../src/SchemaTypes.js';

async function createSessionForRequest(server: Server, req: Request) {
const provider = 'sys';
const identifier = 'anonymous';
// TODO add authentication
return await server.createSession({
provider,
identifier,
defaultAuthKeys: ['none', 'subject'],
logger: null,
databasePerformance: null,
});
}

export function getPublishedClientForRequest(server: Server, req: Request) {
const session = createSessionForRequest(server, req);
return server.createPublishedDossierClient<AppPublishedDossierClient>(() => session);
}

And in backend/main.ts:

import { RequestHandler } from 'express';
import { getPublishedClientForRequest } from './server.js';

function asyncHandler(handler: (...args: Parameters<RequestHandler>) => Promise<void>) {
return (...args: Parameters<RequestHandler>) => {
return handler(...args).catch(args[2]);
};
}

app.get(
'/api/message',
asyncHandler(async (req, res) => {
const publishedClient = getPublishedClientForRequest(serv er, req);
const samples = (
await publishedClient.getEntitiesSample({ entityTypes: ['Message'] }, { count: 1 })
).valueOrThrow();
const message = samples.items[0];
res.send({ message: message.fields.message });
})
);

We'll now see a random message in the frontend.

The frontend showing a random message