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 GrydCrudbash
# 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.AuthStep 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/productsAPI Endpoints
Your controller now exposes these endpoints:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/products | List products with pagination |
| GET | /api/products/{id} | Get product by ID |
| POST | /api/products | Create 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| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number |
pageSize | int | 20 | Items per page (max: 100) |
sortBy | string | CreatedAt | Property to sort by |
sortDirection | enum | Descending | Ascending or Descending |
search | string | null | Search term |
includeDeleted | bool | false | Include 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:
- Operations - Detailed operation documentation
- Hooks - Customize operation behavior
- Validation - Advanced validation scenarios
- CQRS - Command/Query separation
- API Reference - Complete API documentation
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;
});