Skip to content

CSV Export

Return CsvResponse<TRow> directly from HandleAsync. It implements IResult, so the framework streams the rows to the response without buffering the entire dataset in memory.

public sealed class UserExportRow
{
[Name("id")] public int Id { get; init; }
[Name("name")] public string Name { get; init; } = string.Empty;
[Name("email")] public string Email { get; init; } = string.Empty;
}
public sealed class ExportUsersEndpoint : IEndpoint<CsvResponse<UserExportRow>>
{
private readonly IUserRepository _repository;
public ExportUsersEndpoint(IUserRepository repository) => _repository = repository;
public void Configure(IEndpointConfiguration config)
{
config.Get("/users/export").Summary("Export users as CSV");
}
public Task<CsvResponse<UserExportRow>> HandleAsync(CancellationToken cancel)
{
// IAsyncEnumerable<T> is written row-by-row without loading everything into memory.
var rows = _repository.GetAllAsync(cancel);
return Task.FromResult(CsvResponse.From(rows, fileName: "users.csv"));
}
}

CsvResponse.From also accepts IEnumerable<T> for synchronous sequences.

CsvResponse.From accepts the following parameters:

ParameterTypeDefaultDescription
rowsIAsyncEnumerable<TRow> or IEnumerable<TRow>(required)Sequence of rows to write as CSV
classMapClassMap?nullColumn mapping and formatting configuration through CsvHelper ClassMap. If null, CsvHelper convention-based mapping and attributes on TRow are used
fileNamestring"export.csv"File name written to the Content-Disposition header
statusCodeHttpStatusCodeHttpStatusCode.OKHTTP status code
configurationCsvConfiguration?nullCsvHelper writer configuration. If null, the default configuration using CultureInfo.InvariantCulture is applied
// Example with all parameters specified
CsvResponse.From(
rows,
classMap: new UserRowMap(),
fileName: "users-export.csv",
statusCode: HttpStatusCode.OK,
configuration: new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = "\t"
}
);

For custom column mapping on export, see Class Map.