3 Exploring Components

Exploring Components

In Confix, every configuration is broken down into elements known as components. These components act as a blueprint, ensuring your configuration aligns with a defined structure or schema.

You technically don't need components. You can also create a configuration and just use variables without having a schema. Without components you will not have any validation or autocompletion though.

Components can be defined per application but also can be shared between projects.

Defining a local component

Let's start by defining a component that's specific to our application. First, navigate to the Website folder and execute the following command:

confix component init Website

This command will create a new folder in Website called confix/components/Website. At the moment this folder only contains one file, the .confix.component file.

Now, let's create a schema for our component. This schema will define the structure of our component and give us autocompletion and validation.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/product.schema.json",
  "title": "product",
  "description": "A product from Acme's catalog",
  "type": "object",
  "properties": {
    "Url": {
      "description": "The Url of the website",
      "type": "string"
    }
  },
  "required": ["Url"]
}

When we run the command confix build in the Website folder we now see that our component is loaded and that the configuration we have is applied.

confix build

Output:

 Running in scope Project
 Configuration loaded
 Active Environment is prod
 Component inputs loaded
 Loaded 1 components
  -  Website
 Schema composition completed for project src.Website
 Schema is stored at '/Users/ex/Confix/examples/GettingStarted/.confix/.schemas/src.Website.schema.json'
i Loaded schema from cache for project src.Website
  Persisting configuration file '/Users/ex/.microsoft/usersecrets/f736895a-96b8-471a-8502-09b0e07dfdb8/secrets.json'
  Detected 0 variables
✕ The configuration file '/Users/ex/Confix/examples/GettingStarted/src/Website/appsettings.json' is invalid.
 └── Website
    └── Url
        ├──  Value is "null" but should be "string"
        └──  Expected value to match one of the values specified by the enum
✕ Confix failed.
  The configuration files are invalid.

When we open the appsettings.json file we see that the configuration was initialized and Url is set to null. The intelisense tells us that the Url cannot be empty and has to have a value.

VSCode

Using input providers

Defining JSON schemas is a pain. It's a lot of work and it's easy to make mistakes. Confix has a features called Component Inputs. These allow you to process components and generate a schema based on the result. We will use the graphql provider to generate a schema based on a GraphQL file.

For this we first need to configure the input in the .confixrc.

.confixrc
{
  "component": {
    "inputs": [
      {
        "type": "graphql"
      }
    ]
  },
  "project": {
    "configurationFiles": [
      {
        "type": "appsettings",
        "useUserSecrets": true
      }
    ]
  }
}

Now we can define a schema.graphql file in the component folder and run confix build again.

schema.graphql
type Configuration {
  Url: String!
  Database: Database!
}
type Database {
  ConnectionString: String!
  DatabaseName: String!
}

The schema graphql file is now processed and translated into a JSON schema.

Sharing components

Now, the database part of our configuration can be used in multiple projects. When we create a package for the Database connection, we can share all the common settings and APIs between projects. This way adding a database to a new project is as easy as adding a NuGet package.

Confix allows you to share components between projects. There are different ways to do this. A component can be defined on a shared registry, e.g. a Git repository. In case of a shared project, there is better way though. You can ship the component together with the package and confix automatically recognizes the component.

This has some advantages:

  • The component is versioned together with the source code. This means that you can evolve the shape of the configuration together with the component. If you decide to introduce a breaking change in the configuration, once you upgrade the package to theis version, confix will tell you that the configuration is invalid.
  • There is no external component needed (like a git repository). You just reference the package and you are good to go.

First, to make the components discoverable in .NET, we need a special "component input". This input will registry the component as a embedded resource in the package.

Secondly, we need to tell confix to look for components in the package. For this we use a "component provider". Component providers are responsible for loading components from different sources e.g. git or packages

We are very open to add support for e.g. Maven, NPM or other package managers. If you need this, please open an issue on GitHub.

.confixrc
{
  "component": {
    "inputs": [
      {
        "type": "graphql"
      },
      {
        "type": "dotnet"
      }
    ]
  },
  "project": {
    "configurationFiles": [
      {
        "type": "appsettings",
        "useUserSecrets": true
      }
    ],
    "componentProviders": [
      {
        "name": "dotnet",
        "type": "dotnet-package"
      }
    ]
  }
}

Lets create a shared database component.

Creating the package

Naviagate to the /src folder and execute the following command:

cd src
dotnet new classlib -n Database
cd Database
dotnet add package Microsoft.Extensions.DependencyInjection

Add a extension method

Add a extension method to the class library that registers the component.

src/Database/DatabaseServiceCollectionExtensions.cs
using Microsoft.Extensions.DependencyInjection;
 
namespace Database;
 
public static class DatabaseServiceCollectionExtensions
{
    public static void AddDatabase(this IServiceCollection services)
    {
        // nothing to do here
    }
}

Initialize confix

Create a confix project and a component to the Database package

cd src/Database
confix project init
confix component init Database

Configure the schema

Add a schema to the component

src/Database/confix/components/Database/schema.graphql
type Configuration {
  ConnectionString: String!
  DatabaseName: String!
}

Reference the component

Reference the package in the Website project

cd src/Website
dotnet add reference ../Database

Update the Website config

Remove the Database configuration from the Website component

src/Website/confix/components/Website/schema.graphql
type Configuration {
  Url: String!
}

Use the component

Use the component in the Website project

⚠️

A reference to the package is not enough. You need to use the package in your code. Otherwise .NET will treeshake the package and it will not be included in the final build.

src/Website/Program.cs
using Database;
 
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDatabase();
var app = builder.Build();
 
app.MapGet("/", () => "Hello World!");
 
app.Run();

In the end the folder structure should look like this:

When we now run confix build, the component from the package is recognized.

VSCode

The schema of this component is embedded in the package and is automatically loaded by confix. This means that you can also distribute the component as a NuGet package and use it in other projects.

Continue to the next section to learn about variables.