Azure / Azure/data-api-builder
[Enh]: Support Web Hooks
- Lenguaje dominante
- C#
- Estrellas
- 1.5k
- Forks
- 370
- Merge medio
- 3 d 22 h
- PR fusionados (30 d)
- 9
Descripción
## What?
Support web hooks that trigger before and after CRUD operations on an entity.
* Allows an external service to enforce custom business rules.
* Allows centralized validation across multiple APIs or services.
* Enables auditing and compliance logging of data access and mutations.
* Enables integration with messaging or event systems.
* Enables cache invalidation or search index updates after data changes.
* Enables security checks such as fraud detection or policy enforcement.
* Enables observability and telemetry for downstream monitoring systems.
> [!NOTE]
> This feature supports any HTTP webhook endpoint, including services that publish events to external systems or HTTP-triggered Azure Functions.
## Process flow
```mermaid
sequenceDiagram
participant Client
participant DAB
participant Before as Before Hook
participant DB as Database
participant After as After Hook
Client->>DAB: Request
DAB->>Before: POST
alt stop
Before-->>DAB: stop
DAB-->>Client: Error response
else non-2xx
Before-->>DAB: non-2xx
DAB-->>Client: Error response
else 2xx
Before-->>DAB: Continue
DAB->>DB: CRUD
DB-->>DAB: Result
DAB-->>Client: Response
DAB-->>+After: POST (async)
end
```
## Before
### Outbound payload (to the webhook)
```json
{
"action": "create | read | update | delete | execute",
"entity": "Book",
"timestamp": "2026-03-14T18:22:11Z",
"correlationId": "7b9c5c3e-21d3-4c64-a9e2-4e2e4b0f4f7c",
"source": "rest | graphql | mcp",
"userRole": "authenticated",
"requestData": {
"id": 10,
"title": "Dune",
"author": "Frank Herbert"
}
}
```
| Field | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `action` | Operation that triggered the hook: `create`, `read`, `update`, `delete`, or `execute`. |
| `entity` | Entity name defined in the DAB configuration. |
| `timestamp` | UTC time when the event occurred. |
| `correlationId` | Unique identifier used to correlate the webhook with the original API request. |
| `source` | API surface that triggered the operation: `rest`, `graphql`, or `mcp`. |
| `userRole` | Role assigned to the caller after DAB authorization. |
| `requestData` | Data submitted in the API request that triggered the operation. For `read`, this may contain query parameters or filter information rather than entity data. |
### Inbound payload (from the webhook)
> [!IMPORTANT]
> A webhook may return HTTP status `200` with no payload. In this case, the request proceeds unchanged.
```json
{
"correlationId": "7b9c5c3e-21d3-4c64-a9e2-4e2e4b0f4f7c",
"updateRequestData": {
"title": "New value"
}
}
```
| Field | Description |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `correlationId` | Identifier matching the original webhook request so DAB can associate the response with the correct operation. Generated from the OpenTelemetry trace or span context. |
| `updateRequestData` | Optional object containing field changes the webhook wants to apply before the operation continues. |
The before-hook can return updateRequestData to mutate the request.
1. Any values in `updateRequestData` not in the request are ignored.
2. Any fields, including the primary key fields can be modified.
3. Request substitutions occur before mutation, so authentication is not impacted.
#### Abort example
```json
{
"correlationId": "7b9c5c3e-21d3-4c64-a9e2-4e2e4b0f4f7c",
"stop": {
"reason": "error message"
}
}
```
| Field | Description |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `correlationId` | Identifier matching the original webhook request so DAB can associate the response with the correct operation. Generated from the OpenTelemetry trace or span context. |
| `stop` | Indicates the operation should not continue. |
| `stop.reason` | Human readable explanation for why the request should be rejected. Returned to the client as the error message. |
### Automatic outbound headers
```
x-dab-action: create
x-dab-entity: Book
x-dab-source: rest
x-dab-correlation-id: 7b9c5c3e-21d3-4c64-a9e2-4e2e4b0f4f7c
```
> [!NOTE]
> This is valuable for programmability, but also for telemetry and logging.
## After
### Outbound payload (to the webhook)
```json
{
"action": "create | read | update | delete | execute",
"entity": "Book",
"timestamp": "2026-03-14T18:22:11Z",
"correlationId": "7b9c5c3e-21d3-4c64-a9e2-4e2e4b0f4f7c",
"source": "rest | graphql | mcp",
"userRole": "authenticated",
"result": "success | error",
"responseData": {
"id": 10,
"title": "Dune",
"author": "Frank Herbert"
}
}
```
| Field | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `action` | Operation that triggered the hook: `create`, `read`, `update`, `delete`, or `execute`. |
| `entity` | Entity name defined in the DAB configuration. |
| `timestamp` | UTC time when the event occurred. |
| `correlationId` | Identifier used to correlate the webhook with the originating API request. Generated from the OpenTelemetry trace or span context. |
| `source` | API surface that triggered the operation: `rest`, `graphql`, or `mcp`. |
| `userRole` | Role assigned to the caller after DAB authorization. |
| `result` | Outcome of the database operation: `success` or `error`. |
| `responseData` | Record returned from the operation or the final persisted state of the entity. For `read`, this may contain query result metadata or returned records. |
### Inbound payload (from the webhook)
After is fire and forget, so there is no inbound payload.
### Automatic outbound headers
```
x-dab-action: create
x-dab-entity: Book
x-dab-source: rest
x-dab-correlation-id: 7b9c5c3e-21d3-4c64-a9e2-4e2e4b0f4f7c
```
> [!NOTE]
> These headers are identical to those sent for the `before` webhook.
## Configuration
```json
{
"entities": {
"Book": {
"webhooks": {
"before": {
"enabled": true,
"url": "https://example.com/hooks/before",
"actions": ["*"],
"timeout-seconds": 5,
"abort-on-failure": true
},
"after": {
"enabled": true,
"url": "https://example.com/hooks/after",
"actions": ["delete"]
},
"headers": {
"x-api-key": "@env('WEBHOOK_SECRET')"
}
}
}
}
}
```
> [!IMPORTANT]
> This feature supports `read`, but it is not recommended due to potential performance and event rate impact.
| Property | Default | Description |
| ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `before.enabled` | `false` | Enables the `before` webhook. Disabled by default to avoid unexpected request interception. |
| `before.url` | `""` | Endpoint DAB calls before executing the operation. Required when `before.enabled` is `true`. |
| `before.actions` | `[]` | Operations that trigger the `before` webhook: `create`, `read`, `update`, `delete`, `execute`. Empty means no actions are enabled. |
| `before.timeout-seconds` | `5` | Maximum time DAB waits for the webhook response before continuing or failing the request. |
| `before.abort-on-failure` | `true` | Determines behavior when the webhook cannot be reached or returns a non-2xx response. Does not apply when the webhook explicitly returns `stop`. |
| `after.enabled` | `false` | Enables the `after` webhook, which runs asynchronously after the operation completes. |
| `after.url` | `""` | Endpoint DAB calls after the operation completes. Required when `after.enabled` is `true`. |
| `after.actions` | `[]` | Operations that trigger the `after` webhook: `create`, `read`, `update`, `delete`, `execute`. Empty means no actions are enabled. |
| `headers` | `{}` | Optional HTTP headers included with webhook requests. Applied to both `before` and `after` hooks. |
### abort-on-failure
| Failure scenario | Example | Should abort? |
| -------------------------- | ---------------------------------------- | ------------- |
| **Connection refused** | Webhook endpoint is down | Yes |
| **DNS resolution failure** | URL is misconfigured | Yes |
| **Timeout exceeded** | Endpoint hangs beyond `timeout-seconds` | Yes |
| **HTTP 5xx** | Webhook server error | Yes |
| **HTTP 4xx** | Webhook explicitly rejects the operation | Yes |
| **HTTP 2xx** | Success | No |
> [!NOTE]
> The contract is simple: anything other than a 2xx response, or an inability to get one, is treated as a failure.
> [!IMPORTANT]
> The `abort-on-failure` setting is independent of a `stop` instruction returned in a `before` webhook response. A `stop` always rejects the request regardless of the `abort-on-failure` value.
## Command Line
```
dab update Book --webhooks.before.enabled true
dab update Book --webhooks.before.url "https://example.com/hooks/before"
dab update Book --webhooks.before.actions "create,read,update,delete,execute"
dab update Book --webhooks.before.timeout-seconds 5
dab update Book --webhooks.before.abort-on-failure true
dab update Book --webhooks.after.enabled true
dab update Book --webhooks.after.url "https://example.com/hooks/after"
dab update Book --webhooks.after.actions "create,read,update,delete,execute"
dab update Book --webhooks.headers "x-api-key:@env('WEBHOOK_SECRET')"
```
Guía de contribución
Línea de trabajo
El issue no nombra archivos de implementación, pruebas ni puntos de entrada. Empieza por localizar la canalización de solicitudes para las operaciones CRUD de REST, GraphQL y MCP, y después determina dónde encajarían la configuración, las llamadas HTTP salientes y el trabajo asíncrono posterior a la operación. Se considera terminado cuando el comportamiento y la configuración documentados de los webhooks Before y After están implementados para todas las operaciones compatibles y cuentan con la cobertura adecuada.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- azure, csharp, graphql
- Área
- api, backend-api-design
- Tipo de issue
- Nueva funcionalidad
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Estado de actividad
- Estancado
- Claridad
- Bastante claro
- Aptitud para principiantes
- 25/100