---
title: Camel Casing
editUrl: https://github.com/Cratis/Chronicle/edit/main/Documentation/configuration/camel-casing.mdx
description: Chronicle can apply a camel case naming policy when building projection definitions and persisting read models. This keeps property names consistent with…
---

import { Tabs, TabItem } from '@astrojs/starlight/components';

Chronicle can apply a camel case naming policy when building projection definitions and persisting read models. This keeps property names consistent with common JSON naming conventions.

## Overview

By default, Chronicle uses property names as they appear in C# (PascalCase). Enabling camel case will:

- Govern how projection definitions are built
- Determine the property names used when projections update read models
- Keep naming consistent across projection and read model data

## Using a Direct Client

For direct `ChronicleClient` usage outside of a hosted application, pass a `CamelCaseNamingPolicy` instance via the named `namingPolicy` constructor argument:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle;
using Cratis.Serialization;

public static class CamelCasingDirectClient
{
    public static ChronicleClient Create() =>
        new(options: new ChronicleOptions(), namingPolicy: new CamelCaseNamingPolicy());
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/configuration/camel-casing/direct-client.md)

</TabItem>
<TabItem label="Kotlin">

```text
Kotlin does not support this workflow yet.
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/configuration/camel-casing/direct-client.md)

</TabItem>
<TabItem label="Java">

```text
Java does not support this workflow yet.
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/configuration/camel-casing/direct-client.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/configuration/camel-casing/direct-client.md)

</TabItem>
<TabItem label="TypeScript">

```text
TypeScript does not support this workflow yet.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/configuration/camel-casing/direct-client.md)

</TabItem>
</Tabs>

## Using ASP.NET Core

When building ASP.NET Core applications, use the dependency injection extensions to configure Chronicle with camel case naming policy.

### Basic Configuration

In your `Program.cs` file, configure Chronicle with camel case naming policy using the `IChronicleBuilder` callback:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Microsoft.AspNetCore.Builder;

public static class CamelCasingAspNetCoreBasicRegistration
{
    public static void Configure(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.AddCratisChronicle(
            configure: chronicleBuilder => chronicleBuilder.WithCamelCaseNamingPolicy());

        var app = builder.Build();
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-basic.md)

</TabItem>
<TabItem label="Kotlin">

```text
Kotlin does not support this workflow yet.
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-basic.md)

</TabItem>
<TabItem label="Java">

```text
Java does not support this workflow yet.
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/configuration/camel-casing/aspnetcore-basic.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-basic.md)

</TabItem>
<TabItem label="TypeScript">

```text
TypeScript does not support this workflow yet.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-basic.md)

</TabItem>
</Tabs>

### Dependency Injection Configuration

You can combine camel case naming policy with other Chronicle configuration options:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Microsoft.AspNetCore.Builder;

public static class CamelCasingAspNetCoreWithOptionsRegistration
{
    public static void Configure(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.AddCratisChronicle(
            configureOptions: options => options.EventStore = "MyEventStore",
            configure: chronicleBuilder => chronicleBuilder.WithCamelCaseNamingPolicy());

        var app = builder.Build();
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-with-options.md)

</TabItem>
<TabItem label="Kotlin">

```text
Kotlin does not support this workflow yet.
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-with-options.md)

</TabItem>
<TabItem label="Java">

```text
Java does not support this workflow yet.
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/configuration/camel-casing/aspnetcore-with-options.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-with-options.md)

</TabItem>
<TabItem label="TypeScript">

```text
TypeScript does not support this workflow yet.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/configuration/camel-casing/aspnetcore-with-options.md)

</TabItem>
</Tabs>

## Using .NET Host (Worker Services)

For worker services or other non-web hosts using `IHostApplicationBuilder`:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Microsoft.Extensions.Hosting;

public static class CamelCasingWorkerHostRegistration
{
    public static void Configure(string[] args)
    {
        var builder = Host.CreateApplicationBuilder(args);

        builder.AddCratisChronicle(
            configure: chronicleBuilder => chronicleBuilder.WithCamelCaseNamingPolicy());

        var host = builder.Build();
    }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/configuration/camel-casing/worker-host.md)

</TabItem>
<TabItem label="Kotlin">

```text
Kotlin does not support this workflow yet.
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/configuration/camel-casing/worker-host.md)

</TabItem>
<TabItem label="Java">

```text
Java does not support this workflow yet.
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/configuration/camel-casing/worker-host.md)

</TabItem>
<TabItem label="Elixir">

```text
Elixir does not support this workflow yet.
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/configuration/camel-casing/worker-host.md)

</TabItem>
<TabItem label="TypeScript">

```text
TypeScript does not support this workflow yet.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/configuration/camel-casing/worker-host.md)

</TabItem>
</Tabs>

## Impact on Projections

The naming policy configuration affects how Chronicle handles projections:

### Projection Definition Building

When you configure camel case naming policy, Chronicle uses this policy when building projection definitions. This means property mappings within projections will use camel case property names.

### Read Models

The naming policy affects the read model container name, which affects the name of the collection, table, or file that data is persisted as.

When projections project to read models, the property names used will follow the configured naming policy. For example:

#### Event Definition

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Events;

[EventType]
public record CamelCasingUserRegistered(
    string FirstName,
    string LastName,
    string EmailAddress,
    DateTime RegistrationDate);
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/configuration/camel-casing/event.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.events.EventType
import java.time.Instant

@EventType
data class CamelCasingUserRegistered(
    val firstName: String = "",
    val lastName: String = "",
    val emailAddress: String = "",
    val registrationDate: Instant = Instant.EPOCH
)
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/configuration/camel-casing/event.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.events.EventType;

import java.time.Instant;

@EventType
record CamelCasingUserRegistered(
    String firstName,
    String lastName,
    String emailAddress,
    Instant registrationDate) {
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/configuration/camel-casing/event.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Events.CamelCasingUserRegistered do
  use Chronicle.Events.EventType, id: "camel-casing-user-registered"

  defstruct [:first_name, :last_name, :email_address, :registration_date]
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/configuration/camel-casing/event.md)

</TabItem>
<TabItem label="TypeScript">

```text
TypeScript does not support this workflow yet.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/configuration/camel-casing/event.md)

</TabItem>
</Tabs>

#### Read Model Definition

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
public class CamelCasingUserReadModel
{
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public string EmailAddress { get; set; } = string.Empty;
    public DateTime RegistrationDate { get; set; }
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/configuration/camel-casing/read-model.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.readModels.ReadModel
import java.time.Instant

@ReadModel
data class CamelCasingUserReadModel(
    val firstName: String = "",
    val lastName: String = "",
    val emailAddress: String = "",
    val registrationDate: Instant = Instant.EPOCH
)
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/configuration/camel-casing/read-model.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.readModels.ReadModel;

import java.time.Instant;

@ReadModel
class CamelCasingUserReadModel {
    public String firstName = "";
    public String lastName = "";
    public String emailAddress = "";
    public Instant registrationDate = Instant.EPOCH;
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/configuration/camel-casing/read-model.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.ReadModels.CamelCasingUser do
  use Chronicle.ReadModels.ReadModel

  defstruct first_name: nil, last_name: nil, email_address: nil, registration_date: nil
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/configuration/camel-casing/read-model.md)

</TabItem>
<TabItem label="TypeScript">

```text
TypeScript does not support this workflow yet.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/configuration/camel-casing/read-model.md)

</TabItem>
</Tabs>

#### Projection with Camel Case Naming Policy

With camel case naming policy configured, Chronicle will use camel case property names when building the projection:

<Tabs syncKey="chronicle-client">
<TabItem label="C#">

```csharp
using Cratis.Chronicle.Projections;

public class CamelCasingUserProjection : IProjectionFor<CamelCasingUserReadModel>
{
    public void Define(IProjectionBuilderFor<CamelCasingUserReadModel> builder) => builder
        .From<CamelCasingUserRegistered>();
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Chronicle/blob/main/Documentation/client-snippets/configuration/camel-casing/projection.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.chronicle.projections.IProjectionFor
import io.cratis.chronicle.projections.IProjectionBuilderFor

// Kotlin property names are already camelCase, so the projected read model properties below —
// firstName, lastName, emailAddress, registrationDate — need no naming policy configuration to
// come out as camelCase; that is simply how Kotlin properties are named.
class CamelCasingUserProjection : IProjectionFor<CamelCasingUserReadModel> {
    override fun define(builder: IProjectionBuilderFor<CamelCasingUserReadModel>) {
        builder.from(CamelCasingUserRegistered::class) {
            it.set(CamelCasingUserReadModel::firstName).toProperty("firstName")
            it.set(CamelCasingUserReadModel::lastName).toProperty("lastName")
            it.set(CamelCasingUserReadModel::emailAddress).toProperty("emailAddress")
            it.set(CamelCasingUserReadModel::registrationDate).toProperty("registrationDate")
        }
    }
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets/configuration/camel-casing/projection.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.chronicle.projections.IProjectionFor;
import io.cratis.chronicle.projections.IProjectionBuilderFor;
import io.cratis.chronicle.java.ProjectionBuilderJavaBridge;

// Java field names are already camelCase, and AutoMap matches read model properties to event
// properties of the same name — firstName, lastName, emailAddress, registrationDate — so no
// naming policy configuration is needed to have them come out as camelCase.
class CamelCasingUserProjection implements IProjectionFor<CamelCasingUserReadModel> {
    @Override
    public void define(IProjectionBuilderFor<CamelCasingUserReadModel> builder) {
        ProjectionBuilderJavaBridge.from(builder, CamelCasingUserRegistered.class);
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Chronicle.Kotlin/blob/main/Documentation/client-snippets-java/configuration/camel-casing/projection.md)

</TabItem>
<TabItem label="Elixir">

```elixir
defmodule MyApp.Projections.CamelCasingUserProjection do
  use Chronicle.Projections.Projection, model: MyApp.ReadModels.CamelCasingUser

  from MyApp.Events.CamelCasingUserRegistered,
    set: [
      first_name: :first_name,
      last_name: :last_name,
      email_address: :email_address,
      registration_date: :registration_date
    ]
end
```

[View Elixir snippet source on GitHub](https://github.com/Cratis/Chronicle.Elixir/blob/main/Documentation/client-snippets/configuration/camel-casing/projection.md)

</TabItem>
<TabItem label="TypeScript">

```text
TypeScript does not support this workflow yet.
```

[View TypeScript snippet source on GitHub](https://github.com/Cratis/Chronicle.TypeScript/blob/main/Documentation/client-snippets/configuration/camel-casing/projection.md)

</TabItem>
</Tabs>

The resulting read model data will have camel case property names: `firstName`, `lastName`, `emailAddress`, `registrationDate`.

## Important Notes

- The naming policy affects how Chronicle builds projection definitions internally.
- Property names in read models will follow the configured naming policy when data is persisted.
- This configuration is specific to Chronicle operations and does not affect general ASP.NET Core JSON serialization.
- All projections in your application will use the same naming policy once configured.

## Troubleshooting

### Projection Property Names Not Converting

If projection property names are not being converted to camel case:

1. Verify that `WithCamelCaseNamingPolicy()` is called on the `IChronicleBuilder` during Chronicle configuration.
2. Ensure the configuration is applied before Chronicle services are initialized.
3. Check that all projections are using the same Chronicle client instance.

### Inconsistent Property Naming

If you see inconsistent property naming in your read models:

1. Verify that Chronicle is configured with the camel case naming policy via `IChronicleBuilder.WithCamelCaseNamingPolicy()`.
2. Check if any custom property mappings are overriding the global naming policy.
3. Ensure all projection definitions are rebuilt after changing the naming policy.
