meadow-endpoints 4.0.5 → 4.0.9
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/CONTRIBUTING.md +50 -0
- package/Dockerfile_LUXURYCode +1 -39
- package/README.md +172 -51
- package/dist/indoctrinate_content_staging/Indoctrinate-Catalog-AppData.json +4548 -0
- package/docs/README.md +326 -0
- package/docs/_sidebar.md +37 -0
- package/docs/crud/README.md +220 -0
- package/docs/crud/count.md +168 -0
- package/docs/crud/create.md +194 -0
- package/docs/crud/delete.md +237 -0
- package/docs/crud/read.md +324 -0
- package/docs/crud/schema.md +349 -0
- package/docs/crud/update.md +203 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/index.html +39 -0
- package/docs/retold-catalog.json +177 -0
- package/docs/retold-keyword-index.json +6720 -0
- package/package.json +24 -19
- package/source/Meadow-Endpoints.js +8 -1
- package/source/controller/Meadow-Endpoints-Controller-Base.js +14 -7
- package/source/controller/components/Meadow-Endpoints-Controller-BehaviorInjection.js +4 -1
- package/source/controller/components/Meadow-Endpoints-Controller-Error.js +4 -1
- package/source/controller/components/Meadow-Endpoints-Controller-Log.js +4 -1
- package/source/controller/utility/Meadow-Endpoints-Filter-Parser.js +13 -204
- package/source/controller/utility/Meadow-Endpoints-Session-Marshaler.js +10 -6
- package/source/controller/utility/Meadow-Endpoints-Stream-RecordArray.js +5 -2
- package/source/endpoints/delete/Meadow-Endpoint-Undelete.js +2 -2
- package/source/endpoints/read/Meadow-Endpoint-ReadDistinctList.js +3 -1
- package/source/endpoints/read/Meadow-Endpoint-ReadLiteList.js +3 -1
- package/source/endpoints/read/Meadow-Endpoint-ReadSelectList.js +3 -1
- package/source/endpoints/read/Meadow-Endpoint-Reads.js +3 -1
- package/source/endpoints/read/Meadow-Endpoint-ReadsBy.js +3 -1
- package/source/endpoints/update/Meadow-Operation-Update.js +9 -1
- package/test/MeadowEndpoints_basic_tests.js +1580 -65
- package/tsconfig.json +13 -0
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
|
package/docs/_sidebar.md
ADDED
|
@@ -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`).
|