monapi 0.1.0
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/LICENSE +21 -0
- package/README.md +385 -0
- package/dist/index.d.mts +315 -0
- package/dist/index.d.ts +315 -0
- package/dist/index.js +1015 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +998 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 monapi contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# monapi
|
|
2
|
+
|
|
3
|
+
Auto-generate secure, extensible REST APIs for MongoDB collections using configuration only.
|
|
4
|
+
|
|
5
|
+
Define a schema. Get a full CRUD API. No boilerplate.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install monapi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Peer dependencies:**
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install express mongoose
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import express from 'express'
|
|
23
|
+
import mongoose, { Schema } from 'mongoose'
|
|
24
|
+
import { Monapi } from 'monapi'
|
|
25
|
+
|
|
26
|
+
const app = express()
|
|
27
|
+
app.use(express.json())
|
|
28
|
+
|
|
29
|
+
mongoose.connect('mongodb://localhost:27017/myapp')
|
|
30
|
+
|
|
31
|
+
const UserSchema = new Schema({
|
|
32
|
+
name: { type: String, required: true },
|
|
33
|
+
email: { type: String, required: true },
|
|
34
|
+
age: { type: Number },
|
|
35
|
+
role: { type: String, enum: ['admin', 'user'], default: 'user' },
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const monapi = new Monapi({ connection: mongoose.connection })
|
|
39
|
+
|
|
40
|
+
monapi.resource('users', { schema: UserSchema })
|
|
41
|
+
|
|
42
|
+
app.use('/api', monapi.router())
|
|
43
|
+
app.listen(3000)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This generates:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
GET /api/users List users
|
|
50
|
+
GET /api/users/:id Get user by id
|
|
51
|
+
POST /api/users Create user
|
|
52
|
+
PUT /api/users/:id Replace user
|
|
53
|
+
PATCH /api/users/:id Partial update user
|
|
54
|
+
DELETE /api/users/:id Delete user
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Filtering
|
|
58
|
+
|
|
59
|
+
### Simple equality
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
GET /api/users?role=admin
|
|
63
|
+
GET /api/users?age=25&active=true
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Operators (double-underscore syntax)
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
GET /api/users?age__gt=18&age__lt=30
|
|
70
|
+
GET /api/users?role__in=admin,moderator
|
|
71
|
+
GET /api/users?name__like=john
|
|
72
|
+
GET /api/users?email__exists=true
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
| Operator | MongoDB | Example |
|
|
76
|
+
|----------|---------|---------|
|
|
77
|
+
| `__eq` | `$eq` | `?role__eq=admin` |
|
|
78
|
+
| `__ne` | `$ne` | `?role__ne=banned` |
|
|
79
|
+
| `__gt` | `$gt` | `?age__gt=18` |
|
|
80
|
+
| `__gte` | `$gte` | `?age__gte=18` |
|
|
81
|
+
| `__lt` | `$lt` | `?age__lt=65` |
|
|
82
|
+
| `__lte` | `$lte` | `?age__lte=65` |
|
|
83
|
+
| `__in` | `$in` | `?role__in=admin,user` |
|
|
84
|
+
| `__nin` | `$nin` | `?role__nin=banned` |
|
|
85
|
+
| `__like` | `$regex` (case-insensitive) | `?name__like=john` |
|
|
86
|
+
| `__exists` | `$exists` | `?phone__exists=true` |
|
|
87
|
+
|
|
88
|
+
### Advanced bracket syntax
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
GET /api/users?filter[age][gt]=18&filter[age][lt]=30
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Auto type coercion
|
|
95
|
+
|
|
96
|
+
Query values are automatically coerced:
|
|
97
|
+
- `"25"` becomes `25` (number)
|
|
98
|
+
- `"true"` / `"false"` becomes boolean
|
|
99
|
+
- `"2024-01-15"` becomes a Date object
|
|
100
|
+
|
|
101
|
+
## Sorting
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
GET /api/users?sort=name # ascending
|
|
105
|
+
GET /api/users?sort=-createdAt # descending
|
|
106
|
+
GET /api/users?sort=role,-createdAt # multiple fields
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Pagination
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
GET /api/users?page=2&limit=20
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
List responses include metadata:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"data": [...],
|
|
120
|
+
"meta": {
|
|
121
|
+
"page": 2,
|
|
122
|
+
"limit": 20,
|
|
123
|
+
"total": 150,
|
|
124
|
+
"totalPages": 8
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Field Selection
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
GET /api/users?fields=name,email
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Query Configuration
|
|
136
|
+
|
|
137
|
+
Control what's filterable and sortable per collection:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
monapi.resource('users', {
|
|
141
|
+
schema: UserSchema,
|
|
142
|
+
query: {
|
|
143
|
+
allowedFilters: ['name', 'email', 'age', 'role'],
|
|
144
|
+
allowedSorts: ['name', 'age', 'createdAt'],
|
|
145
|
+
defaultSort: '-createdAt',
|
|
146
|
+
defaultLimit: 20,
|
|
147
|
+
maxLimit: 100,
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Lifecycle Hooks
|
|
153
|
+
|
|
154
|
+
Run custom logic before or after any operation:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
monapi.resource('users', {
|
|
158
|
+
schema: UserSchema,
|
|
159
|
+
hooks: {
|
|
160
|
+
beforeCreate: async (ctx) => {
|
|
161
|
+
ctx.data.slug = slugify(ctx.data.name)
|
|
162
|
+
},
|
|
163
|
+
afterCreate: async (ctx) => {
|
|
164
|
+
await sendWelcomeEmail(ctx.result.email)
|
|
165
|
+
},
|
|
166
|
+
beforeFind: async (ctx) => {
|
|
167
|
+
// Inject tenant filter
|
|
168
|
+
ctx.query.filter.tenantId = ctx.user?.tenantId
|
|
169
|
+
},
|
|
170
|
+
beforeDelete: async (ctx) => {
|
|
171
|
+
// Prevent deletion
|
|
172
|
+
if (ctx.user?.role !== 'admin') {
|
|
173
|
+
ctx.preventDefault = true
|
|
174
|
+
ctx.res.status(403).json({ error: { message: 'Only admins can delete' } })
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Available hooks: `beforeFind`, `afterFind`, `beforeCreate`, `afterCreate`, `beforeUpdate`, `afterUpdate`, `beforeDelete`, `afterDelete`.
|
|
182
|
+
|
|
183
|
+
### Hook Context
|
|
184
|
+
|
|
185
|
+
Every hook receives a mutable `HookContext`:
|
|
186
|
+
|
|
187
|
+
| Property | Type | Description |
|
|
188
|
+
|----------|------|-------------|
|
|
189
|
+
| `collection` | string | Collection name |
|
|
190
|
+
| `operation` | string | `'find'`, `'create'`, `'update'`, `'patch'`, `'delete'` |
|
|
191
|
+
| `user` | User | Authenticated user (from `req.user`) |
|
|
192
|
+
| `query` | MongoQuery | MongoDB query object (find operations) |
|
|
193
|
+
| `data` | any | Request body (create/update operations) |
|
|
194
|
+
| `result` | any | Database result (after hooks) |
|
|
195
|
+
| `id` | string | Document ID (get/update/delete) |
|
|
196
|
+
| `req` | Request | Express request |
|
|
197
|
+
| `res` | Response | Express response |
|
|
198
|
+
| `meta` | object | Custom metadata |
|
|
199
|
+
| `preventDefault` | boolean | Set `true` to skip the default operation |
|
|
200
|
+
|
|
201
|
+
## Authentication & Authorization
|
|
202
|
+
|
|
203
|
+
### Auth middleware
|
|
204
|
+
|
|
205
|
+
Provide your own auth middleware to populate `req.user`:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const monapi = new Monapi({
|
|
209
|
+
connection: mongoose.connection,
|
|
210
|
+
auth: {
|
|
211
|
+
middleware: (req, res, next) => {
|
|
212
|
+
const token = req.headers.authorization?.split(' ')[1]
|
|
213
|
+
req.user = verifyToken(token)
|
|
214
|
+
next()
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Role-based permissions
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
monapi.resource('posts', {
|
|
224
|
+
schema: PostSchema,
|
|
225
|
+
permissions: {
|
|
226
|
+
list: ['admin', 'user'], // any of these roles
|
|
227
|
+
get: ['admin', 'user'],
|
|
228
|
+
create: ['admin', 'editor'],
|
|
229
|
+
update: ['admin', 'editor'],
|
|
230
|
+
delete: ['admin'],
|
|
231
|
+
},
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Custom permission functions
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
monapi.resource('posts', {
|
|
239
|
+
schema: PostSchema,
|
|
240
|
+
permissions: {
|
|
241
|
+
update: async (ctx) => {
|
|
242
|
+
// Only allow authors to edit their own posts
|
|
243
|
+
const post = await PostModel.findById(ctx.id)
|
|
244
|
+
return post?.author.toString() === ctx.user.id
|
|
245
|
+
},
|
|
246
|
+
delete: (ctx) => ctx.user.roles?.includes('admin'),
|
|
247
|
+
},
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Custom Middleware
|
|
252
|
+
|
|
253
|
+
Inject middleware at different levels:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
monapi.resource('users', {
|
|
257
|
+
schema: UserSchema,
|
|
258
|
+
middleware: {
|
|
259
|
+
all: [rateLimiter], // all operations
|
|
260
|
+
create: [validateCaptcha], // only create
|
|
261
|
+
delete: [requireSuperAdmin], // only delete
|
|
262
|
+
},
|
|
263
|
+
})
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Custom Handlers
|
|
267
|
+
|
|
268
|
+
Override any default handler:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
monapi.resource('users', {
|
|
272
|
+
schema: UserSchema,
|
|
273
|
+
handlers: {
|
|
274
|
+
list: async (req, res, next) => {
|
|
275
|
+
// Completely custom list implementation
|
|
276
|
+
const users = await UserModel.aggregate([...])
|
|
277
|
+
res.json({ data: users, meta: { page: 1, limit: 10, total: users.length } })
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Global Defaults
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
const monapi = new Monapi({
|
|
287
|
+
connection: mongoose.connection,
|
|
288
|
+
defaults: {
|
|
289
|
+
pagination: {
|
|
290
|
+
limit: 20, // default page size
|
|
291
|
+
maxLimit: 100, // maximum allowed page size
|
|
292
|
+
},
|
|
293
|
+
security: {
|
|
294
|
+
maxRegexLength: 50, // max length for __like patterns
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Response Formats
|
|
301
|
+
|
|
302
|
+
### List response
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"data": [
|
|
307
|
+
{ "_id": "...", "name": "John", "email": "john@test.com" }
|
|
308
|
+
],
|
|
309
|
+
"meta": {
|
|
310
|
+
"page": 1,
|
|
311
|
+
"limit": 20,
|
|
312
|
+
"total": 42,
|
|
313
|
+
"totalPages": 3
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Single document response
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"data": {
|
|
323
|
+
"_id": "...",
|
|
324
|
+
"name": "John",
|
|
325
|
+
"email": "john@test.com"
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Error response
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"error": {
|
|
335
|
+
"code": "NOT_FOUND",
|
|
336
|
+
"message": "users with id '123' not found"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Security
|
|
342
|
+
|
|
343
|
+
monapi enforces safe defaults:
|
|
344
|
+
|
|
345
|
+
- Filters only work on schema-defined fields (when `allowedFilters` is set or adapter is provided)
|
|
346
|
+
- All filter operators are whitelisted (no raw MongoDB operators)
|
|
347
|
+
- `$` prefixed field names are blocked everywhere (filters, sort, projection, patch body)
|
|
348
|
+
- Regex patterns have length limits
|
|
349
|
+
- Maximum filter count enforced
|
|
350
|
+
- Pagination limits enforced (configurable `maxLimit`)
|
|
351
|
+
- Patch bodies reject `$` operators (no `$set`, `$unset` injection)
|
|
352
|
+
- Production error responses hide internal details
|
|
353
|
+
|
|
354
|
+
## API Reference
|
|
355
|
+
|
|
356
|
+
### `new Monapi(config)`
|
|
357
|
+
|
|
358
|
+
| Option | Type | Default | Description |
|
|
359
|
+
|--------|------|---------|-------------|
|
|
360
|
+
| `connection` | `mongoose.Connection` | *required* | MongoDB connection |
|
|
361
|
+
| `basePath` | `string` | `''` | Base path prefix for routes |
|
|
362
|
+
| `auth` | `AuthConfig` | - | Auth configuration |
|
|
363
|
+
| `defaults` | `DefaultConfig` | - | Global defaults |
|
|
364
|
+
| `logger` | `Logger` | console logger | Custom logger |
|
|
365
|
+
|
|
366
|
+
### `monapi.resource(name, config)`
|
|
367
|
+
|
|
368
|
+
| Option | Type | Description |
|
|
369
|
+
|--------|------|-------------|
|
|
370
|
+
| `schema` | `Schema \| Model` | Mongoose schema or model |
|
|
371
|
+
| `model` | `Model` | Explicit Mongoose model (optional) |
|
|
372
|
+
| `query` | `QueryConfig` | Filter/sort/pagination config |
|
|
373
|
+
| `hooks` | `LifecycleHooks` | Before/after hooks |
|
|
374
|
+
| `permissions` | `PermissionConfig` | Role-based or custom permissions |
|
|
375
|
+
| `middleware` | `MiddlewareConfig` | Custom middleware per operation |
|
|
376
|
+
| `handlers` | `CRUDHandlers` | Override default handlers |
|
|
377
|
+
| `adapter` | `SchemaAdapter` | Custom schema adapter |
|
|
378
|
+
|
|
379
|
+
### `monapi.router()`
|
|
380
|
+
|
|
381
|
+
Returns an Express `Router` with all registered collection routes.
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { Request, Response, NextFunction, RequestHandler, Router } from 'express';
|
|
2
|
+
import { FilterQuery, SortOrder, Model, Schema, Connection } from 'mongoose';
|
|
3
|
+
|
|
4
|
+
type FilterOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'like' | 'exists';
|
|
5
|
+
interface FilterCondition {
|
|
6
|
+
operator: FilterOperator;
|
|
7
|
+
value: any;
|
|
8
|
+
}
|
|
9
|
+
interface ParsedFilters {
|
|
10
|
+
[field: string]: FilterCondition;
|
|
11
|
+
}
|
|
12
|
+
interface QueryOptions {
|
|
13
|
+
page?: number;
|
|
14
|
+
limit?: number;
|
|
15
|
+
sort?: string | string[];
|
|
16
|
+
fields?: string | string[];
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
}
|
|
19
|
+
interface MongoQuery {
|
|
20
|
+
filter: FilterQuery<any>;
|
|
21
|
+
sort?: {
|
|
22
|
+
[key: string]: SortOrder;
|
|
23
|
+
};
|
|
24
|
+
skip?: number;
|
|
25
|
+
limit?: number;
|
|
26
|
+
projection?: {
|
|
27
|
+
[key: string]: 0 | 1;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface PaginationMeta {
|
|
31
|
+
page: number;
|
|
32
|
+
limit: number;
|
|
33
|
+
total: number;
|
|
34
|
+
totalPages?: number;
|
|
35
|
+
}
|
|
36
|
+
interface QueryConfig {
|
|
37
|
+
allowedFilters?: string[];
|
|
38
|
+
allowedSorts?: string[];
|
|
39
|
+
defaultSort?: string;
|
|
40
|
+
defaultLimit?: number;
|
|
41
|
+
maxLimit?: number;
|
|
42
|
+
}
|
|
43
|
+
declare enum FieldType {
|
|
44
|
+
String = "string",
|
|
45
|
+
Number = "number",
|
|
46
|
+
Boolean = "boolean",
|
|
47
|
+
Date = "date",
|
|
48
|
+
ObjectId = "objectid",
|
|
49
|
+
Array = "array",
|
|
50
|
+
Object = "object",
|
|
51
|
+
Mixed = "mixed"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type CRUDOperation = 'find' | 'create' | 'update' | 'patch' | 'delete';
|
|
55
|
+
interface User {
|
|
56
|
+
id: string;
|
|
57
|
+
roles?: string[];
|
|
58
|
+
[key: string]: any;
|
|
59
|
+
}
|
|
60
|
+
interface HookContext {
|
|
61
|
+
collection: string;
|
|
62
|
+
operation: CRUDOperation;
|
|
63
|
+
user?: User;
|
|
64
|
+
query?: MongoQuery;
|
|
65
|
+
data?: any;
|
|
66
|
+
result?: any;
|
|
67
|
+
id?: string;
|
|
68
|
+
req: Request;
|
|
69
|
+
res: Response;
|
|
70
|
+
meta?: Record<string, any>;
|
|
71
|
+
preventDefault?: boolean;
|
|
72
|
+
}
|
|
73
|
+
type HookFunction = (ctx: HookContext) => Promise<void> | void;
|
|
74
|
+
interface LifecycleHooks {
|
|
75
|
+
beforeFind?: HookFunction;
|
|
76
|
+
afterFind?: HookFunction;
|
|
77
|
+
beforeCreate?: HookFunction;
|
|
78
|
+
afterCreate?: HookFunction;
|
|
79
|
+
beforeUpdate?: HookFunction;
|
|
80
|
+
afterUpdate?: HookFunction;
|
|
81
|
+
beforeDelete?: HookFunction;
|
|
82
|
+
afterDelete?: HookFunction;
|
|
83
|
+
}
|
|
84
|
+
interface HookEntry {
|
|
85
|
+
collection: string;
|
|
86
|
+
operation: keyof LifecycleHooks;
|
|
87
|
+
handler: HookFunction;
|
|
88
|
+
priority?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface PermissionContext {
|
|
92
|
+
user: User;
|
|
93
|
+
collection: string;
|
|
94
|
+
operation: CRUDOperation;
|
|
95
|
+
data?: any;
|
|
96
|
+
id?: string;
|
|
97
|
+
req: Request;
|
|
98
|
+
}
|
|
99
|
+
type PermissionFunction = (ctx: PermissionContext) => boolean | Promise<boolean>;
|
|
100
|
+
type Permission = string[] | PermissionFunction;
|
|
101
|
+
interface PermissionConfig {
|
|
102
|
+
list?: Permission;
|
|
103
|
+
get?: Permission;
|
|
104
|
+
create?: Permission;
|
|
105
|
+
update?: Permission;
|
|
106
|
+
patch?: Permission;
|
|
107
|
+
delete?: Permission;
|
|
108
|
+
}
|
|
109
|
+
interface FieldPermission {
|
|
110
|
+
read?: string[];
|
|
111
|
+
write?: string[];
|
|
112
|
+
}
|
|
113
|
+
interface FieldPermissions {
|
|
114
|
+
[fieldName: string]: FieldPermission;
|
|
115
|
+
}
|
|
116
|
+
type AuthMiddleware = (req: Request, res: Response, next: NextFunction) => void | Promise<void>;
|
|
117
|
+
interface AuthConfig {
|
|
118
|
+
middleware?: AuthMiddleware;
|
|
119
|
+
jwtSecret?: string;
|
|
120
|
+
jwtOptions?: {
|
|
121
|
+
algorithm?: string;
|
|
122
|
+
expiresIn?: string;
|
|
123
|
+
};
|
|
124
|
+
sessionSecret?: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface ValidationResult {
|
|
128
|
+
valid: boolean;
|
|
129
|
+
errors?: ValidationError$1[];
|
|
130
|
+
data?: any;
|
|
131
|
+
}
|
|
132
|
+
interface ValidationError$1 {
|
|
133
|
+
field: string;
|
|
134
|
+
message: string;
|
|
135
|
+
code?: string;
|
|
136
|
+
}
|
|
137
|
+
interface FieldMetadata {
|
|
138
|
+
name: string;
|
|
139
|
+
type: FieldType;
|
|
140
|
+
required?: boolean;
|
|
141
|
+
default?: any;
|
|
142
|
+
enum?: any[];
|
|
143
|
+
}
|
|
144
|
+
interface SchemaAdapter {
|
|
145
|
+
getFields(): string[];
|
|
146
|
+
getFieldType(field: string): FieldType;
|
|
147
|
+
getFieldMetadata(field: string): FieldMetadata | undefined;
|
|
148
|
+
getAllFieldsMetadata(): FieldMetadata[];
|
|
149
|
+
validate(data: unknown): Promise<ValidationResult> | ValidationResult;
|
|
150
|
+
getMongooseModel?(): Model<any> | undefined;
|
|
151
|
+
getMongooseSchema?(): Schema | undefined;
|
|
152
|
+
}
|
|
153
|
+
declare enum SchemaType {
|
|
154
|
+
Mongoose = "mongoose",
|
|
155
|
+
Typegoose = "typegoose",
|
|
156
|
+
Zod = "zod",
|
|
157
|
+
Joi = "joi",
|
|
158
|
+
Yup = "yup",
|
|
159
|
+
Unknown = "unknown"
|
|
160
|
+
}
|
|
161
|
+
interface SchemaOptions {
|
|
162
|
+
strict?: boolean;
|
|
163
|
+
timestamps?: boolean;
|
|
164
|
+
validateBeforeSave?: boolean;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
type Handler = (req: Request, res: Response, next: NextFunction) => void | Promise<void>;
|
|
168
|
+
interface CRUDHandlers {
|
|
169
|
+
list: Handler;
|
|
170
|
+
get: Handler;
|
|
171
|
+
create: Handler;
|
|
172
|
+
update: Handler;
|
|
173
|
+
patch: Handler;
|
|
174
|
+
delete: Handler;
|
|
175
|
+
}
|
|
176
|
+
interface MiddlewareConfig {
|
|
177
|
+
all?: RequestHandler[];
|
|
178
|
+
list?: RequestHandler[];
|
|
179
|
+
get?: RequestHandler[];
|
|
180
|
+
create?: RequestHandler[];
|
|
181
|
+
update?: RequestHandler[];
|
|
182
|
+
patch?: RequestHandler[];
|
|
183
|
+
delete?: RequestHandler[];
|
|
184
|
+
}
|
|
185
|
+
interface CollectionConfig {
|
|
186
|
+
schema: Schema | Model<any> | any;
|
|
187
|
+
validator?: any;
|
|
188
|
+
model?: Model<any>;
|
|
189
|
+
handlers?: Partial<CRUDHandlers>;
|
|
190
|
+
hooks?: Partial<LifecycleHooks>;
|
|
191
|
+
middleware?: MiddlewareConfig;
|
|
192
|
+
permissions?: PermissionConfig;
|
|
193
|
+
fields?: FieldPermissions;
|
|
194
|
+
query?: QueryConfig;
|
|
195
|
+
adapter?: SchemaAdapter;
|
|
196
|
+
}
|
|
197
|
+
interface DefaultConfig {
|
|
198
|
+
pagination?: {
|
|
199
|
+
limit?: number;
|
|
200
|
+
maxLimit?: number;
|
|
201
|
+
};
|
|
202
|
+
security?: {
|
|
203
|
+
maxRegexLength?: number;
|
|
204
|
+
maxRegexComplexity?: number;
|
|
205
|
+
maxQueryCost?: number;
|
|
206
|
+
};
|
|
207
|
+
query?: QueryConfig;
|
|
208
|
+
}
|
|
209
|
+
interface MonapiConfig {
|
|
210
|
+
connection: Connection;
|
|
211
|
+
basePath?: string;
|
|
212
|
+
framework?: 'express' | 'fastify';
|
|
213
|
+
defaults?: DefaultConfig;
|
|
214
|
+
auth?: AuthConfig;
|
|
215
|
+
logger?: Logger;
|
|
216
|
+
}
|
|
217
|
+
interface Logger {
|
|
218
|
+
info(message: string, meta?: any): void;
|
|
219
|
+
warn(message: string, meta?: any): void;
|
|
220
|
+
error(message: string, meta?: any): void;
|
|
221
|
+
debug(message: string, meta?: any): void;
|
|
222
|
+
}
|
|
223
|
+
interface ListResponse<T = any> {
|
|
224
|
+
data: T[];
|
|
225
|
+
meta: {
|
|
226
|
+
page: number;
|
|
227
|
+
limit: number;
|
|
228
|
+
total: number;
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
interface SingleResponse<T = any> {
|
|
232
|
+
data: T;
|
|
233
|
+
}
|
|
234
|
+
interface ErrorResponse {
|
|
235
|
+
error: {
|
|
236
|
+
code: string;
|
|
237
|
+
message: string;
|
|
238
|
+
details?: any;
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
declare class Monapi {
|
|
243
|
+
private config;
|
|
244
|
+
private logger;
|
|
245
|
+
private collections;
|
|
246
|
+
private authMiddleware?;
|
|
247
|
+
constructor(config: MonapiConfig);
|
|
248
|
+
resource(name: string, collectionConfig: CollectionConfig): this;
|
|
249
|
+
router(): Router;
|
|
250
|
+
getModel(name: string): Model<any> | undefined;
|
|
251
|
+
getAdapter(name: string): SchemaAdapter | undefined;
|
|
252
|
+
private resolveModel;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
declare class MongooseAdapter implements SchemaAdapter {
|
|
256
|
+
private schema;
|
|
257
|
+
private model?;
|
|
258
|
+
constructor(schemaOrModel: Schema | Model<any>);
|
|
259
|
+
private isModel;
|
|
260
|
+
getFields(): string[];
|
|
261
|
+
getFieldType(field: string): FieldType;
|
|
262
|
+
getFieldMetadata(field: string): FieldMetadata | undefined;
|
|
263
|
+
getAllFieldsMetadata(): FieldMetadata[];
|
|
264
|
+
validate(data: unknown): Promise<ValidationResult>;
|
|
265
|
+
private basicValidation;
|
|
266
|
+
getMongooseModel(): Model<any> | undefined;
|
|
267
|
+
getMongooseSchema(): Schema;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
declare function detectSchemaType(schema: any): SchemaType;
|
|
271
|
+
declare function createSchemaAdapter(schema: Schema | Model<any> | any): SchemaAdapter;
|
|
272
|
+
|
|
273
|
+
declare class MonapiError extends Error {
|
|
274
|
+
readonly statusCode: number;
|
|
275
|
+
readonly code: string;
|
|
276
|
+
readonly details?: any;
|
|
277
|
+
constructor(message: string, statusCode: number, code: string, details?: any);
|
|
278
|
+
}
|
|
279
|
+
declare class NotFoundError extends MonapiError {
|
|
280
|
+
constructor(resource: string, id?: string);
|
|
281
|
+
}
|
|
282
|
+
declare class ValidationError extends MonapiError {
|
|
283
|
+
constructor(message: string, details?: any);
|
|
284
|
+
}
|
|
285
|
+
declare class ForbiddenError extends MonapiError {
|
|
286
|
+
constructor(message?: string);
|
|
287
|
+
}
|
|
288
|
+
declare class UnauthorizedError extends MonapiError {
|
|
289
|
+
constructor(message?: string);
|
|
290
|
+
}
|
|
291
|
+
declare class BadRequestError extends MonapiError {
|
|
292
|
+
constructor(message: string, details?: any);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
interface FilterParserOptions {
|
|
296
|
+
adapter?: SchemaAdapter;
|
|
297
|
+
queryConfig?: QueryConfig;
|
|
298
|
+
maxRegexLength?: number;
|
|
299
|
+
maxFilters?: number;
|
|
300
|
+
}
|
|
301
|
+
declare function parseFilters(query: Record<string, any>, options?: FilterParserOptions): FilterQuery<any>;
|
|
302
|
+
|
|
303
|
+
interface QueryBuilderOptions {
|
|
304
|
+
adapter?: SchemaAdapter;
|
|
305
|
+
queryConfig?: QueryConfig;
|
|
306
|
+
defaultLimit?: number;
|
|
307
|
+
maxLimit?: number;
|
|
308
|
+
maxRegexLength?: number;
|
|
309
|
+
}
|
|
310
|
+
declare function buildQuery(queryParams: Record<string, any>, options?: QueryBuilderOptions): MongoQuery;
|
|
311
|
+
declare function buildPaginationMeta(total: number, page: number, limit: number): PaginationMeta;
|
|
312
|
+
|
|
313
|
+
declare function createErrorHandler(logger?: Logger): (err: Error, _req: Request, res: Response, _next: NextFunction) => void;
|
|
314
|
+
|
|
315
|
+
export { type AuthConfig, type AuthMiddleware, BadRequestError, type CRUDHandlers, type CRUDOperation, type CollectionConfig, type DefaultConfig, type ErrorResponse, type FieldMetadata, type FieldPermission, type FieldPermissions, FieldType, type FilterCondition, type FilterOperator, ForbiddenError, type Handler, type HookContext, type HookEntry, type HookFunction, type LifecycleHooks, type ListResponse, type Logger, type MiddlewareConfig, Monapi, type MonapiConfig, MonapiError, type MongoQuery, MongooseAdapter, NotFoundError, type PaginationMeta, type ParsedFilters, type Permission, type PermissionConfig, type PermissionContext, type PermissionFunction, type QueryConfig, type QueryOptions, type SchemaAdapter, type SchemaOptions, SchemaType, type ValidationError$1 as SchemaValidationError, type SingleResponse, UnauthorizedError, type User, ValidationError, type ValidationResult, buildPaginationMeta, buildQuery, createErrorHandler, createSchemaAdapter, detectSchemaType, parseFilters };
|