@promind/honey 1.34.5
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/CHANGELOG.md +563 -0
- package/README.md +795 -0
- package/dist/index.d.ts +196 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +997 -0
- package/dist/index.js.map +1 -0
- package/dist/module.js +962 -0
- package/dist/module.js.map +1 -0
- package/package.json +88 -0
package/README.md
ADDED
|
@@ -0,0 +1,795 @@
|
|
|
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
|
+
- [API Reference](#api-reference)
|
|
13
|
+
- [Core Methods](#core-methods)
|
|
14
|
+
- [CRUD Operations](#crud-operations)
|
|
15
|
+
- [Advanced Usage](#advanced-usage)
|
|
16
|
+
- [Database Utilities](#database-utilities)
|
|
17
|
+
- [Error Handling](#error-handling)
|
|
18
|
+
- [Middleware](#middleware)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
- [Custom Express Routes](#custom-express-routes)
|
|
21
|
+
- [TypeScript Support](#typescript-support)
|
|
22
|
+
- [Contributing](#contributing)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
- **Declarative API Definition**: Define your REST endpoints with simple configuration objects
|
|
28
|
+
- **Full CRUD Support**: Built-in controllers for create, read, update, delete, and upsert operations
|
|
29
|
+
- **PostgreSQL Integration**: Seamless connection to PostgreSQL via Sequelize ORM
|
|
30
|
+
- **Query Building**: Flexible filtering, sorting, and pagination
|
|
31
|
+
- **Middleware Support**: Add custom middleware to your routes
|
|
32
|
+
- **TypeScript Ready**: Full TypeScript support with comprehensive type definitions
|
|
33
|
+
- **Validation**: Request validation using Joi
|
|
34
|
+
- **Error Handling**: Consistent error handling and response formatting
|
|
35
|
+
- **Swagger Documentation**: Automatic OpenAPI documentation generation
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
- Node.js (>=20.9.0)
|
|
40
|
+
- PostgreSQL database
|
|
41
|
+
- TypeScript (for TypeScript projects)
|
|
42
|
+
- Create the relevant tables and data models for your Postgres database. You can use the exposed sequelize utility functions and migrations to do this.
|
|
43
|
+
- See <https://github.com/chinaza/generator-honeyjs> to generate a project
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Using npm
|
|
49
|
+
npm install @promind/honey
|
|
50
|
+
|
|
51
|
+
# Using yarn
|
|
52
|
+
yarn add @promind/honey
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
1. Create a new file (e.g., `server.ts` or `server.js`)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { createHoney } from '@promind/honey';
|
|
61
|
+
|
|
62
|
+
// Initialize HoneyJS with port and database connection string
|
|
63
|
+
const honey = createHoney(
|
|
64
|
+
'3000',
|
|
65
|
+
'postgresql://username:password@localhost:5432/database'
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Define a simple CRUD API for 'users' resource
|
|
69
|
+
honey.get({
|
|
70
|
+
resource: 'users',
|
|
71
|
+
fields: ['id', 'name', 'email', 'created_at']
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
honey.getById({
|
|
75
|
+
resource: 'users',
|
|
76
|
+
fields: ['id', 'name', 'email', 'created_at']
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
honey.create({
|
|
80
|
+
resource: 'users',
|
|
81
|
+
params: {
|
|
82
|
+
name: 'string',
|
|
83
|
+
email: 'string',
|
|
84
|
+
created_at: '@updatedAt'
|
|
85
|
+
},
|
|
86
|
+
message: 'User created successfully'
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
honey.updateById({
|
|
90
|
+
resource: 'users',
|
|
91
|
+
params: {
|
|
92
|
+
name: 'replace',
|
|
93
|
+
email: 'replace'
|
|
94
|
+
},
|
|
95
|
+
message: 'User updated successfully'
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
honey.deleteById({
|
|
99
|
+
resource: 'users',
|
|
100
|
+
message: 'User deleted successfully'
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Start the server
|
|
104
|
+
honey.startServer();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
2. Run your server:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# If using TypeScript
|
|
111
|
+
npx ts-node server.ts
|
|
112
|
+
|
|
113
|
+
# If using JavaScript
|
|
114
|
+
node server.js
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Your API will be available at `http://localhost:3000/api/users`
|
|
118
|
+
|
|
119
|
+
## Configuration
|
|
120
|
+
|
|
121
|
+
### Database Connection
|
|
122
|
+
|
|
123
|
+
You can connect to your PostgreSQL database using either a connection string or a configuration object:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// Using connection string
|
|
127
|
+
const honey = createHoney(
|
|
128
|
+
'3000',
|
|
129
|
+
'postgresql://username:password@localhost:5432/database'
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Using configuration object
|
|
133
|
+
const honey = createHoney('3000', {
|
|
134
|
+
host: 'localhost',
|
|
135
|
+
port: 5432,
|
|
136
|
+
user: 'username',
|
|
137
|
+
password: 'password',
|
|
138
|
+
database: 'database'
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Environment Variables
|
|
143
|
+
|
|
144
|
+
HoneyJS supports configuration through environment variables. Create a `.env` file in your project root:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
PORT=3000
|
|
148
|
+
DB_URI=postgresql://username:password@localhost:5432/database
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Then initialize HoneyJS without parameters:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { createHoney } from '@promind/honey';
|
|
155
|
+
|
|
156
|
+
const honey = createHoney(process.env.PORT, process.env.DB_URI);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## API Reference
|
|
160
|
+
|
|
161
|
+
### Core Methods
|
|
162
|
+
|
|
163
|
+
#### `createHoney(port, dbOptions, metadata?)`
|
|
164
|
+
|
|
165
|
+
Creates a new HoneyJS instance.
|
|
166
|
+
|
|
167
|
+
- `port`: Server port number or string
|
|
168
|
+
- `dbOptions`: PostgreSQL connection string or configuration object
|
|
169
|
+
- `metadata` (optional): Additional configuration options
|
|
170
|
+
- `fallbackErrorMessage`: Custom 404 error message
|
|
171
|
+
- `routePrefix`: API route prefix (default: `/api`)
|
|
172
|
+
|
|
173
|
+
Returns a `Honey` instance.
|
|
174
|
+
|
|
175
|
+
#### `honey.startServer()`
|
|
176
|
+
|
|
177
|
+
Starts the HTTP server.
|
|
178
|
+
|
|
179
|
+
#### `honey.addMiddleware(middleware[])`
|
|
180
|
+
|
|
181
|
+
Adds global middleware to all routes.
|
|
182
|
+
|
|
183
|
+
### CRUD Operations
|
|
184
|
+
|
|
185
|
+
#### `honey.create(options)`
|
|
186
|
+
|
|
187
|
+
Creates a POST endpoint for creating new resources.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
honey.create({
|
|
191
|
+
resource: 'posts', // Resource name (used in URL path)
|
|
192
|
+
table: 'blog_posts', // Optional: Table name if different from resource
|
|
193
|
+
params: {
|
|
194
|
+
// Request body parameters
|
|
195
|
+
title: 'string', // Parameter type validation
|
|
196
|
+
content: 'string',
|
|
197
|
+
author_id: 'number',
|
|
198
|
+
published: 'boolean',
|
|
199
|
+
metadata: 'json', // For JSON fields
|
|
200
|
+
created_at: '@updatedAt' // Special value to set current timestamp
|
|
201
|
+
},
|
|
202
|
+
message: 'Post created', // Success message
|
|
203
|
+
pathOverride: '/blog/posts', // Optional: Custom path
|
|
204
|
+
middleware: [authMiddleware], // Optional: Route-specific middleware
|
|
205
|
+
processResponseData: (data, req) => {
|
|
206
|
+
// Optional: Transform response data
|
|
207
|
+
return { ...data, extra: 'info' };
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### `honey.get(options)`
|
|
213
|
+
|
|
214
|
+
Creates a GET endpoint for retrieving a list of resources.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
honey.get({
|
|
218
|
+
resource: 'posts',
|
|
219
|
+
fields: ['id', 'title', 'author_id', 'created_at'], // Fields to return
|
|
220
|
+
filter: {
|
|
221
|
+
// Optional: Query filters
|
|
222
|
+
title: {
|
|
223
|
+
operator: 'like', // Filter operator
|
|
224
|
+
value: 'string' // Parameter type
|
|
225
|
+
},
|
|
226
|
+
published: {
|
|
227
|
+
operator: '=',
|
|
228
|
+
value: 'boolean'
|
|
229
|
+
},
|
|
230
|
+
$or: {
|
|
231
|
+
// Logical OR condition
|
|
232
|
+
title: {
|
|
233
|
+
operator: 'like',
|
|
234
|
+
value: 'string'
|
|
235
|
+
},
|
|
236
|
+
content: {
|
|
237
|
+
operator: 'like',
|
|
238
|
+
value: 'string'
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
format: {
|
|
243
|
+
// Optional: Sorting
|
|
244
|
+
sort: 'DESC', // ASC or DESC
|
|
245
|
+
sortField: 'created_at' // Field to sort by
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
#### `honey.getById(options)`
|
|
251
|
+
|
|
252
|
+
Creates a GET endpoint for retrieving a single resource by ID.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
honey.getById({
|
|
256
|
+
resource: 'posts',
|
|
257
|
+
fields: ['id', 'title', 'content', 'author_id', 'created_at'],
|
|
258
|
+
idField: 'slug', // Optional: Use a different field as identifier (default: 'id')
|
|
259
|
+
filter: {
|
|
260
|
+
// Optional: Additional filters
|
|
261
|
+
published: {
|
|
262
|
+
operator: '=',
|
|
263
|
+
overrideValue: true // Force a value regardless of request
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### `honey.updateById(options)`
|
|
270
|
+
|
|
271
|
+
Creates a PUT endpoint for updating a resource by ID.
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
honey.updateById({
|
|
275
|
+
resource: 'posts',
|
|
276
|
+
params: {
|
|
277
|
+
title: 'replace', // Replace the value
|
|
278
|
+
views: 'inc', // Increment the value
|
|
279
|
+
likes: 'dec', // Decrement the value
|
|
280
|
+
updated_at: '@updatedAt' // Set to current timestamp
|
|
281
|
+
},
|
|
282
|
+
message: 'Post updated',
|
|
283
|
+
idField: 'slug' // Optional: Use a different field as identifier
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### `honey.update(options)`
|
|
288
|
+
|
|
289
|
+
Creates a PUT endpoint for updating multiple resources based on filters.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
honey.update({
|
|
293
|
+
resource: 'posts',
|
|
294
|
+
params: {
|
|
295
|
+
published: 'replace',
|
|
296
|
+
updated_at: '@updatedAt'
|
|
297
|
+
},
|
|
298
|
+
filter: {
|
|
299
|
+
// Required: Filter criteria
|
|
300
|
+
author_id: {
|
|
301
|
+
operator: '=',
|
|
302
|
+
value: 'number'
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
message: 'Posts updated'
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### `honey.upsertById(options)`
|
|
310
|
+
|
|
311
|
+
Creates a PUT endpoint for upserting a resource by ID.
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
honey.upsertById({
|
|
315
|
+
resource: 'posts',
|
|
316
|
+
params: {
|
|
317
|
+
title: 'replace',
|
|
318
|
+
content: 'replace',
|
|
319
|
+
updated_at: '@updatedAt'
|
|
320
|
+
},
|
|
321
|
+
message: 'Post upserted',
|
|
322
|
+
idField: 'id' // Field to use for conflict detection
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### `honey.upsert(options)`
|
|
327
|
+
|
|
328
|
+
Creates a PUT endpoint for upserting a resource with custom conflict resolution.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
honey.upsert({
|
|
332
|
+
resource: 'posts',
|
|
333
|
+
params: {
|
|
334
|
+
title: 'replace',
|
|
335
|
+
content: 'replace',
|
|
336
|
+
slug: 'replace',
|
|
337
|
+
updated_at: '@updatedAt'
|
|
338
|
+
},
|
|
339
|
+
message: 'Post upserted',
|
|
340
|
+
conflictTarget: ['slug'] // Fields to check for conflicts
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### `honey.deleteById(options)`
|
|
345
|
+
|
|
346
|
+
Creates a DELETE endpoint for deleting a resource by ID.
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
honey.deleteById({
|
|
350
|
+
resource: 'posts',
|
|
351
|
+
message: 'Post deleted',
|
|
352
|
+
idField: 'id', // Optional: Field to use as identifier
|
|
353
|
+
filter: {
|
|
354
|
+
// Optional: Additional filters
|
|
355
|
+
author_id: {
|
|
356
|
+
operator: '=',
|
|
357
|
+
value: 'number'
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Advanced Usage
|
|
364
|
+
|
|
365
|
+
#### Custom Response Processing
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
honey.getById({
|
|
369
|
+
resource: 'users',
|
|
370
|
+
fields: ['id', 'name', 'email', 'role_id'],
|
|
371
|
+
processResponseData: async (data, req) => {
|
|
372
|
+
// Fetch related data
|
|
373
|
+
const role = await fetchRoleById(data.role_id);
|
|
374
|
+
|
|
375
|
+
// Transform response
|
|
376
|
+
return {
|
|
377
|
+
...data,
|
|
378
|
+
role: role.name,
|
|
379
|
+
_links: {
|
|
380
|
+
self: `/api/users/${data.id}`,
|
|
381
|
+
posts: `/api/users/${data.id}/posts`
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### Custom Error Handling
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
honey.create({
|
|
392
|
+
resource: 'users',
|
|
393
|
+
params: {
|
|
394
|
+
email: 'string',
|
|
395
|
+
password: 'string'
|
|
396
|
+
},
|
|
397
|
+
message: 'User created',
|
|
398
|
+
processErrorResponse: (err) => {
|
|
399
|
+
// Customize error response
|
|
400
|
+
if (err.message.includes('duplicate key')) {
|
|
401
|
+
return new HttpError('Email already exists', 409);
|
|
402
|
+
}
|
|
403
|
+
return err;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
## Database Utilities
|
|
409
|
+
|
|
410
|
+
HoneyJS provides several utilities for working directly with the database.
|
|
411
|
+
|
|
412
|
+
### Sequelize Model Definition
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
import { defineModel, DataTypes } from '@promind/honey';
|
|
416
|
+
|
|
417
|
+
const User = defineModel('users', {
|
|
418
|
+
id: {
|
|
419
|
+
type: DataTypes.UUID,
|
|
420
|
+
defaultValue: DataTypes.UUIDV4,
|
|
421
|
+
primaryKey: true
|
|
422
|
+
},
|
|
423
|
+
name: {
|
|
424
|
+
type: DataTypes.STRING,
|
|
425
|
+
allowNull: false
|
|
426
|
+
},
|
|
427
|
+
email: {
|
|
428
|
+
type: DataTypes.STRING,
|
|
429
|
+
allowNull: false,
|
|
430
|
+
unique: true
|
|
431
|
+
},
|
|
432
|
+
created_at: {
|
|
433
|
+
type: DataTypes.DATE,
|
|
434
|
+
defaultValue: DataTypes.NOW
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
export default User;
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Raw Query Execution
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
import { runDbQuery, QueryTypes } from '@promind/honey';
|
|
445
|
+
|
|
446
|
+
async function getUsersWithPosts() {
|
|
447
|
+
const query = `
|
|
448
|
+
SELECT u.id, u.name, COUNT(p.id) as post_count
|
|
449
|
+
FROM users u
|
|
450
|
+
LEFT JOIN posts p ON u.id = p.author_id
|
|
451
|
+
GROUP BY u.id, u.name
|
|
452
|
+
`;
|
|
453
|
+
|
|
454
|
+
return runDbQuery(query, { type: QueryTypes.SELECT });
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Error Handling
|
|
459
|
+
|
|
460
|
+
HoneyJS provides a consistent error handling mechanism:
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
import { HttpError, handleHttpError } from '@promind/honey';
|
|
464
|
+
|
|
465
|
+
// In your middleware or custom controller
|
|
466
|
+
try {
|
|
467
|
+
// Some operation
|
|
468
|
+
if (!user) {
|
|
469
|
+
throw new HttpError('User not found', 404);
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
handleHttpError(error, res);
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Middleware
|
|
477
|
+
|
|
478
|
+
### Request Validation
|
|
479
|
+
|
|
480
|
+
HoneyJS includes built-in request validation using Joi:
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import { validateRequestData } from '@promind/honey';
|
|
484
|
+
import Joi from 'joi';
|
|
485
|
+
|
|
486
|
+
const userSchema = Joi.object({
|
|
487
|
+
name: Joi.string().required(),
|
|
488
|
+
email: Joi.string().email().required(),
|
|
489
|
+
age: Joi.number().integer().min(18)
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
honey.create({
|
|
493
|
+
resource: 'users',
|
|
494
|
+
params: {
|
|
495
|
+
name: 'string',
|
|
496
|
+
email: 'string',
|
|
497
|
+
age: 'number'
|
|
498
|
+
},
|
|
499
|
+
message: 'User created',
|
|
500
|
+
middleware: [validateRequestData(userSchema)]
|
|
501
|
+
});
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### Custom Middleware
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
import { Middleware } from '@promind/honey';
|
|
508
|
+
|
|
509
|
+
const authMiddleware: Middleware = (req, res, next) => {
|
|
510
|
+
const token = req.headers.authorization?.split(' ')[1];
|
|
511
|
+
|
|
512
|
+
if (!token) {
|
|
513
|
+
return res.status(401).json({ message: 'Authentication required' });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
// Verify token and attach user to request
|
|
518
|
+
req.user = verifyToken(token);
|
|
519
|
+
next();
|
|
520
|
+
} catch (error) {
|
|
521
|
+
res.status(401).json({ message: 'Invalid token' });
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
honey.addMiddleware([authMiddleware]);
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Examples
|
|
529
|
+
|
|
530
|
+
### Complete API Example
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
import { createHoney, HttpError } from '@promind/honey';
|
|
534
|
+
|
|
535
|
+
const honey = createHoney(
|
|
536
|
+
'3000',
|
|
537
|
+
'postgresql://postgres:password@localhost:5432/blog'
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
// Authentication middleware
|
|
541
|
+
const authMiddleware = (req, res, next) => {
|
|
542
|
+
// Implementation omitted for brevity
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
// Posts API
|
|
546
|
+
honey.get({
|
|
547
|
+
resource: 'posts',
|
|
548
|
+
fields: ['id', 'title', 'excerpt', 'author_id', 'created_at'],
|
|
549
|
+
filter: {
|
|
550
|
+
title: { operator: 'like', value: 'string' },
|
|
551
|
+
author_id: { operator: '=', value: 'number' }
|
|
552
|
+
},
|
|
553
|
+
format: { sort: 'DESC', sortField: 'created_at' }
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
honey.getById({
|
|
557
|
+
resource: 'posts',
|
|
558
|
+
fields: ['id', 'title', 'content', 'author_id', 'created_at', 'updated_at'],
|
|
559
|
+
processResponseData: async (data, req) => {
|
|
560
|
+
// Fetch author details
|
|
561
|
+
const author = await fetchAuthor(data.author_id);
|
|
562
|
+
return { ...data, author };
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
honey.create({
|
|
567
|
+
resource: 'posts',
|
|
568
|
+
params: {
|
|
569
|
+
title: 'string',
|
|
570
|
+
content: 'string',
|
|
571
|
+
excerpt: 'string',
|
|
572
|
+
author_id: 'number',
|
|
573
|
+
created_at: '@updatedAt'
|
|
574
|
+
},
|
|
575
|
+
message: 'Post created successfully',
|
|
576
|
+
middleware: [authMiddleware]
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
honey.updateById({
|
|
580
|
+
resource: 'posts',
|
|
581
|
+
params: {
|
|
582
|
+
title: 'replace',
|
|
583
|
+
content: 'replace',
|
|
584
|
+
excerpt: 'replace',
|
|
585
|
+
updated_at: '@updatedAt'
|
|
586
|
+
},
|
|
587
|
+
message: 'Post updated successfully',
|
|
588
|
+
middleware: [authMiddleware]
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
honey.deleteById({
|
|
592
|
+
resource: 'posts',
|
|
593
|
+
message: 'Post deleted successfully',
|
|
594
|
+
middleware: [authMiddleware]
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// Start the server
|
|
598
|
+
honey.startServer();
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Custom Route and Controller
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
import { createHoney } from '@promind/honey';
|
|
605
|
+
import express from 'express';
|
|
606
|
+
|
|
607
|
+
const honey = createHoney(
|
|
608
|
+
'3000',
|
|
609
|
+
'postgresql://postgres:password@localhost:5432/app'
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
// Access the Express router
|
|
613
|
+
const router = honey.routes;
|
|
614
|
+
|
|
615
|
+
// Add a custom route
|
|
616
|
+
router.get('/dashboard', (req, res) => {
|
|
617
|
+
res.json({ status: 'ok', message: 'Welcome to the dashboard' });
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// Start the server
|
|
621
|
+
honey.startServer();
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
## Custom Express Routes
|
|
625
|
+
|
|
626
|
+
While HoneyJS provides declarative methods for common CRUD operations, you can also access the underlying Express router to create custom routes with full flexibility.
|
|
627
|
+
|
|
628
|
+
### Accessing the Express Router
|
|
629
|
+
|
|
630
|
+
The Express router is exposed through the `routes` property of your Honey instance:
|
|
631
|
+
|
|
632
|
+
```typescript
|
|
633
|
+
const honey = createHoney(
|
|
634
|
+
'3000',
|
|
635
|
+
'postgresql://postgres:password@localhost:5432/app'
|
|
636
|
+
);
|
|
637
|
+
const router = honey.routes;
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Adding Custom Routes
|
|
641
|
+
|
|
642
|
+
You can add any standard Express route to the router:
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
honey.routes.get('/client/sessions', async (req, res) => {
|
|
646
|
+
res.send({ message: 'Hello Express' });
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
honey.routes.post('/auth/login', async (req, res) => {
|
|
650
|
+
const { username, password } = req.body;
|
|
651
|
+
|
|
652
|
+
// Custom authentication logic
|
|
653
|
+
const user = await authenticateUser(username, password);
|
|
654
|
+
|
|
655
|
+
if (!user) {
|
|
656
|
+
return res.status(401).send({ message: 'Invalid credentials' });
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
res.send({
|
|
660
|
+
token: generateToken(user),
|
|
661
|
+
user: { id: user.id, name: user.name }
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Combining HoneyJS and Custom Routes
|
|
667
|
+
|
|
668
|
+
You can mix declarative HoneyJS endpoints with custom Express routes in the same application:
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
import { createHoney } from '@promind/honey';
|
|
672
|
+
|
|
673
|
+
const honey = createHoney(
|
|
674
|
+
'3000',
|
|
675
|
+
'postgresql://postgres:password@localhost:5432/app'
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
// HoneyJS declarative endpoints
|
|
679
|
+
honey.get({
|
|
680
|
+
resource: 'products',
|
|
681
|
+
fields: ['id', 'name', 'price', 'category']
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
honey.create({
|
|
685
|
+
resource: 'products',
|
|
686
|
+
params: {
|
|
687
|
+
name: 'string',
|
|
688
|
+
price: 'number',
|
|
689
|
+
category: 'string'
|
|
690
|
+
},
|
|
691
|
+
message: 'Product created'
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// Custom Express routes
|
|
695
|
+
honey.routes.get('/stats/dashboard', async (req, res) => {
|
|
696
|
+
const stats = await calculateDashboardStats();
|
|
697
|
+
res.send(stats);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
honey.routes.post('/batch/import', async (req, res) => {
|
|
701
|
+
try {
|
|
702
|
+
const results = await importDataFromCSV(req.body.fileUrl);
|
|
703
|
+
res.send({ message: 'Import successful', results });
|
|
704
|
+
} catch (error) {
|
|
705
|
+
res.status(500).send({ message: 'Import failed', error: error.message });
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// Start the server
|
|
710
|
+
honey.startServer();
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Using Express Middleware
|
|
714
|
+
|
|
715
|
+
You can use any Express middleware with your custom routes:
|
|
716
|
+
|
|
717
|
+
```typescript
|
|
718
|
+
import multer from 'multer';
|
|
719
|
+
|
|
720
|
+
// Configure multer for file uploads
|
|
721
|
+
const upload = multer({ dest: 'uploads/' });
|
|
722
|
+
|
|
723
|
+
// File upload route with multer middleware
|
|
724
|
+
honey.routes.post('/uploads/image', upload.single('image'), (req, res) => {
|
|
725
|
+
res.send({
|
|
726
|
+
message: 'File uploaded successfully',
|
|
727
|
+
file: req.file
|
|
728
|
+
});
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// Route with multiple middleware functions
|
|
732
|
+
honey.routes.get(
|
|
733
|
+
'/admin/reports',
|
|
734
|
+
checkAdminAuth,
|
|
735
|
+
validateReportParams,
|
|
736
|
+
async (req, res) => {
|
|
737
|
+
const report = await generateReport(req.query.type);
|
|
738
|
+
res.send(report);
|
|
739
|
+
}
|
|
740
|
+
);
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
## TypeScript Support
|
|
744
|
+
|
|
745
|
+
HoneyJS is built with TypeScript and provides comprehensive type definitions:
|
|
746
|
+
|
|
747
|
+
```typescript
|
|
748
|
+
import { createHoney, Middleware, HttpError } from '@promind/honey';
|
|
749
|
+
|
|
750
|
+
// Type-safe middleware
|
|
751
|
+
const loggerMiddleware: Middleware = (req, res, next) => {
|
|
752
|
+
console.log(`${req.method} ${req.path}`);
|
|
753
|
+
next();
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
// Type-safe error handling
|
|
757
|
+
const errorHandler = (err: Error) => {
|
|
758
|
+
if (err.message.includes('constraint')) {
|
|
759
|
+
return new HttpError('Data validation failed', 422);
|
|
760
|
+
}
|
|
761
|
+
return err;
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const honey = createHoney('3000', 'postgresql://localhost:5432/app');
|
|
765
|
+
|
|
766
|
+
honey.create({
|
|
767
|
+
resource: 'products',
|
|
768
|
+
params: {
|
|
769
|
+
name: 'string',
|
|
770
|
+
price: 'number',
|
|
771
|
+
in_stock: 'boolean'
|
|
772
|
+
},
|
|
773
|
+
message: 'Product created',
|
|
774
|
+
middleware: [loggerMiddleware],
|
|
775
|
+
processErrorResponse: errorHandler
|
|
776
|
+
});
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
## Contributing
|
|
780
|
+
|
|
781
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
782
|
+
|
|
783
|
+
## License
|
|
784
|
+
|
|
785
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## Demo and Examples
|
|
790
|
+
|
|
791
|
+
For a complete working example, check out the [honey-example](https://github.com/chinaza/honey-example) repository.
|
|
792
|
+
|
|
793
|
+
## Support
|
|
794
|
+
|
|
795
|
+
If you encounter any issues or have questions, please [open an issue](https://github.com/chinaza/honey/issues) on GitHub.
|