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 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
@@ -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 };