Build an app for end-users to create and manage dynamic schemas
This guide provides a scenario-based walk-through for implementing a backend that allows end users to create, manage, and publish schemas from a custom frontend app. The focus is on the backend logic and the SDK methods that enable this capability.
This guide assumes your frontend app provides a UI for defining a schema (for example, a form builder) and that this UI can send a ClaimsSchema object to your backend.
Part 1: Schema creation workflow
Step 1: Create a new empty schema
The first step is to create a new credential schema resource on the Truvity platform. At this point, the schema is empty and the app needs to populate it as the user adds fields in the UI. Use the SchemaCreate method for this purpose.
- Java
ResourceCredentialSchema createdSchema = client.schemas()
.schemaCreate(SchemaCreateRequest.builder()
.data(CredentialSchemaCreate.builder()
.schema(ClaimsSchemaOptional.builder().build())
.build())
.build());
Step 2: Handle schema updates
As the user interacts with the UI by adding or removing fields, you can handle schema updates in the backend. Use the SchemaUpdate method to apply incremental changes to the credential schema resource by providing the updated ClaimsSchema.
- Java
ResourceCredentialSchema updatedSchema = client.schemas()
.schemaUpdate(
createdSchema.getId(),
SchemaUpdateRequest.builder()
.ifMatch(createdSchema.getEtag())
.data(CredentialSchemaUpdate.builder()
.schema(ClaimsSchema.builder()
.addFields(ClaimsSchemaFieldsItem.string(
ClaimsSchemaFieldStringValue.builder()
.name("name")
.notEmpty(true)
.build()))
.addFields(ClaimsSchemaFieldsItem.number(
ClaimsSchemaFieldNumberValue.builder()
.name("year_of_birth")
.notEmpty(true)
.build()))
.build())
.build())
.build());
Step 3: Publish the schema
Once the user finalizes the schema in the UI, you can publish it using the SchemaPublish method. This action makes the schema immutable and publicly accessible. The user can provide the slug and version in the UI or the app can automatically generate them.
- Java
ResourcePublishedCredentialSchema publishedSchema = client.schemas()
.schemaPublish(
updatedSchema.getId(),
SchemaPublishRequest.builder()
.slug(slug)
.version(version)
.build());
Part 2: Working with the created schema
Step 4: Create a new draft
After the app publishes the schema, the user can use it to create a new credential draft. This example shows how you can create a draft from the previously created schema.
- Java
ResourceDraft createdDraft = client.drafts()
.draftCreate(DraftCreateRequest.builder()
.data(DraftCreate.builder()
.metaSchema(DraftCreateMetaSchema.of(
publishedSchema.getData().getUrl()))
.build())
.build());
In some cases you need to use a schema from a different tenant when creating a draft, for example when one organization defines structure for credentials it wants to receive. In this case, specify the URL of the schema from another tenant:
- Java
ResourceDraft createdDraft = client.drafts()
.draftCreate(DraftCreateRequest.builder()
.data(DraftCreate.builder()
.metaSchema(DraftCreateMetaSchema.of(
"https://ssi.truvity.com/tenants/{tenant_id}/meta-schema/{schema_slug}/v{schema_version}")
.build())
.build());
Step 5: Populate the draft with claims
The example below shows how the app can populate credential claims in the draft as the user fills in the data in the UI.
- Java
client.drafts()
.draftUpdate(
createdDraft.getId(),
DraftUpdateRequest.builder()
.ifMatch(createdDraft.getEtag())
.data(DraftUpdate.builder()
.values(Map.of(
"name",
CredentialClaimValue.string(StringClaimValue.builder()
.value("John Doe")
.build()),
"year_of_birth",
CredentialClaimValue.number(NumberClaimValue.builder()
.value(1985)
.build())))
.build())
.build());
Step 6: Issue the credential and retrieve credential claims
Once the user completes the draft, you can issue the credential. The draftLatestIssue method makes the credential immutable and verifiable.
You can then use the CredentialClaimValues method to retrieve the credential claims in a format-independent way for display in the user interface.
- Java
var issuedVc = client.drafts()
.draftLatestIssue(
createdDraft.getId(),
DraftLatestIssueRequest.builder()
.keyId(privateKeyId)
.build());
var issuedVcClaims = client.credentials().getCredentialClaimValues(issuedVc.getId());
Part 3: Schema evolution workflow
This workflow describes how your backend can handle the evolution of an existing, published schema. It starts with displaying an existing schema in the UI, allowing the user to make changes, and then publishing a new version of the schema.
Step 7: Search for and display an existing schema
When a user selects an existing schema from the UI (for example, Payment/v1), your backend first searches and retrieves it. Use the PublishedSchemaSearch method to find a schema by its slug and version, or the PublishedSchemaLatest method to retrieve it by its ID.
- Java
ListPublishedCredentialSchema foundPublishedSchemas = client.publishedSchemas()
.publishedSchemaSearch(PublishedSchemaSearchRequest.builder()
.filter(List.of(
PublishedSchemaBaseFilter.builder()
.data(PublishedSchemaFilterData.builder()
.slug(PublishedSchemaFilterDataSlug.equal(slug))
.build())
.build(),
PublishedSchemaBaseFilter.builder()
.data(PublishedSchemaFilterData.builder()
.version(PublishedSchemaFilterDataVersion.equal(version))
.build())
.build()))
.build());
ResourcePublishedCredentialSchema publishedSchema = foundPublishedSchemas.getItems().get(0);
// alternatively
ResourcePublishedCredentialSchema publishedSchema =
client.publishedSchemas().publishedSchemaLatest(schemaId);
Step 8: Copy and update the new schema
When the user wants to modify the schema in the UI (for example, to add a new field), your backend should create a new credential schema resource based on the original. This adheres to the principle of immutability. Use the SchemaCreate method with the fromPublishedMetaSchemaUrl parameter for this purpose. Use the SchemaUpdate method to apply the changes from the UI.
- Java
ResourceCredentialSchema copiedSchema = client.schemas()
.schemaCreate(SchemaCreateRequest.builder()
.data(CredentialSchemaCreate.builder()
.fromPublishedSchemaUrl(publishedSchema.getData().getUrl())
.build())
.build());
client.schemas()
.schemaUpdate(
copiedSchema.getId(),
SchemaUpdateRequest.builder()
.ifMatch(copiedSchema.getEtag())
.data(CredentialSchemaUpdate.builder()
.schema(updatedClaimsSchema)
.build())
.build());
Step 9: Publish the new schema version
Once the user finalizes their changes, they can publish the new schema as a new version. Use the schemaPublish method for this purpose. The user can either provide a new version number in the UI or the app can append it automatically.
- Java
ResourcePublishedCredentialSchema publishedCopiedSchema = client.schemas()
.schemaPublish(
copiedSchema.getId(),
SchemaPublishRequest.builder()
.slug(slug)
.version(version)
.build());
What's next
After you create and publish a schema, your app's backend is responsible for implementing the subsequent business logic. Truvity provides the tools to create and manage schemas, but the processing and validation of credential data against the schema are specific to your use case.
Your next steps typically involve:
- Retrieve Credential Claims: Once a Verifiable Credential (VC) is created, retrieve its claim values.
- Implement Business Logic: Process and validate the data according to your app's requirements. This might include comparing the claims of the VC against the schema definitions, running custom validation rules, or performing other logic specific to your business needs.
This business-specific logic must be coded within your app.
Further reading
- Create a schema - Learn how to create schemas programmatically with UDTs.
- Use an external schema - Learn how to handle schemas from external sources.