meadow-endpoints 4.0.7 → 4.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/README.md ADDED
@@ -0,0 +1,326 @@
1
+ # Meadow Endpoints
2
+
3
+ > Automagic REST endpoints for CRUD operations on the Retold framework
4
+
5
+ Meadow Endpoints generates a full suite of RESTful API routes from your Meadow data entities. Define your data model with Meadow, point Meadow Endpoints at it, and you get Create, Read, Update, Delete, Count, Schema, and Validate endpoints -- with authentication hooks, authorization, dynamic filtering, pagination, and behavior injection for customization. The design philosophy is to cover the 99% via configuration; the last 1% is easily hand-craftable.
6
+
7
+ ## Features
8
+
9
+ - **Automatic Route Generation** - Full CRUD endpoints from a Meadow entity with zero boilerplate
10
+ - **Behavior Injection** - Pre/post operation hooks for authentication, authorization, and custom logic
11
+ - **Dynamic Filtering** - URL-based filter expressions for flexible querying
12
+ - **Pagination** - Built-in Begin/Cap parameters with configurable default limits
13
+ - **Bulk Operations** - Batch create, update, and upsert endpoints
14
+ - **Schema Endpoints** - Serve JSON Schema, default objects, and validation
15
+ - **Streaming Responses** - Large recordsets streamed as newline-delimited JSON
16
+ - **Controller Architecture** - Extensible controller system with replaceable error handling, logging, and session management
17
+ - **Fable Integration** - First-class service in the Fable/Orator ecosystem
18
+
19
+ ## Quick Start
20
+
21
+ ```javascript
22
+ const libFable = require('fable');
23
+ const libOrator = require('orator');
24
+ const libOratorServiceServerRestify = require('orator-serviceserver-restify');
25
+ const libMeadow = require('meadow');
26
+ const libMeadowEndpoints = require('meadow-endpoints');
27
+
28
+ const _Fable = new libFable({
29
+ Product: 'MyAPI',
30
+ ProductVersion: '1.0.0',
31
+ ServicePort: 8086
32
+ });
33
+
34
+ // Set up Orator
35
+ _Fable.serviceManager.addServiceType('Orator', libOrator);
36
+ _Fable.serviceManager.addServiceType('OratorServiceServer', libOratorServiceServerRestify);
37
+ _Fable.serviceManager.instantiateServiceProvider('Orator');
38
+ _Fable.serviceManager.instantiateServiceProvider('OratorServiceServer');
39
+
40
+ // Create a Meadow DAL for "Book"
41
+ const tmpMeadow = libMeadow.new(_Fable, 'Book')
42
+ .setProvider('MySQL')
43
+ .setDefaultIdentifier('IDBook')
44
+ .setSchema([
45
+ { Column: 'IDBook', Type: 'AutoIdentity' },
46
+ { Column: 'GUIDBook', Type: 'AutoGUID' },
47
+ { Column: 'Title', Type: 'String', Size: '255' },
48
+ { Column: 'Author', Type: 'String', Size: '128' },
49
+ { Column: 'CreateDate', Type: 'CreateDate' },
50
+ { Column: 'CreatingIDUser', Type: 'CreateIDUser' },
51
+ { Column: 'UpdateDate', Type: 'UpdateDate' },
52
+ { Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
53
+ { Column: 'Deleted', Type: 'Deleted' }
54
+ ]);
55
+
56
+ // Generate and connect endpoints
57
+ const tmpMeadowEndpoints = new libMeadowEndpoints(tmpMeadow);
58
+ tmpMeadowEndpoints.connectRoutes(_Fable.Orator.serviceServer);
59
+
60
+ // Start the server
61
+ _Fable.Orator.startService(() =>
62
+ {
63
+ console.log('API running on port 8086');
64
+ // Now available:
65
+ // GET /1.0/Book/:IDBook - Read one
66
+ // GET /1.0/Book/s/:Begin/:Cap - Read many
67
+ // POST /1.0/Book - Create
68
+ // PUT /1.0/Book - Update
69
+ // DELETE /1.0/Book/:IDBook - Delete
70
+ // GET /1.0/Book/s/Count - Count
71
+ // GET /1.0/Book/Schema - Schema
72
+ // ... and more
73
+ });
74
+ ```
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ npm install meadow-endpoints
80
+ ```
81
+
82
+ ## How It Works
83
+
84
+ Meadow Endpoints takes a configured Meadow DAL instance and registers HTTP routes with an Orator service server. Each route follows an async waterfall pattern with behavior injection points, allowing you to customize any step of the request lifecycle without replacing the endpoint implementation.
85
+
86
+ ```
87
+ Orator (API Server)
88
+ └── Meadow Endpoints (Route Registration)
89
+ ├── Controller (Request Lifecycle)
90
+ │ ├── Session Marshaler (Authentication)
91
+ │ ├── Behavior Injection (Authorization & Custom Logic)
92
+ │ ├── Error Handler (Error Responses)
93
+ │ └── Log Controller (Request Logging)
94
+ ├── Endpoints (Route Handlers)
95
+ │ ├── Create / BulkCreate
96
+ │ ├── Read / Reads / ReadSelectList / ReadLiteList / ReadDistinctList
97
+ │ ├── Update / BulkUpdate / Upsert / BulkUpsert
98
+ │ ├── Delete / Undelete
99
+ │ ├── Count / CountBy
100
+ │ └── Schema / New / Validate
101
+ └── Meadow DAL (Data Access)
102
+ └── Database Provider
103
+ ```
104
+
105
+ ## Generated Routes
106
+
107
+ ### Create
108
+
109
+ | Method | Route | Description |
110
+ |--------|-------|-------------|
111
+ | POST | `/{version}/{entity}` | Create a single record |
112
+ | POST | `/{version}/{entity}/s` | Bulk create (array of records) |
113
+
114
+ ### Read
115
+
116
+ | Method | Route | Description |
117
+ |--------|-------|-------------|
118
+ | GET | `/{version}/{entity}/:IDRecord` | Read a single record by ID |
119
+ | GET | `/{version}/{entity}/s` | Read records (default cap: 250) |
120
+ | GET | `/{version}/{entity}/s/:Begin/:Cap` | Read with pagination |
121
+ | GET | `/{version}/{entity}/s/FilteredTo/:Filter` | Read with filter |
122
+ | GET | `/{version}/{entity}/s/FilteredTo/:Filter/:Begin/:Cap` | Read with filter and pagination |
123
+ | GET | `/{version}/{entity}/s/By/:ByField/:ByValue` | Read filtered by field value |
124
+ | GET | `/{version}/{entity}/s/By/:ByField/:ByValue/:Begin/:Cap` | Field filter with pagination |
125
+ | GET | `/{version}/{entity}/Max/:ColumnName` | Get maximum value for a column |
126
+
127
+ ### Specialized Read
128
+
129
+ | Method | Route | Description |
130
+ |--------|-------|-------------|
131
+ | GET | `/{version}/{entity}/Select` | Select list ({Hash, Value} pairs) |
132
+ | GET | `/{version}/{entity}/Select/:Begin/:Cap` | Paginated select list |
133
+ | GET | `/{version}/{entity}/Select/FilteredTo/:Filter` | Filtered select list |
134
+ | GET | `/{version}/{entity}/s/Lite` | Lite list (minimal columns) |
135
+ | GET | `/{version}/{entity}/s/Lite/:Begin/:Cap` | Paginated lite list |
136
+ | GET | `/{version}/{entity}/s/LiteExtended/:ExtraColumns` | Lite list with extra columns |
137
+ | GET | `/{version}/{entity}/s/Distinct/:Columns` | Distinct values for columns |
138
+ | GET | `/{version}/{entity}/s/Distinct/:Columns/:Begin/:Cap` | Paginated distinct list |
139
+ | GET | `/{version}/{entity}/s/Distinct/:Columns/FilteredTo/:Filter` | Filtered distinct list |
140
+
141
+ ### Update
142
+
143
+ | Method | Route | Description |
144
+ |--------|-------|-------------|
145
+ | PUT | `/{version}/{entity}` | Update a single record |
146
+ | PUT | `/{version}/{entity}/s` | Bulk update (array of records) |
147
+ | PUT | `/{version}/{entity}/Upsert` | Insert or update a record |
148
+ | PUT | `/{version}/{entity}/Upserts` | Bulk upsert (array of records) |
149
+
150
+ ### Delete
151
+
152
+ | Method | Route | Description |
153
+ |--------|-------|-------------|
154
+ | DELETE | `/{version}/{entity}/:IDRecord` | Delete a record |
155
+ | DELETE | `/{version}/{entity}` | Delete (ID in request body) |
156
+ | GET | `/{version}/{entity}/Undelete/:IDRecord` | Undelete a soft-deleted record |
157
+
158
+ ### Count
159
+
160
+ | Method | Route | Description |
161
+ |--------|-------|-------------|
162
+ | GET | `/{version}/{entity}/s/Count` | Total record count |
163
+ | GET | `/{version}/{entity}/s/Count/FilteredTo/:Filter` | Filtered count |
164
+ | GET | `/{version}/{entity}/s/Count/By/:ByField/:ByValue` | Count by field value |
165
+
166
+ ### Schema
167
+
168
+ | Method | Route | Description |
169
+ |--------|-------|-------------|
170
+ | GET | `/{version}/{entity}/Schema` | Return JSON Schema |
171
+ | GET | `/{version}/{entity}/Schema/New` | Return empty default record |
172
+ | POST | `/{version}/{entity}/Schema/Validate` | Validate a record against schema |
173
+
174
+ ## Behavior Injection
175
+
176
+ Behavior injection lets you hook into any endpoint's lifecycle without replacing the endpoint itself. This is the primary mechanism for adding authentication, authorization, and custom business logic.
177
+
178
+ ### Injection Points
179
+
180
+ Each endpoint has named injection points following the pattern `{Verb}-{Phase}`:
181
+
182
+ | Injection Point | When It Runs |
183
+ |----------------|--------------|
184
+ | `Create-PreOperation` | Before record creation |
185
+ | `Create-QueryConfiguration` | After query is built, before execution |
186
+ | `Create-PostOperation` | After record is created |
187
+ | `Read-PreOperation` | Before single record read |
188
+ | `Read-QueryConfiguration` | After read query is built |
189
+ | `Read-PostOperation` | After record is retrieved |
190
+ | `Reads-QueryConfiguration` | After reads query is built |
191
+ | `Reads-PostOperation` | After recordset is retrieved |
192
+ | `Update-QueryConfiguration` | After update query is built |
193
+ | `Delete-QueryConfiguration` | After delete query is built |
194
+ | `Delete-PreOperation` | Before deletion (after pre-read) |
195
+ | `Delete-PostOperation` | After deletion |
196
+ | `Count-QueryConfiguration` | After count query is built |
197
+
198
+ ### Registering Behaviors
199
+
200
+ ```javascript
201
+ const tmpEndpoints = new libMeadowEndpoints(tmpMeadow);
202
+
203
+ // Add authorization: restrict reads to user's own customer
204
+ tmpEndpoints.controller.BehaviorInjection.setBehavior('Reads-QueryConfiguration',
205
+ (pRequest, pRequestState, fCallback) =>
206
+ {
207
+ if (pRequestState.SessionData.UserRoleIndex < 5)
208
+ {
209
+ pRequestState.Query.addFilter('IDCustomer', pRequestState.SessionData.CustomerID);
210
+ }
211
+ return fCallback();
212
+ });
213
+
214
+ // Add validation before create
215
+ tmpEndpoints.controller.BehaviorInjection.setBehavior('Create-PreOperation',
216
+ (pRequest, pRequestState, fCallback) =>
217
+ {
218
+ if (!pRequest.body.Title)
219
+ {
220
+ return fCallback({ Code: 400, Message: 'Title is required' });
221
+ }
222
+ return fCallback();
223
+ });
224
+ ```
225
+
226
+ ## Dynamic Filtering
227
+
228
+ Filter expressions can be passed in endpoint URLs using the `FilteredTo` parameter:
229
+
230
+ ```
231
+ GET /1.0/Book/s/FilteredTo/Title~JavaScript
232
+ GET /1.0/Book/s/FilteredTo/Author=Frank Herbert
233
+ GET /1.0/Book/s/FilteredTo/Price>20
234
+ ```
235
+
236
+ Supported operators: `=`, `!=`, `>`, `<`, `>=`, `<=`, `~` (contains)
237
+
238
+ ## Session Management
239
+
240
+ Meadow Endpoints supports multiple session data sources, configured via `MeadowEndpointsSessionDataSource`:
241
+
242
+ | Source | Description |
243
+ |--------|-------------|
244
+ | `Request` (default) | Session from `pRequest.UserSession` (set by Orator middleware) |
245
+ | `Header` | JSON session from `x-trusted-session` HTTP header |
246
+ | `None` | No session data, uses defaults |
247
+
248
+ The session object provides `UserID`, `CustomerID`, `SessionID`, `UserRole`, `UserRoleIndex`, and `LoggedIn` to all endpoint operations.
249
+
250
+ ## Configuration
251
+
252
+ | Setting | Type | Default | Description |
253
+ |---------|------|---------|-------------|
254
+ | `MeadowEndpointVersion` | string | `"1.0"` | API version prefix in routes |
255
+ | `MeadowDefaultMaxCap` | number | `250` | Default pagination limit for reads |
256
+ | `MeadowEndpointsSessionDataSource` | string | `"Request"` | Session data source |
257
+ | `MeadowEndpointsDefaultSessionObject` | object | `{CustomerID:0, UserID:0, ...}` | Default session template |
258
+ | `SendErrorStatusCodes` | boolean | `false` | Set HTTP status codes on error responses |
259
+
260
+ ## Custom Controllers
261
+
262
+ For deep customization, extend the base controller:
263
+
264
+ ```javascript
265
+ const libMeadowEndpoints = require('meadow-endpoints');
266
+ const BaseController = libMeadowEndpoints.BaseController;
267
+
268
+ class CustomController extends BaseController
269
+ {
270
+ constructor(pMeadowEndpoints, pOptions)
271
+ {
272
+ super(pMeadowEndpoints, pOptions);
273
+ }
274
+ }
275
+
276
+ const tmpEndpoints = new libMeadowEndpoints(tmpMeadow,
277
+ { ControllerClass: CustomController });
278
+ ```
279
+
280
+ ## Disabling Endpoint Groups
281
+
282
+ ```javascript
283
+ const tmpEndpoints = new libMeadowEndpoints(tmpMeadow);
284
+
285
+ // Disable update and delete endpoints
286
+ tmpEndpoints._EnabledBehaviorSets.Update = false;
287
+ tmpEndpoints._EnabledBehaviorSets.Delete = false;
288
+
289
+ tmpEndpoints.connectRoutes(serviceServer);
290
+ // Only Create, Read, Count, and Schema endpoints will be registered
291
+ ```
292
+
293
+ ## CRUD Deep Dive
294
+
295
+ For detailed documentation on each endpoint group, including request lifecycles, behavior injection examples, and real-world usage patterns, see the [CRUD documentation](crud/README.md):
296
+
297
+ - [Create](crud/create.md) - Single and bulk record creation with pre/post hooks
298
+ - [Read](crud/read.md) - Records, lists, pagination, filtering, select lists, lite and distinct projections
299
+ - [Update](crud/update.md) - Single and bulk updates, upserts
300
+ - [Delete](crud/delete.md) - Soft-delete and undelete with authorization hooks
301
+ - [Count](crud/count.md) - Record counts with filtering
302
+ - [Schema, New & Validate](crud/schema.md) - Metadata, default records, and validation with full hook examples
303
+
304
+ ## Testing
305
+
306
+ ```bash
307
+ npm test
308
+ ```
309
+
310
+ ## Documentation
311
+
312
+ Detailed documentation is available in the `docs/` folder and can be served locally:
313
+
314
+ ```bash
315
+ npx docsify-cli serve docs
316
+ ```
317
+
318
+ ## Related Packages
319
+
320
+ - [meadow](https://github.com/stevenvelozo/meadow) - Data access layer (required)
321
+ - [meadow-filter](https://github.com/stevenvelozo/meadow-filter) - URL filter expression parser
322
+ - [orator](https://github.com/stevenvelozo/orator) - API server abstraction
323
+ - [orator-serviceserver-restify](https://github.com/stevenvelozo/orator-serviceserver-restify) - Restify service server
324
+ - [stricture](https://github.com/stevenvelozo/stricture) - Schema definition language
325
+ - [foxhound](https://github.com/stevenvelozo/foxhound) - Query DSL for SQL generation
326
+ - [fable](https://github.com/stevenvelozo/fable) - Service provider framework
@@ -0,0 +1,37 @@
1
+ - Getting Started
2
+
3
+ - [Introduction](/)
4
+ - [Getting Started](getting-started.md)
5
+ - [Architecture](architecture.md)
6
+
7
+ - Core Concepts
8
+
9
+ - [Generated Routes](generated-routes.md)
10
+ - [Behavior Injection](behavior-injection.md)
11
+ - [Dynamic Filtering](dynamic-filtering.md)
12
+ - [Session Management](session-management.md)
13
+
14
+ - Endpoints
15
+
16
+ - [Create Endpoints](endpoints/create.md)
17
+ - [Read Endpoints](endpoints/read.md)
18
+ - [Update Endpoints](endpoints/update.md)
19
+ - [Delete Endpoints](endpoints/delete.md)
20
+ - [Count Endpoints](endpoints/count.md)
21
+ - [Schema Endpoints](endpoints/schema.md)
22
+
23
+ - CRUD Deep Dive
24
+
25
+ - [Overview](crud/README.md)
26
+ - [Create](crud/create.md)
27
+ - [Read](crud/read.md)
28
+ - [Update](crud/update.md)
29
+ - [Delete](crud/delete.md)
30
+ - [Count](crud/count.md)
31
+ - [Schema, New & Validate](crud/schema.md)
32
+
33
+ - Advanced
34
+
35
+ - [Custom Controllers](custom-controllers.md)
36
+ - [Error Handling](error-handling.md)
37
+ - [Configuration Reference](configuration.md)
@@ -0,0 +1,220 @@
1
+ # CRUD Endpoints
2
+
3
+ > Complete REST API lifecycle from a single entity definition
4
+
5
+ Meadow Endpoints generates a full suite of RESTful routes from a configured Meadow entity. Every endpoint follows the same async waterfall pattern with behavior injection hooks, making the entire request lifecycle customizable without replacing any built-in logic.
6
+
7
+ ## Endpoint Groups
8
+
9
+ | Group | Operations | Description |
10
+ |-------|-----------|-------------|
11
+ | [Create](crud/create.md) | Create, BulkCreate | Insert new records |
12
+ | [Read](crud/read.md) | Read, Reads, ReadsBy, ReadMax, ReadSelectList, ReadLiteList, ReadDistinctList | Retrieve records with pagination, filtering, and projections |
13
+ | [Update](crud/update.md) | Update, BulkUpdate, Upsert, BulkUpsert | Modify existing records |
14
+ | [Delete](crud/delete.md) | Delete, Undelete | Soft-delete and restore records |
15
+ | [Count](crud/count.md) | Count, CountBy | Aggregate record counts |
16
+ | [Schema](crud/schema.md) | Schema, New, Validate | Serve schema metadata, default objects, and validation |
17
+
18
+ ## Complete Route Table
19
+
20
+ All routes are prefixed with `/{version}/{entity}`, where `version` defaults to `1.0` and `entity` is the Meadow DAL scope (e.g., `Book`).
21
+
22
+ ### Create
23
+
24
+ | Method | Route | Handler |
25
+ |--------|-------|---------|
26
+ | POST | `/{v}/{entity}` | Create a single record |
27
+ | POST | `/{v}/{entity}/s` | Bulk create (array of records) |
28
+
29
+ ### Read
30
+
31
+ | Method | Route | Handler |
32
+ |--------|-------|---------|
33
+ | GET | `/{v}/{entity}/:IDRecord` | Read one record by ID |
34
+ | GET | `/{v}/{entity}/By/:GUIDRecord` | Read one record by GUID |
35
+ | GET | `/{v}/{entity}/s` | Read list (default cap: 250) |
36
+ | GET | `/{v}/{entity}/s/:Begin/:Cap` | Read list with pagination |
37
+ | GET | `/{v}/{entity}/s/FilteredTo/:Filter` | Read list with filter |
38
+ | GET | `/{v}/{entity}/s/FilteredTo/:Filter/:Begin/:Cap` | Filtered read with pagination |
39
+ | GET | `/{v}/{entity}/s/By/:ByField/:ByValue` | Read by field value |
40
+ | GET | `/{v}/{entity}/s/By/:ByField/:ByValue/:Begin/:Cap` | Read by field with pagination |
41
+ | GET | `/{v}/{entity}/Max/:ColumnName` | Maximum value for a column |
42
+
43
+ ### Specialized Read
44
+
45
+ | Method | Route | Handler |
46
+ |--------|-------|---------|
47
+ | GET | `/{v}/{entity}/Select` | Select list ({Hash, Value} pairs) |
48
+ | GET | `/{v}/{entity}/Select/:Begin/:Cap` | Paginated select list |
49
+ | GET | `/{v}/{entity}/Select/FilteredTo/:Filter` | Filtered select list |
50
+ | GET | `/{v}/{entity}/Select/FilteredTo/:Filter/:Begin/:Cap` | Filtered paginated select list |
51
+ | GET | `/{v}/{entity}/s/Lite` | Lite list (minimal columns) |
52
+ | GET | `/{v}/{entity}/s/Lite/:Begin/:Cap` | Paginated lite list |
53
+ | GET | `/{v}/{entity}/s/LiteExtended/:ExtraColumns` | Lite list with extra columns |
54
+ | GET | `/{v}/{entity}/s/Distinct/:Columns` | Distinct values for columns |
55
+ | GET | `/{v}/{entity}/s/Distinct/:Columns/:Begin/:Cap` | Paginated distinct list |
56
+ | GET | `/{v}/{entity}/s/Distinct/:Columns/FilteredTo/:Filter` | Filtered distinct list |
57
+
58
+ ### Update
59
+
60
+ | Method | Route | Handler |
61
+ |--------|-------|---------|
62
+ | PUT | `/{v}/{entity}` | Update a single record |
63
+ | PUT | `/{v}/{entity}/s` | Bulk update (array of records) |
64
+ | PUT | `/{v}/{entity}/Upsert` | Insert or update a record |
65
+ | PUT | `/{v}/{entity}/Upserts` | Bulk upsert (array of records) |
66
+
67
+ ### Delete
68
+
69
+ | Method | Route | Handler |
70
+ |--------|-------|---------|
71
+ | DELETE | `/{v}/{entity}/:IDRecord` | Delete a record by URL param |
72
+ | DELETE | `/{v}/{entity}` | Delete a record (ID in body) |
73
+ | GET | `/{v}/{entity}/Undelete/:IDRecord` | Undelete a soft-deleted record |
74
+
75
+ ### Count
76
+
77
+ | Method | Route | Handler |
78
+ |--------|-------|---------|
79
+ | GET | `/{v}/{entity}/s/Count` | Total record count |
80
+ | GET | `/{v}/{entity}/s/Count/FilteredTo/:Filter` | Filtered count |
81
+ | GET | `/{v}/{entity}/s/Count/By/:ByField/:ByValue` | Count by field value |
82
+
83
+ ### Schema
84
+
85
+ | Method | Route | Handler |
86
+ |--------|-------|---------|
87
+ | GET | `/{v}/{entity}/Schema` | Return JSON Schema |
88
+ | GET | `/{v}/{entity}/Schema/New` | Return empty default record |
89
+ | POST | `/{v}/{entity}/Schema/Validate` | Validate a record against schema |
90
+
91
+ ## Request Lifecycle
92
+
93
+ Every endpoint follows the same async waterfall pattern:
94
+
95
+ ```
96
+ HTTP Request
97
+ |
98
+ v
99
+ initializeRequestState(pRequest, 'OperationName')
100
+ |
101
+ v
102
+ [Pre-Operation Behavior Injection] <-- authenticate, authorize, validate
103
+ |
104
+ v
105
+ Build/Configure Query
106
+ |
107
+ v
108
+ [Query-Configuration Behavior Injection] <-- modify query, add filters
109
+ |
110
+ v
111
+ Execute DAL Operation (doCreate, doRead, doReads, etc.)
112
+ |
113
+ v
114
+ [Post-Operation Behavior Injection] <-- transform results, audit, notify
115
+ |
116
+ v
117
+ Send Response
118
+ |
119
+ v
120
+ ErrorHandler.handleErrorIfSet() <-- catch any errors from waterfall
121
+ ```
122
+
123
+ Not every endpoint has all three injection points. See the individual endpoint documentation for the specific hooks available.
124
+
125
+ ## Behavior Injection Summary
126
+
127
+ | Injection Point | Available On | Phase |
128
+ |-----------------|-------------|-------|
129
+ | `Create-PreOperation` | Create, BulkCreate | Before create query is built |
130
+ | `Create-QueryConfiguration` | Create, BulkCreate | After query is built, before execution |
131
+ | `Create-PostOperation` | Create, BulkCreate | After record is created and re-read |
132
+ | `Read-PreOperation` | Read | Before filter criteria are applied |
133
+ | `Read-QueryConfiguration` | Read | After filter criteria, before DAL call |
134
+ | `Read-PostOperation` | Read | After record is retrieved |
135
+ | `Reads-QueryConfiguration` | Reads, ReadsBy, ReadSelectList, ReadLiteList, ReadDistinctList, ReadMax | After pagination/filter, before DAL call |
136
+ | `Reads-PostOperation` | Reads, ReadsBy | After recordset is retrieved |
137
+ | `Update-PostOperation` | Update, BulkUpdate | After record is updated and re-read |
138
+ | `Delete-QueryConfiguration` | Delete | After delete query filter is built |
139
+ | `Delete-PreOperation` | Delete | After record is loaded, before delete |
140
+ | `Delete-PostOperation` | Delete | After record is deleted |
141
+ | `Undelete-PreOperation` | Undelete | After deleted record is loaded |
142
+ | `Undelete-PostOperation` | Undelete | After record is undeleted |
143
+ | `Count-QueryConfiguration` | Count | After filter is parsed |
144
+ | `CountBy-QueryConfiguration` | CountBy | After field filter is applied |
145
+ | `Schema-PreOperation` | Schema | Before schema is loaded |
146
+ | `Schema-PostOperation` | Schema | After schema is loaded |
147
+ | `New-PreOperation` | New | Before default object is built |
148
+ | `New-PostOperation` | New | After default object is built |
149
+ | `Validate-PreOperation` | Validate | Before validation runs |
150
+ | `Validate-PostOperation` | Validate | After validation completes |
151
+
152
+ ## Registering Behaviors
153
+
154
+ ```javascript
155
+ const tmpEndpoints = new libMeadowEndpoints(tmpMeadow);
156
+
157
+ tmpEndpoints.controller.BehaviorInjection.setBehavior('Create-PreOperation',
158
+ (pRequest, pRequestState, fCallback) =>
159
+ {
160
+ // pRequest - the HTTP request object (params, body, headers)
161
+ // pRequestState - endpoint state (Query, Record, SessionData, etc.)
162
+ // fCallback - call with no args to continue, or with error to halt
163
+
164
+ if (!pRequest.body.Title)
165
+ {
166
+ return fCallback({ Code: 400, Message: 'Title is required' });
167
+ }
168
+ return fCallback();
169
+ });
170
+ ```
171
+
172
+ ## Request State Object
173
+
174
+ The `pRequestState` object is initialized by `initializeRequestState()` and carries state through the waterfall. Key properties:
175
+
176
+ | Property | Type | Description |
177
+ |----------|------|-------------|
178
+ | `SessionData` | object | Session data (UserID, CustomerID, UserRole, etc.) |
179
+ | `Query` | object | FoxHound query object for the current operation |
180
+ | `Record` | object | The current record (after read/create/update) |
181
+ | `Records` | array | Record array (for Reads operations) |
182
+ | `Result` | object | Result object (for Count: `{Count: N}`) |
183
+ | `RecordCount` | object | Delete result: `{Count: N}` |
184
+ | `JSONSchema` | object | Schema object (for Schema endpoint) |
185
+ | `EmptyEntityRecord` | object | Default record (for New endpoint) |
186
+ | `RecordValidation` | object | Validation result (for Validate endpoint) |
187
+ | `RecordToCreate` | object | Record being created (in Create operation) |
188
+ | `RecordToModify` | object | Record being updated (in Update operation) |
189
+
190
+ ## Disabling Endpoint Groups
191
+
192
+ You can selectively disable any endpoint group before connecting routes:
193
+
194
+ ```javascript
195
+ const tmpEndpoints = new libMeadowEndpoints(tmpMeadow);
196
+
197
+ // Disable specific operations
198
+ tmpEndpoints._EnabledBehaviorSets.Update = false;
199
+ tmpEndpoints._EnabledBehaviorSets.Delete = false;
200
+
201
+ // Connect only the remaining routes
202
+ tmpEndpoints.connectRoutes(serviceServer);
203
+ ```
204
+
205
+ Available behavior sets: `Create`, `Read`, `Update`, `Delete`, `Count`, `Schema`.
206
+
207
+ ## Session Data
208
+
209
+ Every endpoint has access to session data through `pRequestState.SessionData`:
210
+
211
+ | Property | Type | Default | Description |
212
+ |----------|------|---------|-------------|
213
+ | `UserID` | number | `0` | Authenticated user ID |
214
+ | `CustomerID` | number | `0` | Customer/tenant ID |
215
+ | `SessionID` | string | `""` | Session identifier |
216
+ | `UserRole` | string | `"User"` | Role name |
217
+ | `UserRoleIndex` | number | `0` | Numeric role level |
218
+ | `LoggedIn` | boolean | `false` | Authentication status |
219
+
220
+ Session data is populated by the session marshaler before any behavior injection runs. Configure the source via `MeadowEndpointsSessionDataSource` (`Request`, `Header`, or `None`).