API Endpoints Reference

June 10, 2026 · View on GitHub

Complete specification of all HTTP endpoints generated by DataSurface.


Base URL

All endpoints are prefixed with the configured ApiPrefix (default: /api).


Resource Endpoints

List Resources

GET /api/{route}

Returns a paginated list of resources with filtering, sorting, and search.

Query Parameters:

ParameterExampleDescription
page?page=2Page number (1-based, default: 1)
pageSize?pageSize=50Items per page (default: 20, max: contract-defined)
sort?sort=title,-createdAtComma-separated fields, - prefix for descending
filter[field]?filter[price]=gt:100Field filter with operator
q?q=search+termFull-text search across searchable fields
expand?expand=author,tagsInclude related resources
fields?fields=id,title,emailReturn only specified fields

Results are always deterministically ordered: the requested sort wins, falling back to the resource's DefaultSort (see [CrudResource]), with the key appended as a final tie-breaker.

Response: 200 OK

{
  "items": [...],
  "page": 1,
  "pageSize": 20,
  "total": 142
}

Response Headers:

HeaderExampleDescription
X-Total-Count142Total matching records
X-Page1Current page number
X-Page-Size20Items per page
ETagW/"..."Entity tag (if enabled)
Cache-Controlprivate, max-age=60Emitted when CacheControlMaxAgeSeconds > 0

Count Resources (HEAD)

HEAD /api/{route}

Returns only count information — no response body.

Query Parameters: Same as List (filters, search apply).

Response: 200 OK (empty body)

Response Headers: Same as List (X-Total-Count, X-Page, X-Page-Size). X-Page-Size reports the effective page size an equivalent GET would use (the requested size clamped to the resource's MaxPageSize).


Get Single Resource

GET /api/{route}/{id}

Returns a single resource by its primary key.

Query Parameters:

ParameterExampleDescription
expand?expand=authorInclude related resources
fields?fields=id,titleReturn only specified fields

Response: 200 OK

{
  "id": 1,
  "title": "My Post",
  "email": "alice@example.com"
}

Response Headers:

HeaderExampleDescription
ETagW/"AAAAAAB="Entity tag for concurrency

Errors: 404 Not Found if resource does not exist.


Create Resource

POST /api/{route}
Content-Type: application/json

Creates a new resource. Body must contain fields with CrudDto.Create flag.

Request Body:

{
  "email": "alice@example.com",
  "name": "Alice"
}

Response: 201 Created

{
  "id": 1,
  "email": "alice@example.com",
  "name": "Alice",
  "createdAt": "2024-12-28T14:30:00Z"
}

The Location header contains a relative, URL-encoded URI to the created resource (e.g., /api/users/1).

Errors:

  • 400 Bad Request — Validation failure (missing required fields, invalid values, unknown fields)
  • 401 Unauthorized — Authentication required
  • 403 Forbidden — Authorization denied

Partial Update

PATCH /api/{route}/{id}
Content-Type: application/json
If-Match: W/"AAAAAAB="

Updates only the provided fields. Omitted fields remain unchanged.

Request Body:

{
  "name": "Alice Johnson"
}

Response: 200 OK with updated resource.

Errors:

  • 400 Bad Request — Validation failure (immutable fields, invalid values)
  • 404 Not Found — Resource does not exist
  • 409 Conflict — Concurrency conflict (ETag mismatch)

Full Replacement

PUT /api/{route}/{id}
Content-Type: application/json
If-Match: W/"AAAAAAB="

Replaces all updatable fields. All updatable fields must be provided.

Requires: EnablePutForFullUpdate = true in DataSurfaceHttpOptions.

Response: 200 OK with updated resource.

Errors: Same as PATCH, plus 400 if required fields are missing.


Delete Resource

DELETE /api/{route}/{id}

Deletes a resource. For ISoftDelete entities, sets IsDeleted = true.

Response: 204 No Content

Errors:

  • 404 Not Found — Resource does not exist
  • 401 Unauthorized / 403 Forbidden — Access denied

Bulk Endpoint

POST /api/{route}/bulk
Content-Type: application/json

Requires: EnableBulkOperations = true in DataSurfaceHttpOptions (the IDataSurfaceBulkService is registered by AddDataSurfaceEfCore).

Batch create, update, and delete operations. Each non-empty section of the request (create / update / delete) is authorized against that operation's policy — a caller with only the Create policy cannot mass-update or mass-delete through /bulk. See Bulk Operations.


Streaming Endpoint

GET /api/{route}/stream

Requires: EnableStreaming = true in DataSurfaceHttpOptions (the IDataSurfaceStreamingService is registered by AddDataSurfaceEfCore).

Returns NDJSON (newline-delimited JSON). See Streaming.

Content-Type: application/x-ndjson


Import/Export Endpoints

Requires: EnableImportExport = true in DataSurfaceHttpOptions.

Export

GET /api/{route}/export?format=json
GET /api/{route}/export?format=csv

Export materializes the whole result set in memory, capped at MaxExportRows (default 100000); when the cap is reached the export is truncated and the X-Export-Truncated: true response header is added.

Import

POST /api/{route}/import
Content-Type: application/json

See Import/Export.


Schema Endpoint

GET /api/$schema/{resourceKey}

Requires: MapSchemaEndpoint = true (the default; set to false to disable).

Returns JSON Schema for a resource. The segment matches the resource key or the route (case-insensitive). See OpenAPI Integration.


Resource Discovery

GET /api/$resources

Requires: MapResourceDiscoveryEndpoint = true (opt-in; default false).

Lists all registered resources (static and dynamic) with metadata.

Both the schema and discovery endpoints participate in the same default authorization, API key authentication, and rate limiting as the CRUD endpoints.


Dynamic Resource Endpoints

When MapDynamicCatchAll = true, dynamic resources are served under a separate prefix:

GET    /api/d/{route}
HEAD   /api/d/{route}
GET    /api/d/{route}/{id}
POST   /api/d/{route}
PATCH  /api/d/{route}/{id}
DELETE /api/d/{route}/{id}

The dynamic prefix is configurable via DynamicPrefix (default: "/d").


Admin Endpoints

Requires: DataSurface.Admin package and MapDataSurfaceAdmin().

MethodPathDescription
GET{prefix}/entitiesList all entity definitions
GET{prefix}/entities/{key}Get single entity definition
PUT{prefix}/entities/{key}Create or update entity definition
DELETE{prefix}/entities/{key}Delete entity definition
GET{prefix}/exportExport all definitions as JSON
POST{prefix}/importImport definitions from JSON
POST{prefix}/entities/{key}/reindexRebuild search indexes

Default prefix: /admin/ds.


Filter Operators

OperatorExampleDescription
eqfilter[status]=eq:activeEquals (default if omitted)
neqfilter[status]=neq:deletedNot equals
gtfilter[price]=gt:100Greater than
gtefilter[price]=gte:100Greater than or equal
ltfilter[price]=lt:50Less than
ltefilter[price]=lte:50Less than or equal
containsfilter[name]=contains:johnString contains
startsfilter[name]=starts:johnString starts with
endsfilter[name]=ends:sonString ends with
infilter[status]=in:a|b|cIn list (pipe-separated)
isnullfilter[email]=isnull:trueIs null / is not null

Only these operator prefixes are recognized — a value containing : after an unknown prefix (e.g., an ISO timestamp filter[createdAt]=2024-12-28T14:30:00Z) is treated as a plain equality value.