Appearance
Getting Started
This guide walks you through setting up GrydReports in your .NET application, from installation to generating your first report.
Prerequisites
- .NET 10.0 or later
- PostgreSQL (for execution history persistence)
- An existing Gryd.IO project (or use the gryd-api template)
1. Installation
Install the meta-package that includes all layers:
bash
dotnet add package GrydReportsIf you also need recurring scheduling with GrydJobs, add:
bash
dotnet add package GrydReports.Scheduling.GrydJobs
dotnet add package GrydJobsOr install specific renderer packages:
bash
# PDF reports with QuestPDF
dotnet add package GrydReports.Infrastructure.QuestPdf
# Excel reports with ClosedXML
dotnet add package GrydReports.Infrastructure.ClosedXml
# CSV reports with CsvHelper
dotnet add package GrydReports.Infrastructure.CsvHelper2. Configure Services
Register GrydReports in your Program.cs:
csharp
using GrydReports;
using Microsoft.EntityFrameworkCore;
// Optional when using recurring schedules:
// using GrydJobs;
// using GrydReports.Scheduling.GrydJobs;
var builder = WebApplication.CreateBuilder(args);
// Option A: Reports without Jobs (default)
builder.Services.AddGrydReports(
builder.Configuration,
db => db.UseNpgsql(builder.Configuration.GetConnectionString("GrydReports"))
);
// Option B: Reports + Jobs integration (only if you use scheduling endpoints/features)
// builder.Services.AddGrydJobs(options =>
// {
// options.ConnectionString = builder.Configuration.GetConnectionString("Jobs")!;
// });
// builder.Services.AddGrydReportsScheduling();
// Option C: Register layers individually (fine-grained control)
// Use this instead of Option A when you need custom wiring:
// builder.Services.AddGrydReportsApplication(); // MediatR + validators
// builder.Services.AddGrydReportsInfrastructure( // EF Core + services
// configureOptions: opts =>
// {
// opts.RetentionDays = 90;
// opts.LocalStoragePath = "/data/reports";
// },
// configureDbContext: db =>
// db.UseNpgsql(builder.Configuration.GetConnectionString("GrydReports"))
// );
// builder.Services.AddGrydReportsApi(); // REST controllers
// Health checks (optional)
builder.Services.AddGrydReportsHealthChecks(builder.Configuration);
var app = builder.Build();
// Apply middleware
app.UseGrydReports();
// Apply migrations on startup (Development only)
if (app.Environment.IsDevelopment())
{
await app.ApplyGrydReportsMigrationsAsync();
// Optional when GrydJobs is enabled:
// await app.ApplyGrydJobsMigrationsAsync();
}
app.MapControllers();
app.Run();3. Add Configuration
Add the following to your appsettings.json. ConnectionStrings:Jobs is only required if you enabled GrydJobs + AddGrydReportsScheduling().
json
{
"ConnectionStrings": {
"GrydReports": "Host=localhost;Database=gryd_reports;Username=postgres;Password=postgres",
"Jobs": "Host=localhost;Port=5432;Database=gryd_jobs;Username=postgres;Password=postgres"
},
"GrydReports": {
"RetentionDays": 90,
"LocalStoragePath": "./reports-storage",
"MaxSyncGenerationSize": 52428800,
"SyncGenerationTimeout": "00:02:00",
"AutoRegisterTemplates": true,
"Caching": {
"Enabled": false,
"DefaultExpiration": "01:00:00"
}
}
}4. Define a Data Model
Create a class that represents your report data:
csharp
// Reports/Models/InvoiceData.cs
public class InvoiceData
{
public string CompanyName { get; set; } = default!;
public string CustomerName { get; set; } = default!;
public string InvoiceNumber { get; set; } = default!;
public DateTime IssueDate { get; set; }
public DateTime DueDate { get; set; }
public List<InvoiceLineItem> Items { get; set; } = [];
public decimal Subtotal => Items.Sum(i => i.Total);
public decimal Tax => Subtotal * 0.1m;
public decimal GrandTotal => Subtotal + Tax;
}
public class InvoiceLineItem
{
public string Description { get; set; } = default!;
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal Total => Quantity * UnitPrice;
}5. Implement a Data Source
The data source fetches data from your business layer:
csharp
// Reports/DataSources/InvoiceDataSource.cs
using GrydReports.Core.Abstractions;
public class InvoiceDataSource : IReportDataSource<InvoiceData, InvoiceParameters>
{
private readonly IInvoiceRepository _invoices;
public InvoiceDataSource(IInvoiceRepository invoices) => _invoices = invoices;
public async Task<InvoiceData> FetchDataAsync(
InvoiceParameters parameters, CancellationToken ct)
{
var invoice = await _invoices.GetByNumberAsync(parameters.InvoiceNumber, ct)
?? throw new InvalidOperationException($"Invoice {parameters.InvoiceNumber} not found");
return new InvoiceData
{
CompanyName = "Acme Corp",
CustomerName = invoice.CustomerName,
InvoiceNumber = invoice.Number,
IssueDate = invoice.IssueDate,
DueDate = invoice.DueDate,
Items = invoice.Lines.Select(l => new InvoiceLineItem
{
Description = l.Description,
Quantity = l.Quantity,
UnitPrice = l.UnitPrice
}).ToList()
};
}
}
// Reports/Models/InvoiceParameters.cs
public class InvoiceParameters
{
public string InvoiceNumber { get; set; } = default!;
}6. Create a Report Template
The template defines metadata and structure:
csharp
// Reports/Templates/InvoiceTemplate.cs
using GrydReports.Core.Abstractions;
using GrydReports.Core.Enums;
using GrydReports.Core.Models;
public class InvoiceTemplate : IReportTemplate<InvoiceData>
{
public string TemplateId => "invoice";
public string DisplayName => "Invoice Report";
public string? Description => "Generates printable invoices";
public IReadOnlyList<ReportFormat> SupportedFormats =>
[ReportFormat.Pdf, ReportFormat.Excel];
public IReadOnlyList<ReportParameterDefinition> Parameters =>
[
new("invoiceNumber", "Invoice Number", "string", required: true)
];
public ReportMetadata GetMetadata(InvoiceData data) => new()
{
Title = $"Invoice #{data.InvoiceNumber}",
Author = data.CompanyName,
Subject = $"Invoice for {data.CustomerName}"
};
}7. Register Dependencies
Register your custom types in DI:
csharp
// Program.cs (after AddGrydReports)
builder.Services.AddScoped<IReportDataSource<InvoiceData, InvoiceParameters>, InvoiceDataSource>();
builder.Services.AddSingleton<IReportTemplate<InvoiceData>, InvoiceTemplate>();8. Generate a Report
Via API
bash
# Synchronous generation
curl -X POST http://localhost:5000/api/v1/reports/generate \
-H "Content-Type: application/json" \
-d '{
"templateId": "invoice",
"format": 1,
"parameters": { "invoiceNumber": "INV-2026-001" }
}'
# Response: 201 Created
# {
# "id": "a1b2c3d4-...",
# "templateId": "invoice",
# "status": 3,
# "fileName": "invoice-2026-001.pdf",
# "downloadUrl": "/api/v1/reports/a1b2c3d4-.../download"
# }Via Code (MediatR)
csharp
var result = await mediator.Send(new GenerateReportCommand(
TemplateId: "invoice",
Format: ReportFormat.Pdf,
Parameters: new Dictionary<string, object?> { ["invoiceNumber"] = "INV-2026-001" }
));
if (result.IsSuccess)
{
Console.WriteLine($"Report generated: {result.Data!.FileName}");
Console.WriteLine($"Download: /api/v1/reports/{result.Data.Id}/download");
}Next Steps
- Configuration — Fine-tune options, permissions, caching
- Templates — Advanced template patterns (PDF layouts, Excel sheets)
- Generation — Async generation, batch processing
- Scheduling — Set up recurring reports with cron expressions
- Delivery — Auto-deliver via email, webhook, or storage
- Migration (No Jobs) — Remove GrydJobs when scheduling is not needed