DataSurface
June 10, 2026 Β· View on GitHub
Contract-driven CRUD HTTP endpoints for ASP.NET Core
DataSurface eliminates CRUD boilerplate by generating fully-featured HTTP endpoints from a single source of truth: the ResourceContract. Define your resources once using C# attributes or database metadata, and get automatic validation, filtering, sorting, pagination, and more.
You define what a resource is β fields, validation, security, relations β and DataSurface handles:
- CRUD endpoints
- Validation
- Filtering, sorting, pagination
- Authorization & row-level security
- Concurrency, caching, auditing, and observability
All without writing DTOs, controllers, or repetitive glue code.
π« What DataSurface Removes
- Handwritten CRUD controllers
- Read/Create/Update/Delete DTOs
- Manual validation plumbing
- Query parsing logic
- Boilerplate authorization checks
- Repeated Swagger/OpenAPI definitions
β What You Keep
- Full control over your domain model
- Strong typing
- Explicit security rules
- Override hooks when you do need custom logic
Why DataSurface?
Most ASP.NET Core applications repeat the same pattern:
- Entity
- DTOs (Read / Create / Update)
- Controller
- Validation
- Query parsing
- Authorization checks
Multiply that by 20β50 entities and the cost becomes significant.
DataSurface collapses all of that into one contract.
You describe what is allowed, not how to wire it.
The result:
- Fewer files
- Less drift between layers
- Consistent behavior across all resources
- Faster iteration without sacrificing control
Before vs After
β Traditional CRUD
- Entity
- 3β5 DTOs
- Controller with ~200 lines
- Manual validation
- Manual filtering & paging
- Swagger configuration
- Repeated authorization logic
User.cs
UserReadDto.cs
UserCreateDto.cs
UserUpdateDto.cs
UsersController.cs
UserValidator.cs
β With DataSurface
- Entity
- Attributes describing the contract
[CrudResource("users")]
public class User
{
[CrudKey]
public int Id { get; set; }
[CrudField(CrudDto.Read | CrudDto.Create | CrudDto.Update, RequiredOnCreate = true)]
public string Email { get; set; } = default!;
}
app.MapDataSurfaceCrud();
Thatβs it!
Usage Modes
DataSurface can be used in two ways:
π HTTP API (Most Common)
- Generates REST endpoints via Minimal APIs
- Full OpenAPI / Swagger support
- Ideal for frontend, mobile, or external integrations
GET /api/users
POST /api/users
PATCH /api/users/{id}
DELETE /api/users/{id}
βοΈ In-Process (No HTTP)
- Call CRUD operations directly
- Same validation, security, hooks, and contracts
- Ideal for internal services, background jobs, or modular monoliths
await crudService.CreateAsync("User", body, ct);
No controllers. No HTTP. Same guarantees.
When to Use DataSurface
β
You build data-heavy APIs
β
You want consistent CRUD behavior
β
You want fewer DTOs and controllers
β
You need strong validation & security
β
You support dynamic or metadata-driven entities
When NOT to Use DataSurface
β You want full handcrafted controllers for every endpoint
β Your API is mostly bespoke workflows, not CRUD
β You dislike declarative configuration
DataSurface is not a replacement for custom business logic β it handles the 80% so you can focus on the 20%.
Features
| Feature | Description |
|---|---|
| Auto-generated endpoints | GET, POST, PATCH, DELETE, PUT via Minimal APIs |
| Field-level control | Choose which fields appear in read/create/update DTOs |
| Default values | Automatically apply defaults when creating resources |
| Computed fields | Server-calculated read-only fields |
| Validation | Required, immutable, length, range, regex, allowed values |
| Field projection | Select specific fields via ?fields= query parameter |
| Soft delete | Built-in ISoftDelete convention support |
| Timestamps | Auto-populate CreatedAt/UpdatedAt via ITimestamped |
| Filtering & Sorting | Allowlisted fields with operators (eq, gt, contains, etc.) |
| Pagination | Built-in page + pageSize with configurable max |
| Expansion | expand=relation with depth limits |
| HEAD support | HEAD requests return count headers without body |
| Authorization | Per-operation policy names |
| Row-level security | IResourceFilter<T> for tenant/user-based query filtering |
| Resource authorization | IResourceAuthorizer<T> for instance-level access control |
| Field authorization | IFieldAuthorizer for field-level read/write control |
| Tenant isolation | Automatic multi-tenancy with [CrudTenant] attribute |
| Concurrency | Row version + ETag / If-Match headers |
| Hooks | Global and entity-specific lifecycle hooks |
| Overrides | Replace any CRUD operation with custom logic |
| Dynamic entities | Runtime-defined resources without recompilation |
| Query caching | Optional IDistributedCache integration |
| Response caching | ETag-based 304 responses, configurable Cache-Control |
| Bulk operations | Batch create/update/delete via /bulk endpoint |
| Import/Export | Bulk data import/export in JSON or CSV format |
| Async streaming | IAsyncEnumerable support via /stream endpoint |
| Webhooks | Publish events when CRUD operations occur |
| Rate limiting | ASP.NET Core rate limiting integration |
| API key authentication | Machine-to-machine authentication |
| Audit logging | IAuditLogger for tracking all CRUD operations |
| Structured logging | Built-in ILogger integration with operation timing |
| Metrics | OpenTelemetry-compatible counters and histograms |
| Distributed tracing | Activity/span integration for request tracing |
| Health checks | IHealthCheck implementations for monitoring |
| Schema endpoint | GET /api/$schema/{resource} returns JSON Schema |
| API reference UI | Swagger UI and Scalar reference over the generated OpenAPI document |
| Feature flags | Selectively enable/disable features with presets |
Packages
Typical combinations:
- Static only:
Core+EFCore+Http - Dynamic only:
Core+Dynamic+Http+Admin - Both: All of the above
Quick Start
1. Define your entity
using DataSurface.Core.Annotations;
using DataSurface.Core.Enums;
[CrudResource("users")]
public class User
{
[CrudKey]
public int Id { get; set; }
[CrudField(CrudDto.Read | CrudDto.Create | CrudDto.Update, RequiredOnCreate = true)]
public string Email { get; set; } = default!;
[CrudField(CrudDto.Read | CrudDto.Filter | CrudDto.Sort)]
public DateTime CreatedAt { get; set; }
[CrudConcurrency]
public byte[] RowVersion { get; set; } = default!;
}
2. Register services
using DataSurface.EFCore.Services;
using System.Reflection;
// Register contracts, EF Core services, and the full CRUD runtime.
// The generic overload aliases your DbContext to the base DbContext
// the CRUD services depend on.
builder.Services.AddDataSurfaceEfCore<AppDbContext>(opt =>
{
opt.AssembliesToScan = [Assembly.GetExecutingAssembly()];
});
3. Map endpoints
using DataSurface.Http;
app.MapDataSurfaceCrud();
Result: Your API now has these endpoints:
GET /api/usersβ List with filtering, sorting, paginationHEAD /api/usersβ Get count only (inX-Total-Countheader)GET /api/users/{id}β Get single resourcePOST /api/usersβ CreatePATCH /api/users/{id}β UpdateDELETE /api/users/{id}β DeleteGET /api/$schema/usersβ Get JSON Schema for resource
π Documentation
Full documentation lives in /docs β start there for the complete guide, or jump straight to a section below.
Getting Started
- Installation β Package references and dependencies
- Quick Start β Minimal working example in under 5 minutes
- Configuration β Overview of all configuration surfaces
Architecture
- Overview β Module structure, key abstractions, diagram
- Contracts β The ResourceContract system in depth
- Request Lifecycle β How a request flows through the pipeline
Features
- CRUD Operations Β· Querying Β· Validation Β· Relationships
- Security Β· Concurrency Β· Hooks & Overrides Β· Dynamic Entities
- Caching Β· Bulk & Streaming Β· Webhooks Β· Observability
- OpenAPI Integration Β· Source Generator Β· Feature Flags
Reference
- Attributes β All annotation attributes and their properties
- Configuration Options β All options classes
- API Endpoints β Complete HTTP endpoint reference
- Error Responses β Status codes, error types, problem details
- Enums & Types β All enums and canonical field types
More
- Benchmarks β Query engine performance analysis
- Roadmap β Feature status and planned work
Contributing
Issues and pull requests are welcome. See the Roadmap for shipped features and what's planned next β open an issue with the enhancement label to suggest a feature.