Skip to content

Getting Started with GrydCrud

This guide walks you through setting up GrydCrud in your .NET application.

Prerequisites

  • .NET 9.0 SDK or later
  • Entity Framework Core configured
  • Basic understanding of Clean Architecture

Step 1: Install Packages

bash
# Install everything at once
dotnet add package GrydCrud
bash
# Core operations
dotnet add package GrydCrud.Core

# Service layer
dotnet add package GrydCrud.Services

# API controllers
dotnet add package GrydCrud.API

# AutoMapper support
dotnet add package GrydCrud.AutoMapper

# FluentValidation support
dotnet add package GrydCrud.FluentValidation

# CQRS pattern (optional)
dotnet add package GrydCrud.Cqrs

# Auth integration (optional)
dotnet add package GrydCrud.Auth

Step 2: Create Your Entity

csharp
// src/YourApp.Domain/Entities/Product.cs
using Gryd.Domain.Primitives;

// BaseEntity (inherited by AggregateRoot) already implements:
// - IEntity (Id, CreatedAt, IsDeleted)
// - ISoftDeletable (IsDeleted, DeletedAt, DeletedBy, Delete(), Restore())
// - IAuditable (CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)

public class Product : AggregateRoot
{
    public string Name { get; set; } = string.Empty;
    public string Description { get; set; } = string.Empty;
    public decimal Price { get; set; }
    public int Stock { get; set; }
    public bool IsActive { get; set; } = true;
    public Guid CategoryId { get; set; }
    
    // No need to implement ISoftDeletable or IAuditable - inherited from BaseEntity
}

Step 3: Create DTOs

csharp
// src/YourApp.Application/Products/ProductDto.cs

// Result DTO - what API returns
public record ProductDto(
    Guid Id,
    string Name,
    string Description,
    decimal Price,
    int Stock,
    bool IsActive,
    Guid CategoryId,
    DateTime CreatedAt
);

// Create DTO - what API receives for creation
public record CreateProductDto(
    string Name,
    string Description,
    decimal Price,
    int Stock,
    Guid CategoryId
);

// Update DTO - what API receives for updates
public record UpdateProductDto(
    string Name,
    string Description,
    decimal Price,
    int Stock,
    bool IsActive
);

Step 4: Create Validator (Optional)

csharp
// src/YourApp.Application/Products/CreateProductDtoValidator.cs
using FluentValidation;

public class CreateProductDtoValidator : AbstractValidator<CreateProductDto>
{
    public CreateProductDtoValidator()
    {
        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Name is required")
            .MaximumLength(200).WithMessage("Name cannot exceed 200 characters");
        
        RuleFor(x => x.Description)
            .MaximumLength(2000).WithMessage("Description cannot exceed 2000 characters");
        
        RuleFor(x => x.Price)
            .GreaterThan(0).WithMessage("Price must be greater than zero");
        
        RuleFor(x => x.Stock)
            .GreaterThanOrEqualTo(0).WithMessage("Stock cannot be negative");
        
        RuleFor(x => x.CategoryId)
            .NotEmpty().WithMessage("Category is required");
    }
}

public class UpdateProductDtoValidator : AbstractValidator<UpdateProductDto>
{
    public UpdateProductDtoValidator()
    {
        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Name is required")
            .MaximumLength(200);
        
        RuleFor(x => x.Price)
            .GreaterThan(0);
        
        RuleFor(x => x.Stock)
            .GreaterThanOrEqualTo(0);
    }
}

Step 5: Create AutoMapper Profile

csharp
// src/YourApp.Application/Products/ProductMappingProfile.cs
using AutoMapper;

public class ProductMappingProfile : Profile
{
    public ProductMappingProfile()
    {
        // Entity -> DTO
        CreateMap<Product, ProductDto>();
        
        // Create DTO -> Entity
        CreateMap<CreateProductDto, Product>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(_ => Guid.NewGuid()))
            .ForMember(dest => dest.IsActive, opt => opt.MapFrom(_ => true));
        
        // Update DTO -> Entity (for applying changes)
        CreateMap<UpdateProductDto, Product>()
            .ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));
    }
}

Step 6: Configure Services

csharp
// src/YourApp.API/Program.cs
using GrydCrud.Extensions;
using GrydCrud.AutoMapper.Extensions;
using GrydCrud.FluentValidation.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Add DbContext
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));

// Add GrydCrud with options
builder.Services.AddGrydCrud(options =>
{
    options.UseSoftDelete = true;           // Enable soft delete
    options.EnableValidation = true;         // Enable FluentValidation
    options.EnableAuditTracking = true;      // Track created/updated
    options.DefaultPageSize = 20;            // Default page size
    options.MaxPageSize = 100;               // Maximum page size
});

// Add AutoMapper integration
builder.Services.AddGrydCrudAutoMapper(typeof(ProductMappingProfile).Assembly);

// Add FluentValidation
builder.Services.AddGrydCrudValidation(typeof(CreateProductDtoValidator).Assembly);

// Register CRUD for Product entity
builder.Services.AddCrudFor<Product, ProductDto, CreateProductDto, UpdateProductDto>();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

Step 7: Create Controller

Option A: Using Service Pattern

csharp
// src/YourApp.API/Controllers/ProductsController.cs
using GrydCrud.API.Controllers;
using GrydCrud.Core.Models;
using GrydCrud.Services.Abstractions;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : CrudController<Product, CreateProductDto, UpdateProductDto, ProductDto, QueryParameters>
{
    public ProductsController(
        ICrudService<Product, CreateProductDto, UpdateProductDto, ProductDto, QueryParameters> crudService)
        : base(crudService)
    {
    }
    
    // All CRUD endpoints are automatically available:
    // GET    /api/products
    // GET    /api/products/{id}
    // POST   /api/products
    // PUT    /api/products/{id}
    // DELETE /api/products/{id}
}

Option B: Using CQRS Pattern

csharp
// src/YourApp.API/Controllers/ProductsController.cs
using GrydCrud.API.Controllers;
using MediatR;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : CqrsCrudController<Product, ProductDto, CreateProductDto, UpdateProductDto, QueryParameters>
{
    public ProductsController(IMediator mediator) : base(mediator)
    {
    }
}

For CQRS, add MediatR configuration:

csharp
// Program.cs
builder.Services.AddMediatR(cfg => 
    cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));

builder.Services.AddCrudCqrsFor<Product, ProductDto, CreateProductDto, UpdateProductDto>();

Step 8: Run and Test

bash
# Run the application
dotnet run --project src/YourApp.API

# Test the endpoints
curl http://localhost:5000/api/products

API Endpoints

Your controller now exposes these endpoints:

MethodEndpointDescription
GET/api/productsList products with pagination
GET/api/products/{id}Get product by ID
POST/api/productsCreate new product
PUT/api/products/{id}Update product
DELETE/api/products/{id}Delete product (soft/hard)

Query Parameters for List

http
GET /api/products?page=1&pageSize=20&sortBy=Name&sortDirection=Ascending&search=laptop
ParameterTypeDefaultDescription
pageint1Page number
pageSizeint20Items per page (max: 100)
sortBystringCreatedAtProperty to sort by
sortDirectionenumDescendingAscending or Descending
searchstringnullSearch term
includeDeletedboolfalseInclude soft-deleted items

Example Responses

List Products:

json
{
  "items": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Laptop Pro",
      "description": "High-performance laptop",
      "price": 1299.99,
      "stock": 50,
      "isActive": true,
      "categoryId": "category-1",
      "createdAt": "2026-01-29T12:00:00Z"
    }
  ],
  "page": 1,
  "pageSize": 20,
  "totalCount": 150,
  "totalPages": 8,
  "hasNextPage": true,
  "hasPreviousPage": false
}

Create Product:

http
POST /api/products
Content-Type: application/json

{
  "name": "New Product",
  "description": "Product description",
  "price": 99.99,
  "stock": 100,
  "categoryId": "550e8400-e29b-41d4-a716-446655440001"
}

Response (201 Created):

json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440002",
    "name": "New Product",
    "description": "Product description",
    "price": 99.99,
    "stock": 100,
    "isActive": true,
    "categoryId": "550e8400-e29b-41d4-a716-446655440001",
    "createdAt": "2026-01-29T12:00:00Z"
  }
}

Next Steps

Now that you have basic CRUD working, explore these topics:

Troubleshooting

Entity Not Found

Ensure your entity is registered in DbContext:

csharp
public class AppDbContext : DbContext
{
    public DbSet<Product> Products => Set<Product>();
}

AutoMapper Errors

Verify your mapping profile is registered:

csharp
builder.Services.AddGrydCrudAutoMapper(typeof(ProductMappingProfile).Assembly);

Validation Not Working

Ensure validators are registered:

csharp
builder.Services.AddGrydCrudValidation(typeof(CreateProductDtoValidator).Assembly);

And validation is enabled:

csharp
builder.Services.AddGrydCrud(options =>
{
    options.EnableValidation = true;
});

Released under the MIT License.