I have a .NET Core API app that allow users to create models with specific schema (much like common Headless CMS). The models act as Template for contents.
The content created on top of models are stored in NoSQL Database and the class that owns the content is something like this:
public class Content
{
public string Id { get; set; }
public IDictionary<string, object> Data { get; set; }
}
with this structure I am able to serve via API all types of contents configured in the system in a JSON format, so I can have for example a Product content that is represented like this:
{
"id": "625ea48672b93f0d68a9c886",
"data": {
"code": "ABC",
"has-serial-number": true,
"in-catalogue": true,
"in-customer-care": true,
"name": "Product Name",
"main-image": {
"id": "32691756a12ac10a5983f845",
"code": "fjgq7ur3OGo",
"type": "image",
"originalFileName": "myimage.png",
"format": "png",
"mimeType": "image/png",
"size": 4983921,
"url": "https://domain/imageurl.xyz",
"height": 1125,
"width": 2436,
"metadata": {
"it-IT": {
"title": "Image Title"
}
}
},
"main-gallery": [{
"id": "62691756a57cc10a5983f845",
"code": "fjgq7ur3OGo",
"type": "image",
"originalFileName": "myimage.png",
"format": "png",
"mimeType": "image/png",
"size": 4983921,
"url": "https://domain/imageurl.xyz",
"height": 1125,
"width": 2436,
"metadata": {
"it-IT": {
"title": "Image Title"
}
}
}
],
"thumbnail-video": null,
"subtitle": "Product subtitle",
"description": "Product description",
"brochure": true,
"category": [{
"id": "525964hfwhh0af373ef6",
"data": {
"name": "Category Name",
"appMenuIcon": {
"id": "62691756a57cc10a5983f845",
"code": "fjgq7ur3OGo",
"type": "image",
"originalFileName": "mycategoryimage.png",
"format": "png",
"mimeType": "image/png",
"size": 4983921,
"url": "https://domain/imageurl.xyz",
"height": 1125,
"width": 2436,
"metadata": {
"it-IT": {
"title": "Image title"
}
}
},
"subtitle": "Category subtitle",
"media": "6258058f632b390a189402df",
"content": "Category description"
}
},
],
}}
or can be a Post like this
{
"id": "6270f5f63934a209c0f0f9a2",
"data": {
"title": "Post title",
"date": "2022-04-28T00:00:00+00:00",
"type": "News",
"content": "Post content",
"author": "Author name",
"tags": ["tag1","tag2","tag3"],
"media": {
"id": "6270f5f03934a209c0f0f9a1",
"code": "ZvFuBP4Ism9",
"type": "image",
"originalFileName": "image.jpg",
"format": "jpg",
"mimeType": "image/jpeg",
"size": 329571,
"url": "https://domain/imageurl.xyz",
"height": 1609,
"width": 2560,
"metadata": {
"it-IT": {
"title": "image title"
}
}
},
"linkWebSite": "link to web"
}
}
and so on.
Each type of model has is storeod in its own collection on the NoSQL DB and when I query them, my repository accept the model type to know from which collection get the content.
Everything is fine so far and I can serve this contents via API.
Now, my customer asked me to add a GraphQL layer only for querying documents (so no mutations and not subscriptions).
I tried to check HotChocolate and graphql-dotnet, but I didn't find any way to let it work. Looks like that they are perfect to handle all scenario where there are defined all the properties in the class, but here I have:
- Many Types, but only 1 class that represents them
- The class has a Dictionary to store data, so no clr properties created upfront
- Those library seems to work only at startup time, but I can create new model at runtime
Have you ever had a similar scenario to handle and in case how is it possible to solve?
Thank you
It is possible to create a schema dynamically. It really depends on how you want to present the data to the user of the GraphQL API. GraphQL is statically typed, so typically you would create Objects with Fields to represent the data.
There is an _Any scalar type in the Federation objects which you can use to support dictionaries directly if you want to go that route, details found here:
https://github.com/graphql-dotnet/graphql-dotnet/issues/669#issuecomment-674273598
Though if you want to create a dynamic schema with individual Objects and Fields that can be queried, here is a sample console app that is one approach.
Example output:
Here is also some sample code that OrchardCMS uses to dynamically build fields.
https://github.com/OrchardCMS/OrchardCore/blob/526816bbf6fc597c4910e560468b680ef14119ef/src/OrchardCore/OrchardCore.ContentManagement.GraphQL/Queries/ContentItemQuery.cs
Hope that helps!