@promind/honey 1.47.13 → 1.47.24
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/package.json +6 -4
- package/README.md +0 -1041
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promind/honey",
|
|
3
|
-
"version": "1.47.
|
|
3
|
+
"version": "1.47.24",
|
|
4
4
|
"description": "An expressJS based library for simplifying the development of CRUD APIs using Postgres",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"engineStrict": true,
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "https://github.com/chinaza/honey.git"
|
|
17
|
+
"url": "git+https://github.com/chinaza/honey.git"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"dist"
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"test-watch": "jest --watch --passWithNoTests",
|
|
30
30
|
"start": "node dist/index.js",
|
|
31
31
|
"start:dev": "nodemon --exec ts-node --transpile-only ./src/index.ts",
|
|
32
|
-
"prepare": "npm run build",
|
|
33
32
|
"prepare:dev": "husky install",
|
|
34
33
|
"bump": "yarn changeset; yarn changeset version"
|
|
35
34
|
},
|
|
@@ -84,5 +83,8 @@
|
|
|
84
83
|
"sequelize": "^6.35.2",
|
|
85
84
|
"yamljs": "^0.3.0"
|
|
86
85
|
},
|
|
87
|
-
"packageManager": "yarn@4.9.4"
|
|
86
|
+
"packageManager": "yarn@4.9.4",
|
|
87
|
+
"publishConfig": {
|
|
88
|
+
"access": "public"
|
|
89
|
+
}
|
|
88
90
|
}
|
package/README.md
DELETED
|
@@ -1,1041 +0,0 @@
|
|
|
1
|
-
# HoneyJS
|
|
2
|
-
|
|
3
|
-
A TypeScript-based Node.js declarative library for building RESTful APIs with seamless integration to PostgreSQL databases using Sequelize ORM. HoneyJS streamlines the process of creating, reading, updating, deleting, and upserting data with a focus on simplicity and reusability.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Features](#features)
|
|
8
|
-
- [Prerequisites](#prerequisites)
|
|
9
|
-
- [Installation](#installation)
|
|
10
|
-
- [Quick Start](#quick-start)
|
|
11
|
-
- [Configuration](#configuration)
|
|
12
|
-
- [Database Connection](#database-connection)
|
|
13
|
-
- [Environment Variables](#environment-variables)
|
|
14
|
-
- [Built-in Defaults](#built-in-defaults)
|
|
15
|
-
- [API Reference](#api-reference)
|
|
16
|
-
- [Core Methods](#core-methods)
|
|
17
|
-
- [CRUD Operations](#crud-operations)
|
|
18
|
-
- [Advanced Usage](#advanced-usage)
|
|
19
|
-
- [Database Utilities](#database-utilities)
|
|
20
|
-
- [Error Handling](#error-handling)
|
|
21
|
-
- [Middleware](#middleware)
|
|
22
|
-
- [Examples](#examples)
|
|
23
|
-
- [Custom Express Routes](#custom-express-routes)
|
|
24
|
-
- [TypeScript Support](#typescript-support)
|
|
25
|
-
- [Contributing](#contributing)
|
|
26
|
-
- [License](#license)
|
|
27
|
-
|
|
28
|
-
## Features
|
|
29
|
-
|
|
30
|
-
- **Declarative API Definition**: Define your REST endpoints with simple configuration objects
|
|
31
|
-
- **Full CRUD Support**: Built-in controllers for create, read, update, delete, and upsert operations
|
|
32
|
-
- **PostgreSQL Integration**: Seamless connection to PostgreSQL via Sequelize ORM
|
|
33
|
-
- **Query Building**: Flexible filtering, sorting, and pagination
|
|
34
|
-
- **Middleware Support**: Add custom middleware to your routes
|
|
35
|
-
- **TypeScript Ready**: Full TypeScript support with comprehensive type definitions
|
|
36
|
-
- **Validation**: Request validation using Joi
|
|
37
|
-
- **Error Handling**: Consistent error handling and response formatting
|
|
38
|
-
- **API Documentation**: Automatic OpenAPI documentation is generated internally via `express-oas-generator`.
|
|
39
|
-
|
|
40
|
-
## Prerequisites
|
|
41
|
-
|
|
42
|
-
- Node.js (>=20.9.0)
|
|
43
|
-
- PostgreSQL database
|
|
44
|
-
- TypeScript (for TypeScript projects)
|
|
45
|
-
- Create the relevant tables and data models for your Postgres database. You can use the exposed sequelize utility functions and migrations to do this.
|
|
46
|
-
- See <https://github.com/chinaza/generator-honeyjs> to generate a project
|
|
47
|
-
|
|
48
|
-
## Installation
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
# Using npm
|
|
52
|
-
npm install @promind/honey
|
|
53
|
-
|
|
54
|
-
# Using yarn
|
|
55
|
-
yarn add @promind/honey
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Quick Start
|
|
59
|
-
|
|
60
|
-
1. Create a new file (e.g., `server.ts` or `server.js`)
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
import { createHoney } from '@promind/honey';
|
|
64
|
-
|
|
65
|
-
// Initialize HoneyJS with port and database connection string
|
|
66
|
-
const honey = createHoney(
|
|
67
|
-
'3000',
|
|
68
|
-
'postgresql://username:password@localhost:5432/database'
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
// Define a simple CRUD API for 'users' resource
|
|
72
|
-
honey.get({
|
|
73
|
-
resource: 'users',
|
|
74
|
-
fields: ['id', 'name', 'email', 'created_at']
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
honey.getById({
|
|
78
|
-
resource: 'users',
|
|
79
|
-
fields: ['id', 'name', 'email', 'created_at']
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
honey.create({
|
|
83
|
-
resource: 'users',
|
|
84
|
-
params: {
|
|
85
|
-
name: 'string',
|
|
86
|
-
email: 'string',
|
|
87
|
-
created_at: '@updatedAt'
|
|
88
|
-
},
|
|
89
|
-
message: 'User created successfully'
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
honey.updateById({
|
|
93
|
-
resource: 'users',
|
|
94
|
-
params: {
|
|
95
|
-
name: 'replace',
|
|
96
|
-
email: 'replace'
|
|
97
|
-
},
|
|
98
|
-
message: 'User updated successfully'
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
honey.deleteById({
|
|
102
|
-
resource: 'users',
|
|
103
|
-
message: 'User deleted successfully'
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// Start the server
|
|
107
|
-
honey.startServer();
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
1. Run your server:
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
# If using TypeScript
|
|
114
|
-
npx ts-node server.ts
|
|
115
|
-
|
|
116
|
-
# If using JavaScript
|
|
117
|
-
node server.js
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Your API will be available at `http://localhost:3000/api/users`
|
|
121
|
-
|
|
122
|
-
## Configuration
|
|
123
|
-
|
|
124
|
-
### Database Connection
|
|
125
|
-
|
|
126
|
-
You can connect to your PostgreSQL database using either a connection string or a configuration object:
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
// Using connection string
|
|
130
|
-
const honey = createHoney(
|
|
131
|
-
'3000',
|
|
132
|
-
'postgresql://username:password@localhost:5432/database'
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
// Using configuration object
|
|
136
|
-
const honey = createHoney('3000', {
|
|
137
|
-
host: 'localhost',
|
|
138
|
-
port: 5432,
|
|
139
|
-
user: 'username',
|
|
140
|
-
password: 'password',
|
|
141
|
-
database: 'database'
|
|
142
|
-
});
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Environment Variables
|
|
146
|
-
|
|
147
|
-
HoneyJS supports configuration through environment variables. Create a `.env` file in your project root:
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
PORT=3000
|
|
151
|
-
DB_URI=postgresql://username:password@localhost:5432/database
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Then initialize HoneyJS without parameters:
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
import { createHoney } from '@promind/honey';
|
|
158
|
-
|
|
159
|
-
const honey = createHoney(process.env.PORT, process.env.DB_URI);
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Built-in Defaults
|
|
163
|
-
|
|
164
|
-
HoneyJS automatically configures the following on startup:
|
|
165
|
-
|
|
166
|
-
- **CORS**: Enabled for all origins (`*`) with all methods and headers allowed
|
|
167
|
-
- **Body parsing**: JSON and URL-encoded bodies up to 50MB
|
|
168
|
-
- **Cookie parsing**: Enabled via `cookie-parser`
|
|
169
|
-
- **Route prefix**: `/api` (configurable via `metadata.routePrefix`)
|
|
170
|
-
|
|
171
|
-
## API Reference
|
|
172
|
-
|
|
173
|
-
### Core Methods
|
|
174
|
-
|
|
175
|
-
#### `createHoney(port, dbOptions, metadata?)`
|
|
176
|
-
|
|
177
|
-
Creates a new HoneyJS instance.
|
|
178
|
-
|
|
179
|
-
- `port`: Server port number or string
|
|
180
|
-
- `dbOptions`: PostgreSQL connection string or configuration object
|
|
181
|
-
- `metadata` (optional): Additional configuration options
|
|
182
|
-
- `fallbackErrorMessage`: Custom 404 error message
|
|
183
|
-
- `routePrefix`: API route prefix (default: `/api`)
|
|
184
|
-
|
|
185
|
-
Returns a `Honey` instance.
|
|
186
|
-
|
|
187
|
-
#### `honey.startServer()`
|
|
188
|
-
|
|
189
|
-
Starts the HTTP server.
|
|
190
|
-
|
|
191
|
-
#### `honey.addMiddleware(middleware[])`
|
|
192
|
-
|
|
193
|
-
Adds global middleware to all routes.
|
|
194
|
-
|
|
195
|
-
#### `honey.db`
|
|
196
|
-
|
|
197
|
-
Exposes the underlying Sequelize instance for direct database access.
|
|
198
|
-
|
|
199
|
-
### CRUD Operations
|
|
200
|
-
|
|
201
|
-
#### `honey.create(options)`
|
|
202
|
-
|
|
203
|
-
Creates a POST endpoint for creating new resources.
|
|
204
|
-
|
|
205
|
-
The response `data` field contains `{ id }` (the inserted record's ID) by default unless `processResponseData` is provided.
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
honey.create({
|
|
209
|
-
resource: 'posts', // Resource name (used in URL path)
|
|
210
|
-
table: 'blog_posts', // Optional: Table name if different from resource
|
|
211
|
-
params: {
|
|
212
|
-
// Request body parameters
|
|
213
|
-
title: 'string', // Parameter type validation
|
|
214
|
-
content: 'string',
|
|
215
|
-
author_id: 'number',
|
|
216
|
-
published: 'boolean',
|
|
217
|
-
metadata: 'json', // For JSON fields
|
|
218
|
-
created_at: '@updatedAt' // Special value to set current timestamp
|
|
219
|
-
},
|
|
220
|
-
message: 'Post created', // Success message
|
|
221
|
-
pathOverride: '/blog/posts', // Optional: Custom path
|
|
222
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
223
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
224
|
-
methodOverride: 'post', // Optional: Override the HTTP method
|
|
225
|
-
processResponseData: (data, req) => {
|
|
226
|
-
// Optional: Transform response data
|
|
227
|
-
return { ...data, extra: 'info' };
|
|
228
|
-
},
|
|
229
|
-
processErrorResponse: (err) => err // Optional: Customize error response
|
|
230
|
-
});
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
#### `honey.get(options)`
|
|
234
|
-
|
|
235
|
-
Creates a GET endpoint for retrieving a list of resources.
|
|
236
|
-
|
|
237
|
-
Automatically supports `?page=` and `?limit=` query parameters for pagination. The response shape is:
|
|
238
|
-
|
|
239
|
-
```json
|
|
240
|
-
{
|
|
241
|
-
"data": [...],
|
|
242
|
-
"meta": {
|
|
243
|
-
"pagination": {
|
|
244
|
-
"total": 100,
|
|
245
|
-
"pageSize": 10,
|
|
246
|
-
"page": 1,
|
|
247
|
-
"pageCount": 10
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
honey.get({
|
|
255
|
-
resource: 'posts',
|
|
256
|
-
fields: ['id', 'title', 'author_id', 'created_at'], // Fields to return
|
|
257
|
-
filter: {
|
|
258
|
-
// Optional: Query filters
|
|
259
|
-
title: {
|
|
260
|
-
operator: 'like', // Filter operator
|
|
261
|
-
value: 'string', // Parameter type
|
|
262
|
-
location: 'query' // Optional: where to read the value from (default: query string)
|
|
263
|
-
},
|
|
264
|
-
published: {
|
|
265
|
-
operator: '=',
|
|
266
|
-
value: 'boolean'
|
|
267
|
-
},
|
|
268
|
-
author_id: {
|
|
269
|
-
operator: '=',
|
|
270
|
-
value: 'number',
|
|
271
|
-
overrideValue: (req) => req.user?.id // Dynamic value based on request
|
|
272
|
-
},
|
|
273
|
-
$or: {
|
|
274
|
-
// Logical OR condition
|
|
275
|
-
title: {
|
|
276
|
-
operator: 'like',
|
|
277
|
-
value: 'string'
|
|
278
|
-
},
|
|
279
|
-
content: {
|
|
280
|
-
operator: 'like',
|
|
281
|
-
value: 'string'
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
},
|
|
285
|
-
format: {
|
|
286
|
-
// Optional: Sorting
|
|
287
|
-
sort: 'DESC', // ASC or DESC
|
|
288
|
-
sortField: 'created_at' // Field to sort by
|
|
289
|
-
},
|
|
290
|
-
joins: [
|
|
291
|
-
// Optional: SQL joins
|
|
292
|
-
{
|
|
293
|
-
table: 'users',
|
|
294
|
-
type: 'inner', // 'inner' | 'left' | 'right' | 'full' | 'cross' (default: 'inner')
|
|
295
|
-
on: {
|
|
296
|
-
left: 'posts.author_id',
|
|
297
|
-
right: 'users.id',
|
|
298
|
-
operator: '=' // '=' | '!=' | '<' | '<=' | '>' | '>='
|
|
299
|
-
},
|
|
300
|
-
alias: 'author' // Optional alias
|
|
301
|
-
}
|
|
302
|
-
],
|
|
303
|
-
shouldErrorOnNotFound: false, // Optional: When false, returns empty data instead of 404 (default: true)
|
|
304
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
305
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
306
|
-
methodOverride: 'get' // Optional: Override the HTTP method
|
|
307
|
-
});
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
**Filter operators:** `'='`, `'!='`, `'<'`, `'<='`, `'>'`, `'>='`, `'in'`, `'not in'`, `'like'`, `'not like'`
|
|
311
|
-
|
|
312
|
-
> **Note:** `'like'` uses PostgreSQL `ILIKE` for case-insensitive matching.
|
|
313
|
-
|
|
314
|
-
**Filter `value` types:** `'string'`, `'number'`, `'boolean'`, `'json'`, `'csv'` (splits comma-separated string into array), `'as-is'` (passes value through unchanged)
|
|
315
|
-
|
|
316
|
-
**Filter `location` options:** `'query'` (default), `'body'`, `'headers'`, `'request'`, `'params'`
|
|
317
|
-
|
|
318
|
-
**Filter `overrideValue`:** Can be a static value or a function `(req) => value` for dynamic values based on the request.
|
|
319
|
-
|
|
320
|
-
**Using joins with dot-notation:** When using joins, fields can use dot-notation to reference columns from joined tables: `'tableName.fieldName'`.
|
|
321
|
-
|
|
322
|
-
#### `honey.getById(options)`
|
|
323
|
-
|
|
324
|
-
Creates a GET endpoint for retrieving a single resource by ID.
|
|
325
|
-
|
|
326
|
-
```typescript
|
|
327
|
-
honey.getById({
|
|
328
|
-
resource: 'posts',
|
|
329
|
-
fields: ['id', 'title', 'content', 'author_id', 'created_at'],
|
|
330
|
-
idField: 'slug', // Optional: Use a different field as identifier (default: 'id')
|
|
331
|
-
filter: {
|
|
332
|
-
// Optional: Additional filters
|
|
333
|
-
published: {
|
|
334
|
-
operator: '=',
|
|
335
|
-
overrideValue: true // Force a value regardless of request
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
joins: [
|
|
339
|
-
// Optional: SQL joins (same as honey.get())
|
|
340
|
-
{
|
|
341
|
-
table: 'users',
|
|
342
|
-
type: 'left',
|
|
343
|
-
on: { left: 'posts.author_id', right: 'users.id' }
|
|
344
|
-
}
|
|
345
|
-
],
|
|
346
|
-
shouldErrorOnNotFound: false, // Optional: When false, returns empty data instead of 404 (default: true)
|
|
347
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
348
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
349
|
-
methodOverride: 'get' // Optional: Override the HTTP method
|
|
350
|
-
});
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
#### `honey.updateById(options)`
|
|
354
|
-
|
|
355
|
-
Creates a PUT endpoint for updating a resource by ID.
|
|
356
|
-
|
|
357
|
-
```typescript
|
|
358
|
-
honey.updateById({
|
|
359
|
-
resource: 'posts',
|
|
360
|
-
params: {
|
|
361
|
-
title: 'replace', // Replace the value
|
|
362
|
-
views: 'inc', // Increment the value
|
|
363
|
-
likes: 'dec', // Decrement the value
|
|
364
|
-
updated_at: '@updatedAt' // Set to current timestamp
|
|
365
|
-
},
|
|
366
|
-
message: 'Post updated',
|
|
367
|
-
idField: 'slug', // Optional: Use a different field as identifier
|
|
368
|
-
filter: {
|
|
369
|
-
// Optional: Additional WHERE conditions beyond the ID
|
|
370
|
-
author_id: {
|
|
371
|
-
operator: '=',
|
|
372
|
-
value: 'number'
|
|
373
|
-
}
|
|
374
|
-
},
|
|
375
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
376
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
377
|
-
methodOverride: 'put' // Optional: Override the HTTP method
|
|
378
|
-
});
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
#### `honey.update(options)`
|
|
382
|
-
|
|
383
|
-
Creates a PUT endpoint for updating multiple resources based on filters.
|
|
384
|
-
|
|
385
|
-
```typescript
|
|
386
|
-
honey.update({
|
|
387
|
-
resource: 'posts',
|
|
388
|
-
params: {
|
|
389
|
-
published: 'replace',
|
|
390
|
-
updated_at: '@updatedAt'
|
|
391
|
-
},
|
|
392
|
-
filter: {
|
|
393
|
-
// Required: Filter criteria
|
|
394
|
-
author_id: {
|
|
395
|
-
operator: '=',
|
|
396
|
-
value: 'number'
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
message: 'Posts updated',
|
|
400
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
401
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
402
|
-
methodOverride: 'put' // Optional: Override the HTTP method
|
|
403
|
-
});
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
> **Note:** `filter` is **required** for `honey.update()` to prevent unintended bulk updates.
|
|
407
|
-
|
|
408
|
-
#### `honey.upsertById(options)`
|
|
409
|
-
|
|
410
|
-
Creates a PUT endpoint for upserting a resource by ID.
|
|
411
|
-
|
|
412
|
-
After an upsert, `req.isInsert` is set to `true` if the operation was an INSERT, or `false` if it was an UPDATE. This flag is accessible in `processResponseData` and `exitMiddleware`.
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
honey.upsertById({
|
|
416
|
-
resource: 'posts',
|
|
417
|
-
params: {
|
|
418
|
-
title: 'replace',
|
|
419
|
-
content: 'replace',
|
|
420
|
-
updated_at: '@updatedAt'
|
|
421
|
-
},
|
|
422
|
-
message: 'Post upserted',
|
|
423
|
-
idField: 'id', // Required: Field to use for conflict detection
|
|
424
|
-
doNothingOnConflict: false, // Optional: When true, returns existing record unchanged on conflict (default: false)
|
|
425
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
426
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
427
|
-
methodOverride: 'put', // Optional: Override the HTTP method
|
|
428
|
-
processResponseData: (data, req) => {
|
|
429
|
-
// req.isInsert is true for INSERT, false for UPDATE
|
|
430
|
-
return { ...data, wasInserted: req.isInsert };
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
#### `honey.upsert(options)`
|
|
436
|
-
|
|
437
|
-
Creates a PUT endpoint for upserting a resource with custom conflict resolution.
|
|
438
|
-
|
|
439
|
-
After an upsert, `req.isInsert` is set to `true` if the operation was an INSERT, or `false` if it was an UPDATE. This flag is accessible in `processResponseData` and `exitMiddleware`.
|
|
440
|
-
|
|
441
|
-
```typescript
|
|
442
|
-
honey.upsert({
|
|
443
|
-
resource: 'posts',
|
|
444
|
-
params: {
|
|
445
|
-
title: 'replace',
|
|
446
|
-
content: 'replace',
|
|
447
|
-
slug: 'replace',
|
|
448
|
-
updated_at: '@updatedAt'
|
|
449
|
-
},
|
|
450
|
-
message: 'Post upserted',
|
|
451
|
-
conflictTarget: ['slug'], // Fields to check for conflicts
|
|
452
|
-
doNothingOnConflict: false, // Optional: When true, returns existing record unchanged on conflict (default: false)
|
|
453
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
454
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
455
|
-
methodOverride: 'put' // Optional: Override the HTTP method
|
|
456
|
-
});
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
#### `honey.deleteById(options)`
|
|
460
|
-
|
|
461
|
-
Creates a DELETE endpoint for deleting a resource by ID.
|
|
462
|
-
|
|
463
|
-
```typescript
|
|
464
|
-
honey.deleteById({
|
|
465
|
-
resource: 'posts',
|
|
466
|
-
message: 'Post deleted',
|
|
467
|
-
idField: 'id', // Optional: Field to use as identifier
|
|
468
|
-
filter: {
|
|
469
|
-
// Optional: Additional filters
|
|
470
|
-
author_id: {
|
|
471
|
-
operator: '=',
|
|
472
|
-
value: 'number'
|
|
473
|
-
}
|
|
474
|
-
},
|
|
475
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
476
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
477
|
-
methodOverride: 'delete' // Optional: Override the HTTP method
|
|
478
|
-
});
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
#### `honey.delete(options)`
|
|
482
|
-
|
|
483
|
-
Creates a DELETE endpoint for bulk-deleting resources (no ID in path).
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
honey.delete({
|
|
487
|
-
resource: 'posts',
|
|
488
|
-
filter: {
|
|
489
|
-
// Filter criteria for records to delete
|
|
490
|
-
author_id: {
|
|
491
|
-
operator: '=',
|
|
492
|
-
value: 'number'
|
|
493
|
-
}
|
|
494
|
-
},
|
|
495
|
-
message: 'Posts deleted',
|
|
496
|
-
table: 'blog_posts', // Optional: Table name if different from resource
|
|
497
|
-
pathOverride: '/blog/posts', // Optional: Custom path
|
|
498
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
499
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
500
|
-
methodOverride: 'delete', // Optional: Override the HTTP method
|
|
501
|
-
processErrorResponse: (err) => err // Optional: Customize error response
|
|
502
|
-
});
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
#### `honey.query(options)`
|
|
506
|
-
|
|
507
|
-
Creates a custom endpoint backed by a raw Knex query. Useful for complex queries that go beyond standard CRUD operations.
|
|
508
|
-
|
|
509
|
-
Automatically supports `?page=` and `?limit=` query parameters for pagination.
|
|
510
|
-
|
|
511
|
-
```typescript
|
|
512
|
-
honey.query({
|
|
513
|
-
resource: 'stats',
|
|
514
|
-
methodOverride: 'get', // Optional: HTTP method (default: 'get')
|
|
515
|
-
query: (knex, req) => {
|
|
516
|
-
return knex('posts')
|
|
517
|
-
.select('author_id')
|
|
518
|
-
.count('id as post_count')
|
|
519
|
-
.groupBy('author_id');
|
|
520
|
-
},
|
|
521
|
-
processResponseData: (data, req) => {
|
|
522
|
-
return data;
|
|
523
|
-
},
|
|
524
|
-
pathOverride: '/stats/posts', // Optional: Custom path
|
|
525
|
-
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
526
|
-
exitMiddleware: [auditMiddleware], // Optional: Middleware that runs after response is sent
|
|
527
|
-
processErrorResponse: (err) => err // Optional: Customize error response
|
|
528
|
-
});
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
**Options:**
|
|
532
|
-
|
|
533
|
-
- `resource` — Resource name used in the URL path
|
|
534
|
-
- `query` — Function receiving `(knex, req)` and returning a Knex `QueryBuilder`
|
|
535
|
-
- `pathOverride` — Optional custom path
|
|
536
|
-
- `methodOverride` — HTTP method (default: `'get'`)
|
|
537
|
-
- `middleware` — Route-specific middleware
|
|
538
|
-
- `exitMiddleware` — Middleware that runs after the response is sent
|
|
539
|
-
- `processResponseData` — Transform the response data
|
|
540
|
-
- `processErrorResponse` — Customize the error response
|
|
541
|
-
|
|
542
|
-
### Advanced Usage
|
|
543
|
-
|
|
544
|
-
#### Custom Response Processing
|
|
545
|
-
|
|
546
|
-
```typescript
|
|
547
|
-
honey.getById({
|
|
548
|
-
resource: 'users',
|
|
549
|
-
fields: ['id', 'name', 'email', 'role_id'],
|
|
550
|
-
processResponseData: async (data, req) => {
|
|
551
|
-
// Fetch related data
|
|
552
|
-
const role = await fetchRoleById(data.role_id);
|
|
553
|
-
|
|
554
|
-
// Transform response
|
|
555
|
-
return {
|
|
556
|
-
...data,
|
|
557
|
-
role: role.name,
|
|
558
|
-
_links: {
|
|
559
|
-
self: `/api/users/${data.id}`,
|
|
560
|
-
posts: `/api/users/${data.id}/posts`
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
#### Custom Error Handling
|
|
568
|
-
|
|
569
|
-
```typescript
|
|
570
|
-
honey.create({
|
|
571
|
-
resource: 'users',
|
|
572
|
-
params: {
|
|
573
|
-
email: 'string',
|
|
574
|
-
password: 'string'
|
|
575
|
-
},
|
|
576
|
-
message: 'User created',
|
|
577
|
-
processErrorResponse: (err) => {
|
|
578
|
-
// Customize error response
|
|
579
|
-
if (err.message.includes('duplicate key')) {
|
|
580
|
-
return new HttpError('Email already exists', 409);
|
|
581
|
-
}
|
|
582
|
-
return err;
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
```
|
|
586
|
-
|
|
587
|
-
## Database Utilities
|
|
588
|
-
|
|
589
|
-
HoneyJS provides several utilities for working directly with the database.
|
|
590
|
-
|
|
591
|
-
### Sequelize Model Definition
|
|
592
|
-
|
|
593
|
-
```typescript
|
|
594
|
-
import { defineModel, DataTypes } from '@promind/honey';
|
|
595
|
-
|
|
596
|
-
const User = defineModel(
|
|
597
|
-
'users',
|
|
598
|
-
{
|
|
599
|
-
id: {
|
|
600
|
-
type: DataTypes.UUID,
|
|
601
|
-
defaultValue: DataTypes.UUIDV4,
|
|
602
|
-
primaryKey: true
|
|
603
|
-
},
|
|
604
|
-
name: {
|
|
605
|
-
type: DataTypes.STRING,
|
|
606
|
-
allowNull: false
|
|
607
|
-
},
|
|
608
|
-
email: {
|
|
609
|
-
type: DataTypes.STRING,
|
|
610
|
-
allowNull: false,
|
|
611
|
-
unique: true
|
|
612
|
-
},
|
|
613
|
-
created_at: {
|
|
614
|
-
type: DataTypes.DATE,
|
|
615
|
-
defaultValue: DataTypes.NOW
|
|
616
|
-
}
|
|
617
|
-
},
|
|
618
|
-
{
|
|
619
|
-
// Optional: Sequelize ModelOptions
|
|
620
|
-
timestamps: false,
|
|
621
|
-
tableName: 'app_users'
|
|
622
|
-
}
|
|
623
|
-
);
|
|
624
|
-
|
|
625
|
-
export default User;
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
### Raw Query Execution
|
|
629
|
-
|
|
630
|
-
```typescript
|
|
631
|
-
import { runDbQuery, QueryTypes } from '@promind/honey';
|
|
632
|
-
|
|
633
|
-
async function getUsersWithPosts() {
|
|
634
|
-
const query = `
|
|
635
|
-
SELECT u.id, u.name, COUNT(p.id) as post_count
|
|
636
|
-
FROM users u
|
|
637
|
-
LEFT JOIN posts p ON u.id = p.author_id
|
|
638
|
-
GROUP BY u.id, u.name
|
|
639
|
-
`;
|
|
640
|
-
|
|
641
|
-
return runDbQuery(query, { type: QueryTypes.SELECT });
|
|
642
|
-
}
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
### `createReqTransit`
|
|
646
|
-
|
|
647
|
-
`createReqTransit` creates a typed transit object for safely passing data between middleware and controllers via the request object.
|
|
648
|
-
|
|
649
|
-
```typescript
|
|
650
|
-
import { createReqTransit } from '@promind/honey';
|
|
651
|
-
|
|
652
|
-
// Create a typed transit for passing data between middleware and controllers
|
|
653
|
-
const userTransit = createReqTransit<User>('currentUser');
|
|
654
|
-
|
|
655
|
-
// In middleware
|
|
656
|
-
const authMiddleware = (req, res, next) => {
|
|
657
|
-
const user = verifyToken(req.headers.authorization);
|
|
658
|
-
userTransit.inject(req, user);
|
|
659
|
-
next();
|
|
660
|
-
};
|
|
661
|
-
|
|
662
|
-
// In processResponseData or exitMiddleware
|
|
663
|
-
honey.getById({
|
|
664
|
-
resource: 'posts',
|
|
665
|
-
fields: ['id', 'title'],
|
|
666
|
-
middleware: [authMiddleware],
|
|
667
|
-
processResponseData: (data, req) => {
|
|
668
|
-
const user = userTransit.extract(req, null);
|
|
669
|
-
return { ...data, viewedBy: user?.name };
|
|
670
|
-
}
|
|
671
|
-
});
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
## Error Handling
|
|
675
|
-
|
|
676
|
-
HoneyJS provides a consistent error handling mechanism:
|
|
677
|
-
|
|
678
|
-
```typescript
|
|
679
|
-
import { HttpError, handleHttpError } from '@promind/honey';
|
|
680
|
-
|
|
681
|
-
// In your middleware or custom controller
|
|
682
|
-
try {
|
|
683
|
-
// Some operation
|
|
684
|
-
if (!user) {
|
|
685
|
-
throw new HttpError('User not found', 404);
|
|
686
|
-
}
|
|
687
|
-
} catch (error) {
|
|
688
|
-
handleHttpError(error, res);
|
|
689
|
-
}
|
|
690
|
-
```
|
|
691
|
-
|
|
692
|
-
## Middleware
|
|
693
|
-
|
|
694
|
-
### Request Validation
|
|
695
|
-
|
|
696
|
-
HoneyJS includes built-in request validation using Joi:
|
|
697
|
-
|
|
698
|
-
```typescript
|
|
699
|
-
import { validateRequestData } from '@promind/honey';
|
|
700
|
-
import Joi from 'joi';
|
|
701
|
-
|
|
702
|
-
const userSchema = Joi.object({
|
|
703
|
-
name: Joi.string().required(),
|
|
704
|
-
email: Joi.string().email().required(),
|
|
705
|
-
age: Joi.number().integer().min(18)
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
honey.create({
|
|
709
|
-
resource: 'users',
|
|
710
|
-
params: {
|
|
711
|
-
name: 'string',
|
|
712
|
-
email: 'string',
|
|
713
|
-
age: 'number'
|
|
714
|
-
},
|
|
715
|
-
message: 'User created',
|
|
716
|
-
middleware: [validateRequestData(userSchema)]
|
|
717
|
-
});
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
`validateRequestData` accepts the following arguments:
|
|
721
|
-
|
|
722
|
-
```typescript
|
|
723
|
-
validateRequestData(
|
|
724
|
-
schema, // Joi ObjectSchema
|
|
725
|
-
location?, // 'body' | 'params' | 'query' | 'headers' | 'cookies' (default: 'body')
|
|
726
|
-
options? // Joi ValidationOptions (default: { allowUnknown: true })
|
|
727
|
-
)
|
|
728
|
-
```
|
|
729
|
-
|
|
730
|
-
### Custom Middleware
|
|
731
|
-
|
|
732
|
-
```typescript
|
|
733
|
-
import { Middleware } from '@promind/honey';
|
|
734
|
-
|
|
735
|
-
const authMiddleware: Middleware = (req, res, next) => {
|
|
736
|
-
const token = req.headers.authorization?.split(' ')[1];
|
|
737
|
-
|
|
738
|
-
if (!token) {
|
|
739
|
-
return res.status(401).json({ message: 'Authentication required' });
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
try {
|
|
743
|
-
// Verify token and attach user to request
|
|
744
|
-
req.user = verifyToken(token);
|
|
745
|
-
next();
|
|
746
|
-
} catch (error) {
|
|
747
|
-
res.status(401).json({ message: 'Invalid token' });
|
|
748
|
-
}
|
|
749
|
-
};
|
|
750
|
-
|
|
751
|
-
honey.addMiddleware([authMiddleware]);
|
|
752
|
-
```
|
|
753
|
-
|
|
754
|
-
## Examples
|
|
755
|
-
|
|
756
|
-
### Complete API Example
|
|
757
|
-
|
|
758
|
-
```typescript
|
|
759
|
-
import { createHoney, HttpError } from '@promind/honey';
|
|
760
|
-
|
|
761
|
-
const honey = createHoney(
|
|
762
|
-
'3000',
|
|
763
|
-
'postgresql://postgres:password@localhost:5432/blog'
|
|
764
|
-
);
|
|
765
|
-
|
|
766
|
-
// Authentication middleware
|
|
767
|
-
const authMiddleware = (req, res, next) => {
|
|
768
|
-
// Implementation omitted for brevity
|
|
769
|
-
};
|
|
770
|
-
|
|
771
|
-
// Posts API
|
|
772
|
-
honey.get({
|
|
773
|
-
resource: 'posts',
|
|
774
|
-
fields: ['id', 'title', 'excerpt', 'author_id', 'created_at'],
|
|
775
|
-
filter: {
|
|
776
|
-
title: { operator: 'like', value: 'string' },
|
|
777
|
-
author_id: { operator: '=', value: 'number' }
|
|
778
|
-
},
|
|
779
|
-
format: { sort: 'DESC', sortField: 'created_at' }
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
honey.getById({
|
|
783
|
-
resource: 'posts',
|
|
784
|
-
fields: ['id', 'title', 'content', 'author_id', 'created_at', 'updated_at'],
|
|
785
|
-
processResponseData: async (data, req) => {
|
|
786
|
-
// Fetch author details
|
|
787
|
-
const author = await fetchAuthor(data.author_id);
|
|
788
|
-
return { ...data, author };
|
|
789
|
-
}
|
|
790
|
-
});
|
|
791
|
-
|
|
792
|
-
honey.create({
|
|
793
|
-
resource: 'posts',
|
|
794
|
-
params: {
|
|
795
|
-
title: 'string',
|
|
796
|
-
content: 'string',
|
|
797
|
-
excerpt: 'string',
|
|
798
|
-
author_id: 'number',
|
|
799
|
-
created_at: '@updatedAt'
|
|
800
|
-
},
|
|
801
|
-
message: 'Post created successfully',
|
|
802
|
-
middleware: [authMiddleware]
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
honey.updateById({
|
|
806
|
-
resource: 'posts',
|
|
807
|
-
params: {
|
|
808
|
-
title: 'replace',
|
|
809
|
-
content: 'replace',
|
|
810
|
-
excerpt: 'replace',
|
|
811
|
-
updated_at: '@updatedAt'
|
|
812
|
-
},
|
|
813
|
-
message: 'Post updated successfully',
|
|
814
|
-
middleware: [authMiddleware]
|
|
815
|
-
});
|
|
816
|
-
|
|
817
|
-
honey.deleteById({
|
|
818
|
-
resource: 'posts',
|
|
819
|
-
message: 'Post deleted successfully',
|
|
820
|
-
middleware: [authMiddleware]
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
// Start the server
|
|
824
|
-
honey.startServer();
|
|
825
|
-
```
|
|
826
|
-
|
|
827
|
-
### Custom Route and Controller
|
|
828
|
-
|
|
829
|
-
```typescript
|
|
830
|
-
import { createHoney } from '@promind/honey';
|
|
831
|
-
import express from 'express';
|
|
832
|
-
|
|
833
|
-
const honey = createHoney(
|
|
834
|
-
'3000',
|
|
835
|
-
'postgresql://postgres:password@localhost:5432/app'
|
|
836
|
-
);
|
|
837
|
-
|
|
838
|
-
// Access the Express router
|
|
839
|
-
const router = honey.routes;
|
|
840
|
-
|
|
841
|
-
// Add a custom route
|
|
842
|
-
router.get('/dashboard', (req, res) => {
|
|
843
|
-
res.json({ status: 'ok', message: 'Welcome to the dashboard' });
|
|
844
|
-
});
|
|
845
|
-
|
|
846
|
-
// Start the server
|
|
847
|
-
honey.startServer();
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
## Custom Express Routes
|
|
851
|
-
|
|
852
|
-
While HoneyJS provides declarative methods for common CRUD operations, you can also access the underlying Express router to create custom routes with full flexibility.
|
|
853
|
-
|
|
854
|
-
### Accessing the Express Router
|
|
855
|
-
|
|
856
|
-
The Express router is exposed through the `routes` property of your Honey instance:
|
|
857
|
-
|
|
858
|
-
```typescript
|
|
859
|
-
const honey = createHoney(
|
|
860
|
-
'3000',
|
|
861
|
-
'postgresql://postgres:password@localhost:5432/app'
|
|
862
|
-
);
|
|
863
|
-
const router = honey.routes;
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
### Adding Custom Routes
|
|
867
|
-
|
|
868
|
-
You can add any standard Express route to the router:
|
|
869
|
-
|
|
870
|
-
```typescript
|
|
871
|
-
honey.routes.get('/client/sessions', async (req, res) => {
|
|
872
|
-
res.send({ message: 'Hello Express' });
|
|
873
|
-
});
|
|
874
|
-
|
|
875
|
-
honey.routes.post('/auth/login', async (req, res) => {
|
|
876
|
-
const { username, password } = req.body;
|
|
877
|
-
|
|
878
|
-
// Custom authentication logic
|
|
879
|
-
const user = await authenticateUser(username, password);
|
|
880
|
-
|
|
881
|
-
if (!user) {
|
|
882
|
-
return res.status(401).send({ message: 'Invalid credentials' });
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
res.send({
|
|
886
|
-
token: generateToken(user),
|
|
887
|
-
user: { id: user.id, name: user.name }
|
|
888
|
-
});
|
|
889
|
-
});
|
|
890
|
-
```
|
|
891
|
-
|
|
892
|
-
### Combining HoneyJS and Custom Routes
|
|
893
|
-
|
|
894
|
-
You can mix declarative HoneyJS endpoints with custom Express routes in the same application:
|
|
895
|
-
|
|
896
|
-
```typescript
|
|
897
|
-
import { createHoney } from '@promind/honey';
|
|
898
|
-
|
|
899
|
-
const honey = createHoney(
|
|
900
|
-
'3000',
|
|
901
|
-
'postgresql://postgres:password@localhost:5432/app'
|
|
902
|
-
);
|
|
903
|
-
|
|
904
|
-
// HoneyJS declarative endpoints
|
|
905
|
-
honey.get({
|
|
906
|
-
resource: 'products',
|
|
907
|
-
fields: ['id', 'name', 'price', 'category']
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
honey.create({
|
|
911
|
-
resource: 'products',
|
|
912
|
-
params: {
|
|
913
|
-
name: 'string',
|
|
914
|
-
price: 'number',
|
|
915
|
-
category: 'string'
|
|
916
|
-
},
|
|
917
|
-
message: 'Product created'
|
|
918
|
-
});
|
|
919
|
-
|
|
920
|
-
// Custom Express routes
|
|
921
|
-
honey.routes.get('/stats/dashboard', async (req, res) => {
|
|
922
|
-
const stats = await calculateDashboardStats();
|
|
923
|
-
res.send(stats);
|
|
924
|
-
});
|
|
925
|
-
|
|
926
|
-
honey.routes.post('/batch/import', async (req, res) => {
|
|
927
|
-
try {
|
|
928
|
-
const results = await importDataFromCSV(req.body.fileUrl);
|
|
929
|
-
res.send({ message: 'Import successful', results });
|
|
930
|
-
} catch (error) {
|
|
931
|
-
res.status(500).send({ message: 'Import failed', error: error.message });
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
|
|
935
|
-
// Start the server
|
|
936
|
-
honey.startServer();
|
|
937
|
-
```
|
|
938
|
-
|
|
939
|
-
### Using Express Middleware
|
|
940
|
-
|
|
941
|
-
You can use any Express middleware with your custom routes:
|
|
942
|
-
|
|
943
|
-
```typescript
|
|
944
|
-
import multer from 'multer';
|
|
945
|
-
|
|
946
|
-
// Configure multer for file uploads
|
|
947
|
-
const upload = multer({ dest: 'uploads/' });
|
|
948
|
-
|
|
949
|
-
// File upload route with multer middleware
|
|
950
|
-
honey.routes.post('/uploads/image', upload.single('image'), (req, res) => {
|
|
951
|
-
res.send({
|
|
952
|
-
message: 'File uploaded successfully',
|
|
953
|
-
file: req.file
|
|
954
|
-
});
|
|
955
|
-
});
|
|
956
|
-
|
|
957
|
-
// Route with multiple middleware functions
|
|
958
|
-
honey.routes.get(
|
|
959
|
-
'/admin/reports',
|
|
960
|
-
checkAdminAuth,
|
|
961
|
-
validateReportParams,
|
|
962
|
-
async (req, res) => {
|
|
963
|
-
const report = await generateReport(req.query.type);
|
|
964
|
-
res.send(report);
|
|
965
|
-
}
|
|
966
|
-
);
|
|
967
|
-
```
|
|
968
|
-
|
|
969
|
-
## TypeScript Support
|
|
970
|
-
|
|
971
|
-
HoneyJS is built with TypeScript and provides comprehensive type definitions:
|
|
972
|
-
|
|
973
|
-
```typescript
|
|
974
|
-
import { createHoney, Middleware, HttpError } from '@promind/honey';
|
|
975
|
-
|
|
976
|
-
// Type-safe middleware
|
|
977
|
-
const loggerMiddleware: Middleware = (req, res, next) => {
|
|
978
|
-
console.log(`${req.method} ${req.path}`);
|
|
979
|
-
next();
|
|
980
|
-
};
|
|
981
|
-
|
|
982
|
-
// Type-safe error handling
|
|
983
|
-
const errorHandler = (err: Error) => {
|
|
984
|
-
if (err.message.includes('constraint')) {
|
|
985
|
-
return new HttpError('Data validation failed', 422);
|
|
986
|
-
}
|
|
987
|
-
return err;
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
const honey = createHoney('3000', 'postgresql://localhost:5432/app');
|
|
991
|
-
|
|
992
|
-
honey.create({
|
|
993
|
-
resource: 'products',
|
|
994
|
-
params: {
|
|
995
|
-
name: 'string',
|
|
996
|
-
price: 'number',
|
|
997
|
-
in_stock: 'boolean'
|
|
998
|
-
},
|
|
999
|
-
message: 'Product created',
|
|
1000
|
-
middleware: [loggerMiddleware],
|
|
1001
|
-
processErrorResponse: errorHandler
|
|
1002
|
-
});
|
|
1003
|
-
```
|
|
1004
|
-
|
|
1005
|
-
### `ExitMiddleware`
|
|
1006
|
-
|
|
1007
|
-
`ExitMiddleware` is middleware that runs after the response has been sent. It receives the response data as its first argument, making it ideal for audit logging, analytics, or post-response side effects.
|
|
1008
|
-
|
|
1009
|
-
```typescript
|
|
1010
|
-
import { ExitMiddleware } from '@promind/honey';
|
|
1011
|
-
|
|
1012
|
-
const auditMiddleware: ExitMiddleware = (data, req, res, next) => {
|
|
1013
|
-
console.log(`Response sent for ${req.method} ${req.path}:`, data);
|
|
1014
|
-
next();
|
|
1015
|
-
};
|
|
1016
|
-
|
|
1017
|
-
honey.create({
|
|
1018
|
-
resource: 'users',
|
|
1019
|
-
params: { name: 'string', email: 'string' },
|
|
1020
|
-
message: 'User created',
|
|
1021
|
-
exitMiddleware: [auditMiddleware]
|
|
1022
|
-
});
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
## Contributing
|
|
1026
|
-
|
|
1027
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
1028
|
-
|
|
1029
|
-
## License
|
|
1030
|
-
|
|
1031
|
-
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
1032
|
-
|
|
1033
|
-
---
|
|
1034
|
-
|
|
1035
|
-
## Demo and Examples
|
|
1036
|
-
|
|
1037
|
-
For a complete working example, check out the [honey-example](https://github.com/chinaza/honey-example) repository.
|
|
1038
|
-
|
|
1039
|
-
## Support
|
|
1040
|
-
|
|
1041
|
-
If you encounter any issues or have questions, please [open an issue](https://github.com/chinaza/honey/issues) on GitHub.
|