API Reference
Complete API documentation for GrydCrud module.
Endpoints
GrydCrud automatically generates RESTful endpoints for your entities.
Base URL Pattern
/api/{entity-plural}Example for Product entity: /api/products
CRUD Operations
Create Entity
Create a new entity.
http
POST /api/{entities}
Content-Type: application/json
Authorization: Bearer {token}Request Body:
json
{
"name": "Product Name",
"description": "Product description",
"price": 99.99,
"categoryId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}Response:
http
HTTP/1.1 201 Created
Location: /api/products/550e8400-e29b-41d4-a716-446655440000
Content-Type: application/jsonjson
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Product Name",
"description": "Product description",
"price": 99.99,
"categoryId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"createdAt": "2024-01-15T10:30:00Z",
"createdBy": "user@example.com"
}
}Error Response (400):
json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more validation errors occurred",
"details": [
{ "field": "name", "message": "Name is required" },
{ "field": "price", "message": "Price must be greater than 0" }
]
}
}Get Entity by ID
Retrieve a single entity by its unique identifier.
http
GET /api/{entities}/{id}
Authorization: Bearer {token}Response:
http
HTTP/1.1 200 OK
Content-Type: application/jsonjson
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Product Name",
"description": "Product description",
"price": 99.99,
"categoryId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"category": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Electronics"
},
"createdAt": "2024-01-15T10:30:00Z"
}
}Error Response (404):
json
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Product with id '550e8400-e29b-41d4-a716-446655440000' not found"
}
}Query Entities (Paginated)
Retrieve a paginated list of entities with optional filtering and sorting.
http
GET /api/{entities}?page=1&pageSize=20&search=keyword&sortBy=name&sortOrder=asc
Authorization: Bearer {token}Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | int | 1 | Page number (1-indexed) |
pageSize | int | 20 | Items per page (max: 100) |
search | string | null | Full-text search term |
sortBy | string | "CreatedAt" | Property to sort by |
sortDirection | enum | Descending | Sort direction: Ascending or Descending |
includeDeleted | bool | false | Include soft-deleted items |
Response:
http
HTTP/1.1 200 OK
Content-Type: application/jsonjson
{
"success": true,
"data": {
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Product A",
"price": 99.99
},
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Product B",
"price": 149.99
}
],
"page": 1,
"pageSize": 20,
"totalCount": 150,
"totalPages": 8,
"hasNextPage": true,
"hasPreviousPage": false
}
}Update Entity
Update an existing entity.
http
PUT /api/{entities}/{id}
Content-Type: application/json
Authorization: Bearer {token}Request Body:
json
{
"name": "Updated Product Name",
"description": "Updated description",
"price": 129.99
}Response:
http
HTTP/1.1 200 OK
Content-Type: application/jsonjson
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Updated Product Name",
"description": "Updated description",
"price": 129.99,
"categoryId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:45:00Z",
"updatedBy": "user@example.com"
}
}Delete Entity
Delete an entity (soft delete if entity implements ISoftDeletable).
http
DELETE /api/{entities}/{id}
Authorization: Bearer {token}Response:
http
HTTP/1.1 204 No ContentError Response (404):
json
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Product with id '550e8400-e29b-41d4-a716-446655440000' not found"
}
}Filtering
Simple Filters
http
GET /api/products?categoryId=3fa85f64-5717-4562-b3fc-2c963f66afa6&isActive=trueRange Filters
http
GET /api/products?minPrice=50&maxPrice=200
GET /api/products?createdAfter=2024-01-01&createdBefore=2024-12-31Multiple Values
http
GET /api/products?status=active,pending,review
GET /api/products?categoryIds=guid1,guid2,guid3Nested Filters
http
GET /api/products?category.name=Electronics
GET /api/products?supplier.country=USASorting
Single Field
http
GET /api/products?sortBy=price&sortOrder=asc
GET /api/products?sortBy=createdAt&sortOrder=descMultiple Fields
http
GET /api/products?sort=category.name:asc,price:descIncludes (Eager Loading)
Request related entities to be included in the response.
http
GET /api/products/{id}?include=category,images,reviews
GET /api/products?include=categorySearch
Full-Text Search
http
GET /api/products?search=laptop gamingField-Specific Search
http
GET /api/products?name.contains=Pro&description.contains=wirelessBatch Operations
Batch Create
http
POST /api/{entities}/batch
Content-Type: application/json
Authorization: Bearer {token}json
{
"items": [
{ "name": "Product 1", "price": 99.99 },
{ "name": "Product 2", "price": 149.99 },
{ "name": "Product 3", "price": 199.99 }
]
}Response:
json
{
"success": true,
"data": {
"created": 3,
"items": [
{ "id": "...", "name": "Product 1" },
{ "id": "...", "name": "Product 2" },
{ "id": "...", "name": "Product 3" }
]
}
}Batch Update
http
PUT /api/{entities}/batch
Content-Type: application/json
Authorization: Bearer {token}json
{
"items": [
{ "id": "guid1", "price": 109.99 },
{ "id": "guid2", "price": 159.99 }
]
}Batch Delete
http
DELETE /api/{entities}/batch
Content-Type: application/json
Authorization: Bearer {token}json
{
"ids": ["guid1", "guid2", "guid3"]
}Export
CSV Export
http
GET /api/{entities}/export?format=csv
Accept: text/csv
Authorization: Bearer {token}Excel Export
http
GET /api/{entities}/export?format=xlsx
Accept: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Authorization: Bearer {token}JSON Export
http
GET /api/{entities}/export?format=json
Accept: application/json
Authorization: Bearer {token}Import
http
POST /api/{entities}/import
Content-Type: multipart/form-data
Authorization: Bearer {token}Form Data:
file: CSV or Excel fileoptions: Import options (optional)
Response:
json
{
"success": true,
"data": {
"imported": 150,
"skipped": 5,
"errors": [
{ "row": 23, "message": "Invalid category ID" },
{ "row": 45, "message": "Duplicate SKU" }
]
}
}Response Formats
Success Response
json
{
"success": true,
"data": { /* entity or collection */ },
"meta": {
"timestamp": "2024-01-15T10:30:00Z",
"requestId": "abc-123-def"
}
}Error Response
json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human readable message",
"details": [ /* optional additional details */ ],
"traceId": "abc-123-def"
}
}Error Codes
| Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Request validation failed |
BAD_REQUEST | 400 | Malformed request |
UNAUTHORIZED | 401 | Authentication required |
FORBIDDEN | 403 | Insufficient permissions |
NOT_FOUND | 404 | Entity not found |
CONFLICT | 409 | Duplicate or conflicting data |
UNPROCESSABLE_ENTITY | 422 | Business rule violation |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Internal server error |
Rate Limits
| Endpoint | Limit | Window |
|---|---|---|
POST /api/{entities} | 100 | 1 minute |
PUT /api/{entities}/{id} | 100 | 1 minute |
DELETE /api/{entities}/{id} | 50 | 1 minute |
GET /api/{entities} | 500 | 1 minute |
GET /api/{entities}/{id} | 1000 | 1 minute |
POST /api/{entities}/batch | 10 | 1 minute |
GET /api/{entities}/export | 5 | 1 minute |
Rate Limit Headers:
http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705318200Service Interfaces
ICrudService
csharp
public interface ICrudService<TEntity, in TCreateDto, in TUpdateDto, TResultDto, in TQueryParameters>
where TEntity : class, IEntity
where TQueryParameters : QueryParameters
{
Task<Result<TResultDto>> CreateAsync(TCreateDto dto, CancellationToken ct = default);
Task<Result<TResultDto>> UpdateAsync(Guid id, TUpdateDto dto, CancellationToken ct = default);
Task<Result> DeleteAsync(Guid id, CancellationToken ct = default);
Task<Result<TResultDto>> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<Result<PagedResult<TResultDto>>> GetPagedAsync(TQueryParameters parameters, CancellationToken ct = default);
}
// Simplified version with unified DTO
public interface ICrudService<TEntity, TDto>
: ICrudService<TEntity, TDto, TDto, TDto, QueryParameters>
where TEntity : class, IEntity
{
}ICreateOperation
csharp
public interface ICreateOperation<TEntity, in TCreateDto, TResultDto>
where TEntity : class, IEntity
{
Task<Result<TResultDto>> ExecuteAsync(TCreateDto dto, CancellationToken cancellationToken = default);
}IUpdateOperation
csharp
public interface IUpdateOperation<TEntity, in TUpdateDto, TResultDto>
where TEntity : class, IEntity
{
Task<Result<TResultDto>> ExecuteAsync(Guid id, TUpdateDto dto, CancellationToken cancellationToken = default);
}IDeleteOperation
csharp
public interface IDeleteOperation<TEntity>
where TEntity : class, IEntity
{
Task<Result> ExecuteAsync(Guid id, CancellationToken cancellationToken = default);
}IGetByIdOperation
csharp
public interface IGetByIdOperation<TEntity, TResultDto>
where TEntity : class, IEntity
{
Task<Result<TResultDto>> ExecuteAsync(Guid id, CancellationToken cancellationToken = default);
}IQueryOperation
csharp
public interface IQueryOperation<TEntity, TResultDto, in TQueryParameters>
where TEntity : class, IEntity
where TQueryParameters : QueryParameters
{
Task<Result<PagedResult<TResultDto>>> ExecuteAsync(TQueryParameters parameters, CancellationToken cancellationToken = default);
}Configuration Options
CrudOptions
csharp
public class CrudOptions
{
/// <summary>
/// Use soft delete instead of hard delete. Default: true.
/// </summary>
public bool UseSoftDelete { get; set; } = true;
/// <summary>
/// Default page size for queries. Default: 20.
/// </summary>
public int DefaultPageSize { get; set; } = 20;
/// <summary>
/// Maximum page size allowed. Default: 100.
/// </summary>
public int MaxPageSize { get; set; } = 100;
/// <summary>
/// Default sort field when not specified. Default: "CreatedAt".
/// </summary>
public string DefaultSortField { get; set; } = "CreatedAt";
/// <summary>
/// Default sort direction when not specified. Default: Descending.
/// </summary>
public SortDirection DefaultSortDirection { get; set; } = SortDirection.Descending;
/// <summary>
/// Enable automatic validation using registered validators. Default: true.
/// </summary>
public bool EnableValidation { get; set; } = true;
/// <summary>
/// Enable automatic audit tracking (CreatedBy, UpdatedBy). Default: true.
/// </summary>
public bool EnableAuditTracking { get; set; } = true;
}
public enum SortDirection
{
Ascending,
Descending
}Examples
Complete Entity Setup
csharp
// Entity - inherits from AggregateRoot which already implements
// IEntity, ISoftDeletable, and IAuditable
public class Product : AggregateRoot
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public decimal Price { get; set; }
public Guid CategoryId { get; set; }
public virtual Category? Category { get; set; }
// ISoftDeletable - inherited: IsDeleted, DeletedAt, DeletedBy, Delete(), Restore()
// IAuditable - inherited: CreatedAt, UpdatedAt, CreatedBy (Guid?), UpdatedBy (Guid?)
}
// DTOs
public record ProductDto(
Guid Id,
string Name,
string Description,
decimal Price,
Guid CategoryId,
string CategoryName,
DateTime CreatedAt);
public record CreateProductDto(
string Name,
string Description,
decimal Price,
Guid CategoryId);
public record UpdateProductDto(
string Name,
string Description,
decimal Price,
Guid CategoryId);
// Validator
public class CreateProductDtoValidator : AbstractValidator<CreateProductDto>
{
public CreateProductDtoValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
RuleFor(x => x.Price).GreaterThan(0);
RuleFor(x => x.CategoryId).NotEmpty();
}
}
// Controller - note the correct generic parameters
[ApiController]
[Route("api/products")]
public class ProductsController : CrudController<Product, CreateProductDto, UpdateProductDto, ProductDto, QueryParameters>
{
public ProductsController(
ICrudService<Product, CreateProductDto, UpdateProductDto, ProductDto, QueryParameters> service)
: base(service)
{
}
}Custom Query Parameters
csharp
public class ProductQueryParameters : QueryParameters
{
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public Guid? CategoryId { get; set; }
public bool? InStock { get; set; }
}
// Controller
[HttpGet]
public async Task<IActionResult> Query([FromQuery] ProductQueryParameters parameters)
{
var result = await _service.QueryAsync(parameters);
return Ok(result);
}OpenAPI/Swagger
GrydCrud automatically generates OpenAPI documentation for all endpoints.
Accessing Documentation
GET /swagger/index.html
GET /swagger/v1/swagger.jsonCustomizing Documentation
csharp
builder.Services.AddGrydCrudSwagger(options =>
{
options.Title = "My API";
options.Version = "v1";
options.Description = "API documentation for My Application";
options.ContactEmail = "api@example.com";
options.LicenseUrl = "https://example.com/license";
});