Skip to content

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 GrydReports

If you also need recurring scheduling with GrydJobs, add:

bash
dotnet add package GrydReports.Scheduling.GrydJobs
dotnet add package GrydJobs

Or 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.CsvHelper

2. 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

Released under the MIT License.