Skip to main content

Create

GraphQL allows to query and mutate data. Mutations are operations like create, update and delete. This chapter describes how data can be created.

API Approaches

OctoMesh provides two ways to create entities:

ApproachEndpointUse Case
Typedruntime.[typeName].createStrongly typed, IDE autocompletion, compile-time validation
Genericruntime.runtimeEntities.createDynamic type handling, flexible attribute specification

Generic Create Mutation

The generic runtimeEntities.create mutation allows creating entities of any type by specifying the ckTypeId dynamically. This is useful when the type is not known at compile time or when building dynamic applications.

Basic Generic Create

mutation {
runtime {
runtimeEntities {
create(
entities: [
{
ckTypeId: "Basic/TreeNode"
attributes: [
{ attributeName: "name", value: "My Node" }
]
}
]
) {
rtId
}
}
}
}

The result will be:

{
"data": {
"runtime": {
"runtimeEntities": {
"create": [
{
"rtId": "69692194d66195e5364c310f"
}
]
}
}
}
}

Generic Create with Associations

You can create entities with associations to existing entities:

mutation {
runtime {
runtimeEntities {
create(
entities: [
{
ckTypeId: "Basic/TreeNode"
attributes: [
{ attributeName: "name", value: "Child Node" }
]
associations: {
roleName: "parent"
targets: [
{
modOption: CREATE
target: {
ckTypeId: "Basic/Tree"
rtId: "65dc6d24cc529cdc46c84fca"
}
}
]
}
}
]
) {
rtId
}
}
}
}

Generic Create Input Structure

FieldTypeRequiredDescription
ckTypeIdStringYesConstruction Kit type identifier (e.g., Basic/TreeNode)
attributes[AttributeInput]NoArray of attribute name-value pairs
associationsAssociationInputNoAssociation definitions

AttributeInput:

FieldTypeDescription
attributeNameStringName of the attribute
valueSimpleScalarValue to set

AssociationInput:

FieldTypeDescription
roleNameStringName of the association role
targets[AssociationTargetInput]Target entities for the association

AssociationTargetInput:

FieldTypeDescription
modOptionModOptionModification option (CREATE, DELETE)
targetRtEntityIdTarget entity reference with rtId and ckTypeId

Typed Create Mutation

The typed approach uses type-specific endpoints with strongly typed input objects. This provides better IDE support and compile-time validation.

Simple create mutation

The area runtime allows access to entities fo the Runtime Model Let's start with simple sample that requests the name of all energy meters.

mutation {
runtime {
energyCommunityCustomers {
create(
entities: [
{
contact:{
firstName: "John"
lastName: "Doe"
address: {
nationalCode: "AT"
street: "Example Avenue 99"
zipcode: 12345
cityTown: "Testville"
}
}
taxProcedureCreditNote: REVERSE_CHARGE
}
]
) {
rtId
}
}
}
}

This query will create an energy meter object with the given data. The result of the object will be like

{
"data": {
"runtime": {
"energyCommunityCustomers": {
"create": [
{
"rtId": "693c4cd3464d7d9e1396cf0d"
}
]
}
}
}
}

Create with variables

For programmatic use, it is recommended to use GraphQL variables:

mutation createCustomers($entities: [EnergyCommunityCustomerInput]!) {
runtime {
energyCommunityCustomers {
create(entities: $entities) {
rtId
}
}
}
}

Variables:

{
"entities": [
{
"contact": {
"firstName": "John",
"lastName": "Doe",
"address": {
"nationalCode": "AT",
"street": "Example Avenue 99",
"zipcode": 12345,
"cityTown": "Testville"
}
},
"taxProcedureCreditNote": "REVERSE_CHARGE"
}
]
}

Batch create

Multiple entities can be created in a single request by providing multiple entries in the entities array:

mutation {
runtime {
energyCommunityCustomers {
create(
entities: [
{
state: ACTIVE
contact:{
firstName: "John"
lastName: "Doe"
address: {
nationalCode: "AT"
street: "Example Avenue 99"
zipcode: 1010
cityTown: "Wien"
}
}
taxProcedureCreditNote: REVERSE_CHARGE
},
{
state: ACTIVE
contact:{
firstName: "Jane"
lastName: "Smith"
address: {
nationalCode: "DE"
street: "Sample Street 42"
zipcode: 10115
cityTown: "Berlin"
}
}
taxProcedureCreditNote: NO_TAX_PROCEDURE
},
{
state: ACTIVE
contact:{
firstName: "Max"
lastName: "Mustermann"
address: {
nationalCode: "AT"
street: "Musterstraße 12"
zipcode: 4020
cityTown: "Linz"
}
}
taxProcedureCreditNote: REVERSE_CHARGE
},
{
state: INACTIVE
contact:{
firstName: "Erika"
lastName: "Musterfrau"
address: {
nationalCode: "DE"
street: "Beispielweg 7"
zipcode: 80331
cityTown: "München"
}
}
taxProcedureCreditNote: NO_TAX_PROCEDURE
},
{
state: INACTIVE
contact:{
firstName: "Paul"
lastName: "Example"
address: {
nationalCode: "CH"
street: "Testgasse 5"
zipcode: 8001
cityTown: "Zürich"
}
}
taxProcedureCreditNote: REVERSE_CHARGE
}
]
) {
rtId
}
}
}
}

This will create two separate entities in a single mutation call. The result will be like

{
"data": {
"runtime": {
"energyCommunityCustomers": {
"create": [
{
"rtId": "693c4cd3464d7d9e1396cf0d"
},
{
"rtId": "693c4cd3464d7d9e1396cf0e"
}
]
}
}
}
}

Create with associations

When creating entities that have associations to other entities, you can specify the associated entities using their rtId and ckTypeId.

mutation {
runtime {
energyCommunityOperatingFacilitys {
create(
entities: [
{
state: ACTIVE
facilityType: HOUSEHOLD
name: "Demo"
address: {
nationalCode: "AT"
street: "Example Avenue 99"
zipcode: 1010
cityTown: "Wien"
},
parent:[{modOption:CREATE, target: {rtId:"5fc8fda18b2fc75f925e21ac", ckTypeId: "Basic/TreeNode"}}]
customer: [{modOption:CREATE, target:{rtId:"693c4cd3464d7d9e1396cf0d", ckTypeId: "EnergyCommunity/Customer"}}]
}
]
) {
rtId
}
}
}
}

This mutation creates an OperatingFacility entity and associates it with an existing TreeNode and Customer entity.

The result will be like

{
"data": {
"runtime": {
"energyCommunityOperatingFacilitys": {
"create": [
{
"rtId": "693c5b93464d7d9e1396cf1c"
}
]
}
}
}
}

Special Scalar Types

TimeSpan Attributes

TimeSpan attributes store duration/time interval values. In GraphQL, TimeSpan values are represented as seconds (decimal number).

Example Model

The examples below use the OctoSdkDemo/MeteringPoint type from the Octo.Sdk.Demo Construction Kit, which includes:

  • dataTransmissionInterval - TimeSpan (duration in seconds)

Typed Create with TimeSpan

mutation {
runtime {
octoSdkDemoMeteringPoints {
create(
entities: [
{
name: "Main Building Meter"
meteringPointNumber: "MP-2024-001"
meterReading: 12500
operatingStatus: OK
# TimeSpan: 15 minutes = 900 seconds
dataTransmissionInterval: 900
# MeteringPoint requires a parent association
parent: [{modOption: CREATE, target: {ckTypeId: "Basic/TreeNode", rtId: "65dc6d24cc529cdc46c84fcb"}}]
}
]
) {
rtId
name
meteringPointNumber
dataTransmissionInterval
}
}
}
}

Generic Create with TimeSpan

mutation {
runtime {
runtimeEntities {
create(
entities: [
{
ckTypeId: "OctoSdkDemo/MeteringPoint"
attributes: [
{ attributeName: "name", value: "Warehouse Meter" }
{ attributeName: "meteringPointNumber", value: "MP-2024-002" }
{ attributeName: "meterReading", value: 8750 }
{ attributeName: "operatingStatus", value: "OK" }
# TimeSpan: 1 hour = 3600 seconds
{ attributeName: "dataTransmissionInterval", value: 3600 }
]
# MeteringPoint requires a parent association
associations: {
roleName: "parent"
targets: [{
modOption: CREATE
target: { ckTypeId: "Basic/TreeNode", rtId: "65dc6d24cc529cdc46c84fcb" }
}]
}
}
]
) {
rtId
attributes(first: 10) {
items {
attributeName
value
}
}
}
}
}
}

Common TimeSpan Values

DurationSecondsUse Case
1 minute60High-frequency monitoring
5 minutes300Standard monitoring
15 minutes900Energy metering intervals
1 hour3600Hourly reports
1 day86400Daily aggregation

Complex Attribute Types

OctoMesh supports complex attribute types that allow structured data within entities. These include Record, RecordArray, Binary, and BinaryLinked types.

Example Model

The examples below use the OctoSdkDemo/Customer type from the Octo.Sdk.Demo Construction Kit, which includes all complex attribute types:

  • contact - Record (with nested address Record)
  • bankAccount - Record
  • customerStatus - Enum
  • notes - RecordArray of CustomerNote
  • profilePicture - Binary (inline binary data)
  • contractDocument - BinaryLinked (external file reference)

Record Attributes

Record attributes store structured data as embedded objects. They are defined in the Construction Kit and contain their own set of attributes. Records can be nested (e.g., Contact contains an Address record).

Typed Create with Record

mutation {
runtime {
octoSdkDemoCustomers {
create(
entities: [
{
customerStatus: ACTIVE
dateOfBirth: "1985-03-15T00:00:00Z"
contact: {
legalEntityType: NATURAL_PERSON
firstName: "John"
lastName: "Doe"
email: "john.doe@example.com"
address: {
street: "123 Main Street"
zipcode: 10115
cityTown: "Berlin"
nationalCode: "DE"
}
}
bankAccount: {
iban: "DE89370400440532013000"
swiftCode: "COBADEFFXXX"
accountHolder: "John Doe"
}
}
]
) {
rtId
customerStatus
contact {
firstName
lastName
email
address {
street
cityTown
}
}
bankAccount {
iban
accountHolder
}
}
}
}
}

Generic Create with Record

When using the generic API, record values are passed as nested objects in the value field:

mutation {
runtime {
runtimeEntities {
create(
entities: [
{
ckTypeId: "OctoSdkDemo/Customer"
attributes: [
{ attributeName: "customerStatus", value: "ACTIVE" }
{ attributeName: "dateOfBirth", value: "1985-03-15T00:00:00Z" }
{
attributeName: "contact"
value: {
legalEntityType: "NATURAL_PERSON"
firstName: "John"
lastName: "Doe"
email: "john.doe@example.com"
address: {
street: "123 Main Street"
zipcode: 10115
cityTown: "Berlin"
nationalCode: "DE"
}
}
}
]
}
]
) {
rtId
attributes(first: 10) {
items {
attributeName
value
}
}
}
}
}
}
Nested Records

Record attributes can contain other Record attributes (nested records). In the example above, the contact record contains an address record. This nesting works with both:

  • Inline literals: Nested objects directly in the GraphQL mutation
  • Variables: Nested objects passed via JSON variables

The structure must match the Construction Kit model definition.

RecordArray Attributes

RecordArray attributes store arrays of structured records, useful for lists of notes, history entries, or other repeated structures.

Typed Create with RecordArray

The OctoSdkDemo/Customer type has a notes attribute that stores an array of CustomerNote records:

mutation {
runtime {
octoSdkDemoCustomers {
create(
entities: [
{
customerStatus: ACTIVE
contact: {
legalEntityType: NATURAL_PERSON
firstName: "Jane"
lastName: "Smith"
address: {
street: "456 Oak Avenue"
zipcode: 80331
cityTown: "Munich"
nationalCode: "DE"
}
}
notes: [
{
date: "2024-01-15T10:30:00Z"
text: "Initial contact - interested in premium plan"
author: "Sales Team"
category: "Sales"
}
{
date: "2024-01-20T14:00:00Z"
text: "Contract signed"
author: "Account Manager"
category: "Contract"
}
{
date: "2024-02-01T09:00:00Z"
text: "Onboarding completed successfully"
author: "Support Team"
category: "Onboarding"
}
]
}
]
) {
rtId
contact {
firstName
lastName
}
notes {
date
text
author
category
}
}
}
}
}

Generic Create with RecordArray

mutation {
runtime {
runtimeEntities {
create(
entities: [
{
ckTypeId: "OctoSdkDemo/Customer"
attributes: [
{ attributeName: "customerStatus", value: "ACTIVE" }
{
attributeName: "contact"
value: {
legalEntityType: "NATURAL_PERSON"
firstName: "Jane"
lastName: "Smith"
address: {
street: "456 Oak Avenue"
zipcode: 80331
cityTown: "Munich"
nationalCode: "DE"
}
}
}
{
attributeName: "notes"
value: [
{
date: "2024-01-15T10:30:00Z"
text: "Initial contact"
author: "Sales Team"
category: "Sales"
}
{
date: "2024-01-20T14:00:00Z"
text: "Contract signed"
author: "Account Manager"
}
]
}
]
}
]
) {
rtId
attributes(first: 10) {
items {
attributeName
value
}
}
}
}
}
}

Binary Attributes

Binary attributes store small binary data directly within the entity as inline byte arrays. This is ideal for small files like profile pictures, icons, or cached data where external file storage would be overkill.

When to use Binary vs BinaryLinked
  • Binary: Small data (< 16MB), stored inline, immediate access, no separate download required
  • BinaryLinked: Large files, stored externally, requires separate download via REST API

Understanding Binary Data in GraphQL

Binary data is represented as an array of integers (0-255) in GraphQL. When creating or updating entities, you pass the binary data as a JSON array of byte values.

Typed Create with Binary

mutation {
runtime {
octoSdkDemoCustomers {
create(
entities: [
{
customerStatus: ACTIVE
contact: {
legalEntityType: NATURAL_PERSON
firstName: "John"
lastName: "Doe"
address: {
street: "123 Main Street"
zipcode: 10115
cityTown: "Berlin"
nationalCode: "DE"
}
}
# Binary attribute - profile picture (PNG header bytes as example)
profilePicture: [137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82]
}
]
) {
rtId
contact {
firstName
lastName
}
profilePicture
}
}
}
}

Generic Create with Binary

mutation {
runtime {
runtimeEntities {
create(
entities: [
{
ckTypeId: "OctoSdkDemo/Customer"
attributes: [
{ attributeName: "customerStatus", value: "ACTIVE" }
{
attributeName: "contact"
value: {
legalEntityType: "NATURAL_PERSON"
firstName: "John"
lastName: "Doe"
address: {
street: "123 Main Street"
zipcode: 10115
cityTown: "Berlin"
nationalCode: "DE"
}
}
}
{
attributeName: "profilePicture"
value: [137, 80, 78, 71, 13, 10, 26, 10]
}
]
}
]
) {
rtId
attributes(first: 10) {
items {
attributeName
value
}
}
}
}
}
}

Working with Binary Data in Code

JavaScript/TypeScript:

// Convert a file to byte array
async function fileToByteArray(file) {
const arrayBuffer = await file.arrayBuffer();
return Array.from(new Uint8Array(arrayBuffer));
}

// Convert Base64 to byte array
function base64ToByteArray(base64) {
const binaryString = atob(base64);
return Array.from(binaryString, char => char.charCodeAt(0));
}

// Usage with GraphQL mutation
const file = document.getElementById('profilePictureInput').files[0];
const profilePictureBytes = await fileToByteArray(file);

await client.mutate({
mutation: CREATE_CUSTOMER,
variables: {
entities: [{
customerStatus: "ACTIVE",
contact: { /* ... */ },
profilePicture: profilePictureBytes
}]
}
});

C# / .NET:

// Convert file to byte array for GraphQL
byte[] profilePictureBytes = await File.ReadAllBytesAsync("profile.png");

// The SDK handles byte[] automatically
var customer = new CustomerInput
{
CustomerStatus = CustomerStatus.Active,
Contact = new ContactInput { /* ... */ },
Thumbnail = profilePictureBytes
};

Querying Binary Attributes

Binary data is returned as an array of integers:

query {
runtime {
octoSdkDemoCustomers(first: 10) {
items {
rtId
contact {
firstName
lastName
}
profilePicture
}
}
}
}

Response:

{
"data": {
"runtime": {
"octoSdkDemoCustomers": {
"items": [
{
"rtId": "693c5b93464d7d9e1396cf1c",
"contact": {
"firstName": "John",
"lastName": "Doe"
},
"profilePicture": [137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82]
}
]
}
}
}
}

Converting response to usable format in JavaScript:

// Convert byte array to Base64 for display
function byteArrayToBase64(bytes) {
const binaryString = String.fromCharCode(...bytes);
return btoa(binaryString);
}

// Display as image
const base64 = byteArrayToBase64(customer.profilePicture);
const imgSrc = `data:image/png;base64,${base64}`;

BinaryLinked Attributes

BinaryLinked attributes store references to binary files (images, documents, etc.). Files are uploaded via GraphQL Multipart Request as part of the mutation and can be downloaded via the REST API.

The OctoSdkDemo/Customer type has a contractDocument attribute for storing contract PDFs.

Uploading Files via GraphQL Multipart Request

Binary files are uploaded using the GraphQL Multipart Request Specification. This allows file uploads directly within a GraphQL mutation.

Example using curl:

curl -X POST "https://api.example.com/{tenantId}/graphql" \
-H "Authorization: Bearer {token}" \
-F 'operations={"query":"mutation($entities: [OctoSdkDemoCustomerInput!]!) { runtime { octoSdkDemoCustomers { create(entities: $entities) { rtId contractDocument { binaryId filename } } } } }","variables":{"entities":[{"customerStatus":"ACTIVE","contact":{"legalEntityType":"NATURAL_PERSON","firstName":"John","lastName":"Doe","address":{"street":"Main St","zipcode":10115,"cityTown":"Berlin","nationalCode":"DE"}},"contractDocument":null}]}}' \
-F 'map={"0":["variables.entities.0.contractDocument"]}' \
-F '0=@contract.pdf'

The multipart request consists of three parts:

  1. operations - The GraphQL query/mutation with variables (file variables set to null)
  2. map - Maps file indices to variable paths
  3. 0, 1, ... - The actual file data

Example using JavaScript (Apollo Client with apollo-upload-client):

import { createUploadLink } from 'apollo-upload-client';
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const client = new ApolloClient({
link: createUploadLink({ uri: 'https://api.example.com/{tenantId}/graphql' }),
cache: new InMemoryCache()
});

const CREATE_CUSTOMER = gql`
mutation CreateCustomer($entities: [OctoSdkDemoCustomerInput!]!) {
runtime {
octoSdkDemoCustomers {
create(entities: $entities) {
rtId
contact {
firstName
lastName
}
contractDocument {
binaryId
filename
contentType
size
downloadUri
}
}
}
}
}
`;

// File from input element
const file = document.getElementById('contractFile').files[0];

await client.mutate({
mutation: CREATE_CUSTOMER,
variables: {
entities: [{
customerStatus: "ACTIVE",
contact: {
legalEntityType: "NATURAL_PERSON",
firstName: "John",
lastName: "Doe",
address: {
street: "123 Main Street",
zipcode: 10115,
cityTown: "Berlin",
nationalCode: "DE"
}
},
contractDocument: file // Pass the File object directly
}]
}
});

Downloading Files via REST API

Binary files can be downloaded using the REST endpoint with the binaryId from the query response:

GET /{tenantId}/v1/largeBinaries?largeBinaryId={binaryId}

The downloadUri field in queries already contains the full URL for convenience.

Querying BinaryLinked Attributes

When querying entities with BinaryLinked attributes, you get metadata and a download URI:

query {
runtime {
octoSdkDemoCustomers(first: 10) {
items {
rtId
contact {
firstName
lastName
}
contractDocument {
binaryId
filename
contentType
size
downloadUri
}
}
}
}
}

Response:

{
"data": {
"runtime": {
"octoSdkDemoCustomers": {
"items": [
{
"rtId": "693c5b93464d7d9e1396cf1c",
"contact": {
"firstName": "John",
"lastName": "Doe"
},
"contractDocument": {
"binaryId": "693c5b93464d7d9e1396cf20",
"filename": "contract-doe-2024.pdf",
"contentType": "application/pdf",
"size": 245678,
"downloadUri": "https://api.example.com/tenant1/v1/largeBinaries?largeBinaryId=693c5b93464d7d9e1396cf20"
}
}
]
}
}
}
}

Combined Example with All Complex Types

Creating a customer with all complex attribute types (Record, RecordArray, BinaryLinked):

mutation {
runtime {
octoSdkDemoCustomers {
create(
entities: [
{
customerStatus: ACTIVE
dateOfBirth: "1980-06-15T00:00:00Z"
phoneNumberMobile: "+49 170 1234567"
phoneNumberLandLine: "+49 30 9876543"
# Record attribute with nested Record
contact: {
legalEntityType: NATURAL_PERSON
salutation: "Mr."
firstName: "Max"
lastName: "Mustermann"
email: "max.mustermann@example.com"
address: {
street: "Musterstraße 42"
zipcode: 10115
cityTown: "Berlin"
nationalCode: "DE"
}
}
# Another Record attribute
bankAccount: {
iban: "DE89370400440532013000"
swiftCode: "COBADEFFXXX"
accountHolder: "Max Mustermann"
}
# RecordArray attribute
notes: [
{
date: "2024-01-10T09:00:00Z"
text: "Customer registered via web portal"
author: "System"
category: "Registration"
}
{
date: "2024-01-12T11:30:00Z"
text: "Identity verification completed"
author: "Compliance Team"
category: "Verification"
}
{
date: "2024-01-15T14:00:00Z"
text: "Contract signed and activated"
author: "Sales Manager"
category: "Contract"
}
]
# BinaryLinked attribute (uploaded via multipart request)
# contractDocument: <file>
}
]
) {
rtId
customerStatus
dateOfBirth
contact {
firstName
lastName
email
address {
street
cityTown
nationalCode
}
}
bankAccount {
iban
accountHolder
}
notes {
date
text
author
category
}
contractDocument {
binaryId
filename
downloadUri
}
}
}
}
}