monapi 0.1.0 → 1.0.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Auto-generate secure, extensible REST APIs for MongoDB collections using configuration only.
4
4
 
5
- Define a schema. Get a full CRUD API. No boilerplate.
5
+ Define a schema. Get a full CRUD API. No boilerplate. **Works with Express and Hono** (Bun/Deno/Cloudflare Workers).
6
6
 
7
7
  ## Install
8
8
 
@@ -10,10 +10,14 @@ Define a schema. Get a full CRUD API. No boilerplate.
10
10
  npm install monapi
11
11
  ```
12
12
 
13
- **Peer dependencies:**
13
+ **Peer dependencies (pick your framework):**
14
14
 
15
15
  ```bash
16
+ # Express (default)
16
17
  npm install express mongoose
18
+
19
+ # Hono (works on Bun, Deno, Cloudflare Workers, Node)
20
+ npm install hono mongoose
17
21
  ```
18
22
 
19
23
  ## Quick Start
@@ -54,6 +58,33 @@ PATCH /api/users/:id Partial update user
54
58
  DELETE /api/users/:id Delete user
55
59
  ```
56
60
 
61
+ ### Hono (Bun / Deno / Cloudflare Workers / Node)
62
+
63
+ ```ts
64
+ import { Hono } from 'hono'
65
+ import mongoose, { Schema } from 'mongoose'
66
+ import { Monapi } from 'monapi'
67
+
68
+ mongoose.connect('mongodb://localhost:27017/myapp')
69
+
70
+ const UserSchema = new Schema({
71
+ name: { type: String, required: true },
72
+ email: { type: String, required: true },
73
+ })
74
+
75
+ const monapi = new Monapi({
76
+ connection: mongoose.connection,
77
+ framework: 'hono',
78
+ })
79
+
80
+ monapi.resource('users', { schema: UserSchema })
81
+
82
+ const app = new Hono()
83
+ app.route('/api', monapi.router())
84
+
85
+ export default app // Works with Bun.serve, Deno.serve, etc.
86
+ ```
87
+
57
88
  ## Filtering
58
89
 
59
90
  ### Simple equality
@@ -358,6 +389,7 @@ monapi enforces safe defaults:
358
389
  | Option | Type | Default | Description |
359
390
  |--------|------|---------|-------------|
360
391
  | `connection` | `mongoose.Connection` | *required* | MongoDB connection |
392
+ | `framework` | `'express' \| 'hono' \| FrameworkAdapter` | `'express'` | Framework to use |
361
393
  | `basePath` | `string` | `''` | Base path prefix for routes |
362
394
  | `auth` | `AuthConfig` | - | Auth configuration |
363
395
  | `defaults` | `DefaultConfig` | - | Global defaults |
@@ -378,7 +410,37 @@ monapi enforces safe defaults:
378
410
 
379
411
  ### `monapi.router()`
380
412
 
381
- Returns an Express `Router` with all registered collection routes.
413
+ Returns a framework-specific router:
414
+ - **Express**: Express `Router` instance
415
+ - **Hono**: Hono app instance
416
+
417
+ ## Custom Framework Adapter
418
+
419
+ You can bring your own framework by implementing the `FrameworkAdapter` interface:
420
+
421
+ ```ts
422
+ import { Monapi, FrameworkAdapter } from 'monapi'
423
+
424
+ const myAdapter: FrameworkAdapter = {
425
+ name: 'my-framework',
426
+ createRouter(collections, options) { /* ... */ },
427
+ wrapHandler(handler) { /* ... */ },
428
+ createErrorHandler(logger) { /* ... */ },
429
+ }
430
+
431
+ const monapi = new Monapi({
432
+ connection: mongoose.connection,
433
+ framework: myAdapter,
434
+ })
435
+ ```
436
+
437
+ ## Contributing
438
+
439
+ Contributions welcome! Areas that need help:
440
+ - **Fastify adapter** - `src/adapters/framework/fastify.ts`
441
+ - **NestJS adapter** - `src/adapters/framework/nestjs.ts`
442
+ - **Koa adapter** - `src/adapters/framework/koa.ts`
443
+ - **Zod/Joi/Yup schema adapters** - `src/adapters/schema/`
382
444
 
383
445
  ## License
384
446
 
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { Request, Response, NextFunction, RequestHandler, Router } from 'express';
2
1
  import { FilterQuery, SortOrder, Model, Schema, Connection } from 'mongoose';
2
+ import { Request, Response, NextFunction, RequestHandler, Router } from 'express';
3
3
 
4
4
  type FilterOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'like' | 'exists';
5
5
  interface FilterCondition {
@@ -239,19 +239,83 @@ interface ErrorResponse {
239
239
  };
240
240
  }
241
241
 
242
+ interface MonapiRequest {
243
+ params: Record<string, string>;
244
+ query: Record<string, any>;
245
+ body: any;
246
+ headers: Record<string, string | string[] | undefined>;
247
+ method: string;
248
+ path: string;
249
+ user?: any;
250
+ raw: any;
251
+ }
252
+ interface MonapiResponse {
253
+ status(code: number): MonapiResponse;
254
+ json(data: any): void;
255
+ setHeader(key: string, value: string): MonapiResponse;
256
+ raw: any;
257
+ }
258
+ type MonapiHandler = (req: MonapiRequest, res: MonapiResponse) => Promise<void> | void;
259
+ interface OperationResult {
260
+ statusCode: number;
261
+ data: any;
262
+ meta?: {
263
+ page: number;
264
+ limit: number;
265
+ total: number;
266
+ totalPages?: number;
267
+ };
268
+ }
269
+ interface CollectionContext {
270
+ name: string;
271
+ model: Model<any>;
272
+ adapter: SchemaAdapter;
273
+ config: CollectionConfig;
274
+ defaults?: DefaultConfig;
275
+ logger?: Logger;
276
+ }
277
+ interface FrameworkAdapter {
278
+ readonly name: string;
279
+ createRouter(collections: Map<string, CollectionContext>, options?: {
280
+ basePath?: string;
281
+ authMiddleware?: any;
282
+ }): any;
283
+ wrapHandler(handler: MonapiHandler): any;
284
+ createErrorHandler(logger?: Logger): any;
285
+ }
286
+ type BuiltinFramework = 'express' | 'hono';
287
+
242
288
  declare class Monapi {
243
289
  private config;
244
290
  private logger;
245
291
  private collections;
246
- private authMiddleware?;
292
+ private frameworkAdapter;
247
293
  constructor(config: MonapiConfig);
248
294
  resource(name: string, collectionConfig: CollectionConfig): this;
249
- router(): Router;
295
+ router(): any;
296
+ getFrameworkAdapter(): FrameworkAdapter;
250
297
  getModel(name: string): Model<any> | undefined;
251
298
  getAdapter(name: string): SchemaAdapter | undefined;
252
299
  private resolveModel;
253
300
  }
254
301
 
302
+ interface OperationOptions {
303
+ collectionName: string;
304
+ model: Model<any>;
305
+ adapter: SchemaAdapter;
306
+ config: CollectionConfig;
307
+ defaults?: DefaultConfig;
308
+ logger?: Logger;
309
+ }
310
+ declare function listDocuments(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
311
+ declare function getDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
312
+ declare function createDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
313
+ declare function updateDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
314
+ declare function patchDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
315
+ declare function deleteDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
316
+
317
+ declare function checkPermissions(req: MonapiRequest, collection: string, routeOp: string, permissions?: PermissionConfig): Promise<void>;
318
+
255
319
  declare class MongooseAdapter implements SchemaAdapter {
256
320
  private schema;
257
321
  private model?;
@@ -270,6 +334,31 @@ declare class MongooseAdapter implements SchemaAdapter {
270
334
  declare function detectSchemaType(schema: any): SchemaType;
271
335
  declare function createSchemaAdapter(schema: Schema | Model<any> | any): SchemaAdapter;
272
336
 
337
+ declare class ExpressAdapter implements FrameworkAdapter {
338
+ readonly name = "express";
339
+ createRouter(collections: Map<string, CollectionContext>, options?: {
340
+ basePath?: string;
341
+ authMiddleware?: RequestHandler;
342
+ }): Router;
343
+ wrapHandler(handler: MonapiHandler): RequestHandler;
344
+ createErrorHandler(logger?: Logger): any;
345
+ private buildCollectionRouter;
346
+ private handleOp;
347
+ }
348
+
349
+ declare class HonoAdapter implements FrameworkAdapter {
350
+ readonly name = "hono";
351
+ createRouter(collections: Map<string, CollectionContext>, options?: {
352
+ basePath?: string;
353
+ authMiddleware?: any;
354
+ }): any;
355
+ wrapHandler(handler: MonapiHandler): any;
356
+ createErrorHandler(logger?: Logger): any;
357
+ private registerCollectionRoutes;
358
+ }
359
+
360
+ declare function resolveFrameworkAdapter(framework?: BuiltinFramework | FrameworkAdapter): FrameworkAdapter;
361
+
273
362
  declare class MonapiError extends Error {
274
363
  readonly statusCode: number;
275
364
  readonly code: string;
@@ -312,4 +401,4 @@ declare function buildPaginationMeta(total: number, page: number, limit: number)
312
401
 
313
402
  declare function createErrorHandler(logger?: Logger): (err: Error, _req: Request, res: Response, _next: NextFunction) => void;
314
403
 
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 };
404
+ export { type AuthConfig, type AuthMiddleware, BadRequestError, type BuiltinFramework, type CRUDHandlers, type CRUDOperation, type CollectionConfig, type CollectionContext, type DefaultConfig, type ErrorResponse, ExpressAdapter, type FieldMetadata, type FieldPermission, type FieldPermissions, FieldType, type FilterCondition, type FilterOperator, ForbiddenError, type FrameworkAdapter, type Handler, HonoAdapter, type HookContext, type HookEntry, type HookFunction, type LifecycleHooks, type ListResponse, type Logger, type MiddlewareConfig, Monapi, type MonapiConfig, MonapiError, type MonapiHandler, type MonapiRequest, type MonapiResponse, type MongoQuery, MongooseAdapter, NotFoundError, type OperationResult, 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, checkPermissions, createDocument, createErrorHandler, createSchemaAdapter, deleteDocument, detectSchemaType, getDocument, listDocuments, parseFilters, patchDocument, resolveFrameworkAdapter, updateDocument };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Request, Response, NextFunction, RequestHandler, Router } from 'express';
2
1
  import { FilterQuery, SortOrder, Model, Schema, Connection } from 'mongoose';
2
+ import { Request, Response, NextFunction, RequestHandler, Router } from 'express';
3
3
 
4
4
  type FilterOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'like' | 'exists';
5
5
  interface FilterCondition {
@@ -239,19 +239,83 @@ interface ErrorResponse {
239
239
  };
240
240
  }
241
241
 
242
+ interface MonapiRequest {
243
+ params: Record<string, string>;
244
+ query: Record<string, any>;
245
+ body: any;
246
+ headers: Record<string, string | string[] | undefined>;
247
+ method: string;
248
+ path: string;
249
+ user?: any;
250
+ raw: any;
251
+ }
252
+ interface MonapiResponse {
253
+ status(code: number): MonapiResponse;
254
+ json(data: any): void;
255
+ setHeader(key: string, value: string): MonapiResponse;
256
+ raw: any;
257
+ }
258
+ type MonapiHandler = (req: MonapiRequest, res: MonapiResponse) => Promise<void> | void;
259
+ interface OperationResult {
260
+ statusCode: number;
261
+ data: any;
262
+ meta?: {
263
+ page: number;
264
+ limit: number;
265
+ total: number;
266
+ totalPages?: number;
267
+ };
268
+ }
269
+ interface CollectionContext {
270
+ name: string;
271
+ model: Model<any>;
272
+ adapter: SchemaAdapter;
273
+ config: CollectionConfig;
274
+ defaults?: DefaultConfig;
275
+ logger?: Logger;
276
+ }
277
+ interface FrameworkAdapter {
278
+ readonly name: string;
279
+ createRouter(collections: Map<string, CollectionContext>, options?: {
280
+ basePath?: string;
281
+ authMiddleware?: any;
282
+ }): any;
283
+ wrapHandler(handler: MonapiHandler): any;
284
+ createErrorHandler(logger?: Logger): any;
285
+ }
286
+ type BuiltinFramework = 'express' | 'hono';
287
+
242
288
  declare class Monapi {
243
289
  private config;
244
290
  private logger;
245
291
  private collections;
246
- private authMiddleware?;
292
+ private frameworkAdapter;
247
293
  constructor(config: MonapiConfig);
248
294
  resource(name: string, collectionConfig: CollectionConfig): this;
249
- router(): Router;
295
+ router(): any;
296
+ getFrameworkAdapter(): FrameworkAdapter;
250
297
  getModel(name: string): Model<any> | undefined;
251
298
  getAdapter(name: string): SchemaAdapter | undefined;
252
299
  private resolveModel;
253
300
  }
254
301
 
302
+ interface OperationOptions {
303
+ collectionName: string;
304
+ model: Model<any>;
305
+ adapter: SchemaAdapter;
306
+ config: CollectionConfig;
307
+ defaults?: DefaultConfig;
308
+ logger?: Logger;
309
+ }
310
+ declare function listDocuments(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
311
+ declare function getDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
312
+ declare function createDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
313
+ declare function updateDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
314
+ declare function patchDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
315
+ declare function deleteDocument(req: MonapiRequest, res: MonapiResponse, opts: OperationOptions): Promise<OperationResult>;
316
+
317
+ declare function checkPermissions(req: MonapiRequest, collection: string, routeOp: string, permissions?: PermissionConfig): Promise<void>;
318
+
255
319
  declare class MongooseAdapter implements SchemaAdapter {
256
320
  private schema;
257
321
  private model?;
@@ -270,6 +334,31 @@ declare class MongooseAdapter implements SchemaAdapter {
270
334
  declare function detectSchemaType(schema: any): SchemaType;
271
335
  declare function createSchemaAdapter(schema: Schema | Model<any> | any): SchemaAdapter;
272
336
 
337
+ declare class ExpressAdapter implements FrameworkAdapter {
338
+ readonly name = "express";
339
+ createRouter(collections: Map<string, CollectionContext>, options?: {
340
+ basePath?: string;
341
+ authMiddleware?: RequestHandler;
342
+ }): Router;
343
+ wrapHandler(handler: MonapiHandler): RequestHandler;
344
+ createErrorHandler(logger?: Logger): any;
345
+ private buildCollectionRouter;
346
+ private handleOp;
347
+ }
348
+
349
+ declare class HonoAdapter implements FrameworkAdapter {
350
+ readonly name = "hono";
351
+ createRouter(collections: Map<string, CollectionContext>, options?: {
352
+ basePath?: string;
353
+ authMiddleware?: any;
354
+ }): any;
355
+ wrapHandler(handler: MonapiHandler): any;
356
+ createErrorHandler(logger?: Logger): any;
357
+ private registerCollectionRoutes;
358
+ }
359
+
360
+ declare function resolveFrameworkAdapter(framework?: BuiltinFramework | FrameworkAdapter): FrameworkAdapter;
361
+
273
362
  declare class MonapiError extends Error {
274
363
  readonly statusCode: number;
275
364
  readonly code: string;
@@ -312,4 +401,4 @@ declare function buildPaginationMeta(total: number, page: number, limit: number)
312
401
 
313
402
  declare function createErrorHandler(logger?: Logger): (err: Error, _req: Request, res: Response, _next: NextFunction) => void;
314
403
 
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 };
404
+ export { type AuthConfig, type AuthMiddleware, BadRequestError, type BuiltinFramework, type CRUDHandlers, type CRUDOperation, type CollectionConfig, type CollectionContext, type DefaultConfig, type ErrorResponse, ExpressAdapter, type FieldMetadata, type FieldPermission, type FieldPermissions, FieldType, type FilterCondition, type FilterOperator, ForbiddenError, type FrameworkAdapter, type Handler, HonoAdapter, type HookContext, type HookEntry, type HookFunction, type LifecycleHooks, type ListResponse, type Logger, type MiddlewareConfig, Monapi, type MonapiConfig, MonapiError, type MonapiHandler, type MonapiRequest, type MonapiResponse, type MongoQuery, MongooseAdapter, NotFoundError, type OperationResult, 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, checkPermissions, createDocument, createErrorHandler, createSchemaAdapter, deleteDocument, detectSchemaType, getDocument, listDocuments, parseFilters, patchDocument, resolveFrameworkAdapter, updateDocument };