Tutorial – Configuring the schema

Goal for this step 🏁: Configure a schema with an entity type with generated TypeScript types for type safe usage of the API.

In order to be able to create content we first need to create a schema. We can do it in backend/server.ts after creating the server. Using the admin client we create a schema with one entity type, which in turn has a string field.

import { FieldType, type AdminClient } from '@dossierhq/core';

async function updateSchema(adminClient: AdminClient) {
const schemaResult = await adminClient.updateSchemaSpecification({
entityTypes: [
{
name: 'Message',
nameField: 'message',
fields: [{ name: 'message', type: FieldType.String, required: true }],
},
],
});
return schemaResult;
}

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 adminClient = server.createAdminClient(() => initSession);

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

return ok({ server });
}

Next up we generate TypeScript types for the resulting schema to get more feedback from the compiler when interacting with the API.

  • npm install -D @dossierhq/typescript-generator

Add a new script, generate-typescript.ts:

#!/usr/bin/env -S npx tsx
import { Schema, createConsoleLogger, type Logger } from '@dossierhq/core';
import { type Server } from '@dossierhq/server';
import { generateTypescriptForSchema } from '@dossierhq/typescript-generator';
import { writeFile } from 'node:fs/promises';
import { initialize } from './backend/server.js';

async function generateTypes(logger: Logger, schema: Schema, filename: string) {
const publishedSchema = schema.toPublishedSchema();
const sourceCode = generateTypescriptForSchema({
schema,
publishedSchema,
authKeyType: "'none' | 'subject'",
});
await writeFile(filename, sourceCode);
logger.info(`Wrote ${filename}`);
}

async function getAdminSchema(server: Server) {
const initSession = server.createSession({
provider: 'sys',
identifier: 'init',
defaultAuthKeys: [],
logger: null,
databasePerformance: null,
});
const adminClient = server.createAdminClient(() => initSession);
const schemaResult = await adminClient.getSchemaSpecification();
return new Schema(schemaResult.valueOrThrow());
}

async function main() {
const logger = createConsoleLogger(console);
const { server } = (await initialize(logger)).valueOrThrow();

const schema = await getAdminSchema(server);

await generateTypes(logger, schema, './src/SchemaTypes.ts');
await server.shutdown();
}

await main();

Add calling the script to package.json:

{
"scripts": {
"build": "./generate-typescript.ts && tsc && vite build"
}
}

Then running npm run build, you should have a generated file in src/SchemaTypes.ts that includes types for the Message entity type (e.g. AdminMessage and PublishedMessage).

We can now use the AppAdminClient (and in the next step AppPublishedClient) types, from the generated TypeScript file. This helps the compiler to ensure we use the right values for things like entity types and the types of entity fields. In backend/server.ts:

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

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 adminClient = server.createAdminClient<AppAdminClient>(() => initSession);

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

return ok({ server });
}