Azure / Azure/data-api-builder

[Enh]: Support Web Hooks

Ouverte
#3,230 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
2.x mcp-server
Langage dominant
C#
Étoiles
1.5k
Forks
370
Merge moyen
3 j 22 h
PR mergées (30 j)
9

Description

## 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')"
```

Guide de contribution

Ouvrir le guide de contribution

Piste de recherche

L’issue ne nomme pas les fichiers d’implémentation, les tests ni les points d’entrée. Commencez par localiser le pipeline de requêtes pour les opérations CRUD REST, GraphQL et MCP, puis déterminez où s’intégreraient la configuration, les appels HTTP sortants et le travail asynchrone après l’opération. La tâche est terminée lorsque le comportement et la configuration documentés des webhooks Before et After sont implémentés pour toutes les opérations prises en charge, avec une couverture appropriée.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
azure, csharp, graphql
Domaine
api, backend-api-design
Type d'issue
Fonctionnalité
Difficulté
5/5
Temps estimé
Plus d'une semaine
Activité
À l'abandon
Clarté
Plutôt claire
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.