{"id":14970064,"url":"https://github.com/vivocha/arrest","last_synced_at":"2025-10-26T11:31:25.227Z","repository":{"id":8770438,"uuid":"10455958","full_name":"vivocha/arrest","owner":"vivocha","description":"Swagger REST framework for Node.js, with support for MongoDB and JSON-Schema","archived":false,"fork":false,"pushed_at":"2022-12-22T08:37:39.000Z","size":624,"stargazers_count":17,"open_issues_count":1,"forks_count":4,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-30T04:49:28.343Z","etag":null,"topics":["express","expressjs","jsonschema","mongodb","node","nodejs","oauth2","openapi","rest","rest-api","rql","schema","swagger"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vivocha.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-06-03T14:10:54.000Z","updated_at":"2024-05-20T09:21:28.000Z","dependencies_parsed_at":"2023-01-13T14:59:45.291Z","dependency_job_id":null,"html_url":"https://github.com/vivocha/arrest","commit_stats":null,"previous_names":[],"tags_count":103,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivocha%2Farrest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivocha%2Farrest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivocha%2Farrest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivocha%2Farrest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vivocha","download_url":"https://codeload.github.com/vivocha/arrest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238319449,"owners_count":19452339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["express","expressjs","jsonschema","mongodb","node","nodejs","oauth2","openapi","rest","rest-api","rql","schema","swagger"],"created_at":"2024-09-24T13:42:58.506Z","updated_at":"2025-10-26T11:31:25.218Z","avatar_url":"https://github.com/vivocha.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# arrest\n\nA powerful OpenAPI v3 compliant REST framework for Node.js with comprehensive MongoDB support, JSON Schema validation, authentication, and authorization. Build production-ready RESTful APIs in minutes with automatic OpenAPI documentation generation.\n\n[![npm version](https://img.shields.io/npm/v/arrest.svg)](https://www.npmjs.com/package/arrest)\n[![CI](https://github.com/vivocha/arrest/actions/workflows/ci.yml/badge.svg)](https://github.com/vivocha/arrest/actions/workflows/ci.yml)\n[![Coverage Status](https://coveralls.io/repos/github/vivocha/arrest/badge.svg?branch=master)](https://coveralls.io/github/vivocha/arrest?branch=master)\n\n## Features\n\n- ✅ **OpenAPI v3 Compliant**: Automatic OpenAPI specification generation with full v3 support\n- ✅ **MongoDB Integration**: Built-in MongoDB operations (CRUD) with advanced querying\n- ✅ **JSON Schema Validation**: Comprehensive input validation using JSON Schema\n- ✅ **Authentication \u0026 Authorization**: OAuth2 scopes and CASL ability-based permissions\n- ✅ **Resource Query Language (RQL)**: Advanced querying with filtering, sorting, and pagination\n- ✅ **Express.js Integration**: Works seamlessly with existing Express.js applications\n- ✅ **CSV Export**: Built-in CSV export functionality for data endpoints\n- ✅ **JSON-RPC Support**: Dual REST and JSON-RPC endpoint support\n- ✅ **Pipeline Operations**: Complex data processing with pipeline support\n- ✅ **TypeScript Support**: Full TypeScript definitions included\n- ✅ **Modern ES Modules**: Supports both ESM and CommonJS\n\n## Installation\n\n```bash\n# npm\nnpm install arrest\n\n# pnpm  \npnpm add arrest\n\n# yarn\nyarn add arrest\n```\n\n## Quick Start\n\n### Basic REST API\n\n```javascript\nimport { API, MongoResource } from 'arrest';\n\nconst api = new API({\n  info: {\n    title: 'My API',\n    version: '1.0.0'\n  }\n});\n\n// Add a MongoDB-backed resource\napi.addResource(new MongoResource('mongodb://localhost:27017/mydb', {\n  name: 'User',\n  collection: 'users'\n}));\n\n// Start the server\napi.listen(3000);\nconsole.log('API running at http://localhost:3000');\nconsole.log('OpenAPI spec at http://localhost:3000/openapi.json');\n```\n\nThis creates a full CRUD API for users with the following endpoints:\n- `GET /users` - List users with filtering, sorting, pagination\n- `POST /users` - Create a new user\n- `GET /users/{id}` - Get user by ID\n- `PUT /users/{id}` - Update user\n- `PATCH /users/{id}` - Partial update user with JSON Patch\n- `DELETE /users/{id}` - Delete user\n\n### Testing Your API\n\nOnce your API is running, you can interact with it using curl or any HTTP client:\n\n```bash\n# List all users\ncurl \"http://localhost:3000/users\"\n\n# Create a new user\ncurl \"http://localhost:3000/users\" \\\n  -H \"Content-Type: application/json\" \\\n  -X POST \\\n  -d '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'\n\n# Get a specific user\ncurl \"http://localhost:3000/users/507f1f77bcf86cd799439011\"\n\n# Update a user\ncurl \"http://localhost:3000/users/507f1f77bcf86cd799439011\" \\\n  -H \"Content-Type: application/json\" \\\n  -X PUT \\\n  -d '{\"name\": \"John Smith\", \"email\": \"john.smith@example.com\"}'\n\n# Delete a user\ncurl \"http://localhost:3000/users/507f1f77bcf86cd799439011\" -X DELETE\n```\n\n### Custom Operations\n\n```javascript\nimport { API, Resource, Operation } from 'arrest';\n\nclass CustomOperation extends Operation {\n  constructor(resource, path, method) {\n    super(resource, path, method, 'customOp');\n  }\n\n  getDefaultInfo() {\n    return {\n      summary: 'Custom operation',\n      description: 'Performs a custom operation',\n      responses: {\n        '200': {\n          description: 'Success',\n          content: {\n            'application/json': {\n              schema: { type: 'object' }\n            }\n          }\n        }\n      }\n    };\n  }\n\n  async handler(req, res, next) {\n    try {\n      const result = await this.runOperation({ req, res });\n      res.json(result);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async runOperation(job) {\n    return { message: 'Custom operation executed', timestamp: new Date() };\n  }\n}\n\nconst api = new API();\nconst resource = new Resource({ name: 'Custom' });\n\nresource.addOperation(new CustomOperation(resource, '/action', 'post'));\napi.addResource(resource);\n```\n\n## Core Concepts\n\narrest follows a three-tier architecture:\n\n1. **API** - The top-level container that manages resources and generates OpenAPI specifications\n2. **Resource** - A collection of related operations (e.g., User resource with CRUD operations)\n3. **Operation** - Individual HTTP endpoints that handle specific requests\n\n### Architecture Overview\n\n```javascript\nimport { API, Resource, Operation } from 'arrest';\n\n// 1. Create API instance\nconst api = new API({\n  info: {\n    title: 'My REST API',\n    version: '1.0.0',\n    description: 'A comprehensive REST API built with arrest'\n  }\n});\n\n// 2. Create resource with operations\nconst userResource = new Resource({\n  name: 'User',\n  path: 'users' // Optional: defaults to plural of name\n});\n\n// 3. Add custom operations to resource\nuserResource.addOperation('/profile', 'get', async (req, res) =\u003e {\n  res.json({ profile: 'user profile data' });\n});\n\n// 4. Add resource to API\napi.addResource(userResource);\n\n// 5. Start the server\napi.listen(3000);\n```\n\n### Resource Naming and Paths\n\narrest automatically converts resource names to RESTful paths:\n\n```javascript\n// Resource name -\u003e Path conversion\nnew Resource({ name: 'User' });        // -\u003e /users\nnew Resource({ name: 'BlogPost' });    // -\u003e /blog-posts  \nnew Resource({ name: 'UserProfile' }); // -\u003e /user-profiles\n\n// Custom path override\nnew Resource({ \n  name: 'User', \n  path: 'customers',           // Custom path\n  namePlural: 'CustomerList'   // Custom plural name\n});\n```\n\n## API Reference\n\n### API Class\n\nThe main API container that manages resources and server configuration.\n\n**Constructor Options:**\n```javascript\nnew API({\n  info: {\n    title: 'API Title',\n    version: '1.0.0',\n    description: 'API Description'\n  },\n  servers: [\n    { url: 'https://api.example.com', description: 'Production' },\n    { url: 'http://localhost:3000', description: 'Development' }\n  ],\n  security: [\n    { bearerAuth: [] }\n  ]\n})\n```\n\n**Key Methods:**\n- `addResource(resource)` - Add a resource to the API\n- `listen(port, callback?)` - Start HTTP server\n- `listen({ http: 3000, https: 3443, options })` - Start HTTP/HTTPS servers\n- `router()` - Get Express router for integration\n- `attach(app, path?)` - Attach to existing Express app\n\n### Resource Class\n\nRepresents a collection of related operations.\n\n**Constructor Options:**\n```javascript\nnew Resource({\n  name: 'User',                    // Resource name (required)\n  path: 'users',                   // Custom path (optional)\n  namePlural: 'Users',             // Custom plural name (optional)\n  description: 'User management'    // OpenAPI description (optional)\n})\n```\n\n**Key Methods:**\n- `addOperation(path, method, handler)` - Add simple operation\n- `addOperation(operationInstance)` - Add operation instance\n\n### MongoResource Class\n\nSpecialized resource for MongoDB collections with built-in CRUD operations.\n\n**Constructor:**\n```javascript\nnew MongoResource(connectionUri, options, customRoutes?)\n```\n\n**Options:**\n```javascript\n{\n  name: 'User',                    // Resource name\n  collection: 'users',            // MongoDB collection name\n  id: '_id',                       // ID field name (default: '_id')\n  idIsObjectId: true,              // Whether ID is ObjectId (default: true)\n  queryLimit: 100,                 // Maximum query results (default: no limit)\n  createIndexes: false,            // Auto-create indexes (default: false)\n  escapeProperties: false          // Escape MongoDB special characters (default: false)\n}\n```\n\n### Operation Class\n\nBase class for individual API operations.\n\n**Constructor:**\n```javascript\nnew Operation(resource, path, method, operationId)\n```\n\n**Key Methods to Override:**\n- `getDefaultInfo()` - Return OpenAPI operation info\n- `handler(req, res, next)` - Express request handler\n- `runOperation(job)` - Main operation logic\n\n## Advanced Features\n\n### JSON Schema Validation\n\narrest provides comprehensive input validation using OpenAPI v3 and JSON Schema:\n\n```javascript\nimport { Operation } from 'arrest';\n\nclass CreateUserOperation extends Operation {\n  constructor(resource, path, method) {\n    super(resource, path, method, 'createUser');\n  }\n\n  getDefaultInfo() {\n    return {\n      summary: 'Create a new user',\n      requestBody: {\n        required: true,\n        content: {\n          'application/json': {\n            schema: {\n              type: 'object',\n              required: ['name', 'email'],\n              additionalProperties: false,\n              properties: {\n                name: {\n                  type: 'string',\n                  minLength: 1,\n                  maxLength: 100\n                },\n                email: {\n                  type: 'string',\n                  format: 'email'\n                },\n                age: {\n                  type: 'integer',\n                  minimum: 0,\n                  maximum: 150\n                }\n              }\n            }\n          }\n        }\n      },\n      parameters: [\n        {\n          name: 'include',\n          in: 'query',\n          schema: {\n            type: 'array',\n            items: { type: 'string', enum: ['profile', 'preferences'] }\n          },\n          style: 'form',\n          explode: false\n        }\n      ]\n    };\n  }\n\n  async runOperation(job) {\n    const { name, email, age } = job.req.body;\n    const include = job.req.query.include || [];\n    \n    // Create user logic here\n    return { id: '123', name, email, age, created: new Date() };\n  }\n}\n```\n\n### Resource Query Language (RQL)\n\narrest supports powerful querying with RQL syntax:\n\n```javascript\n// Examples of RQL queries\nconst queries = [\n  // Basic filtering\n  'eq(status,active)',\n  'gt(age,18)',\n  'in(category,electronics,books)',\n  \n  // Complex queries\n  'and(eq(status,active),gt(price,100))',\n  'or(eq(category,sale),lt(price,50))',\n  \n  // Sorting and pagination\n  'sort(+name,-created)',\n  'limit(10,20)', // limit(count, offset)\n  \n  // Field selection\n  'select(name,email,created)'\n];\n\n// Use in API calls\n// GET /users?q=and(eq(status,active),gt(age,18))\u0026sort=-created\u0026limit(10)\n```\n\n### Authentication and Authorization\n\n#### OAuth2 Scopes\n\n```javascript\nimport { API, MongoResource } from 'arrest';\n\nclass SecureAPI extends API {\n  initSecurity(req, res, next) {\n    // Extract and validate OAuth2 token\n    const token = req.headers.authorization?.replace('Bearer ', '');\n    if (!token) {\n      return res.status(401).json({ error: 'Missing authorization token' });\n    }\n\n    // Validate token and set scopes\n    req.scopes = ['read:users', 'write:users']; // From token validation\n    next();\n  }\n}\n\nclass SecureOperation extends Operation {\n  get swaggerScopes() {\n    return {\n      'oauth2': ['read:users', 'write:users']\n    };\n  }\n}\n```\n\n#### CASL Ability-based Permissions\n\n```javascript\nimport { defineAbility } from '@casl/ability';\n\nclass AuthorizedAPI extends API {\n  initSecurity(req, res, next) {\n    // Define user abilities based on their role\n    req.ability = defineAbility((can, cannot) =\u003e {\n      if (req.user.role === 'admin') {\n        can('manage', 'all');\n      } else if (req.user.role === 'user') {\n        can('read', 'User', { ownerId: req.user.id });\n        can('update', 'User', { ownerId: req.user.id });\n        cannot('delete', 'User');\n      }\n    });\n    next();\n  }\n}\n```\n\n### MongoDB Integration\n\n#### Advanced MongoDB Operations\n\n```javascript\nimport { MongoResource, QueryMongoOperation } from 'arrest';\n\nclass AdvancedUserResource extends MongoResource {\n  constructor() {\n    super('mongodb://localhost:27017/myapp', {\n      name: 'User',\n      collection: 'users',\n      createIndexes: true\n    });\n  }\n\n  getIndexes() {\n    return [\n      { key: { email: 1 }, unique: true },\n      { key: { 'profile.tags': 1 } },\n      { key: { createdAt: -1 } }\n    ];\n  }\n}\n\n// Custom aggregation operation\nclass UserStatsOperation extends QueryMongoOperation {\n  async prepareQuery(job) {\n    // Return MongoDB aggregation pipeline instead of simple query\n    return [\n      { $match: { status: 'active' } },\n      { $group: {\n          _id: '$department',\n          count: { $sum: 1 },\n          averageAge: { $avg: '$age' }\n        }\n      },\n      { $sort: { count: -1 } }\n    ];\n  }\n}\n```\n\n### CSV Export\n\nBuilt-in CSV export functionality:\n\n```javascript\n// GET /users?format=csv\u0026csv_fields=name,email,created\n// GET /users?format=csv\u0026csv_fields=name,email\u0026csv_options=header=true\u0026csv_names=Name,Email\n```\n\n### JSON-RPC Support\n\narrest supports dual REST and JSON-RPC interfaces:\n\n```javascript\nimport { RPCOperation } from 'arrest';\n\nclass UserRPCOperation extends RPCOperation {\n  async getUserProfile(params) {\n    const { userId } = params;\n    // Fetch user profile logic\n    return { profile: { id: userId, name: 'John Doe' } };\n  }\n\n  async updateUserProfile(params) {\n    const { userId, updates } = params;\n    // Update logic\n    return { success: true, updated: updates };\n  }\n}\n\n// JSON-RPC calls:\n// POST /users/rpc\n// {\"jsonrpc\": \"2.0\", \"method\": \"getUserProfile\", \"params\": {\"userId\": \"123\"}, \"id\": 1}\n```\n\n### Pipeline Operations\n\nComplex data processing with pipeline support:\n\n```javascript\nimport { PipelineOperation } from 'arrest';\n\nclass DataProcessingPipeline extends PipelineOperation {\n  async runOperation(job) {\n    let data = await super.runOperation(job);\n    \n    // Apply transformations\n    data = this.filterSensitiveData(data);\n    data = this.calculateDerivedFields(data);\n    data = this.formatForOutput(data, job.req.query.format);\n    \n    return data;\n  }\n\n  filterSensitiveData(data) {\n    // Remove sensitive fields based on user permissions\n    return data.map(item =\u003e this.filterFields(item, job.req.ability));\n  }\n}\n```\n\n### Express.js Integration\n\narrest works seamlessly with existing Express applications:\n\n```javascript\nimport express from 'express';\nimport { API, MongoResource } from 'arrest';\n\nconst app = express();\nconst api = new API();\n\n// Add resources to API\napi.addResource(new MongoResource('mongodb://localhost:27017/mydb', {\n  name: 'User'\n}));\n\n// Mount API on Express app\napp.use('/api/v1', await api.router());\n\n// Add other Express routes\napp.get('/health', (req, res) =\u003e {\n  res.json({ status: 'healthy' });\n});\n\napp.listen(3000);\n```\n\n### Error Handling\n\nComprehensive error handling with detailed responses:\n\n```javascript\nimport { API } from 'arrest';\n\nclass CustomAPI extends API {\n  handleError(error, req, res, next) {\n    if (error.name === 'ValidationError') {\n      return res.status(400).json({\n        error: 'Validation failed',\n        details: error.errors,\n        path: error.path\n      });\n    }\n    \n    if (error.code === 11000) { // MongoDB duplicate key\n      return res.status(409).json({\n        error: 'Resource already exists',\n        field: Object.keys(error.keyPattern)[0]\n      });\n    }\n    \n    // Default error handling\n    super.handleError(error, req, res, next);\n  }\n}\n```\n\n## TypeScript Support\n\nFull TypeScript definitions are included:\n\n```typescript\nimport { API, MongoResource, Operation } from 'arrest';\nimport { Request, Response } from 'express';\n\ninterface User {\n  id: string;\n  name: string;\n  email: string;\n  createdAt: Date;\n}\n\nclass TypedUserOperation extends Operation {\n  async runOperation(job: { req: Request; res: Response }): Promise\u003cUser\u003e {\n    const userData = job.req.body as Partial\u003cUser\u003e;\n    \n    // Implementation with type safety\n    return {\n      id: 'generated-id',\n      name: userData.name!,\n      email: userData.email!,\n      createdAt: new Date()\n    };\n  }\n}\n```\n\n## Performance and Production\n\n### Optimization Tips\n\n1. **Use MongoDB indexes** - Define indexes for frequently queried fields\n2. **Implement caching** - Use Redis or memory caching for frequently accessed data  \n3. **Limit query results** - Set reasonable queryLimit on resources\n4. **Use projections** - Only fetch needed fields with the `fields` parameter\n5. **Enable compression** - Use gzip compression in production\n\n### Production Configuration\n\n```javascript\nimport { API, MongoResource } from 'arrest';\n\nconst api = new API({\n  info: { title: 'Production API', version: '1.0.0' }\n});\n\n// Production MongoDB resource with optimization\napi.addResource(new MongoResource('mongodb://mongo-cluster/prod-db', {\n  name: 'User',\n  collection: 'users',\n  queryLimit: 100,        // Limit results\n  createIndexes: true,    // Auto-create indexes\n  escapeProperties: true  // Security: escape special chars\n}));\n\n// Start with both HTTP and HTTPS\napi.listen({\n  http: 8080,\n  https: 8443,\n  httpsOptions: {\n    key: fs.readFileSync('private-key.pem'),\n    cert: fs.readFileSync('certificate.pem')\n  }\n});\n```\n\n## License\n\nMIT License - see the [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please ensure all tests pass:\n\n```bash\npnpm install\npnpm test\npnpm run coverage\n```\n\n## Related Projects\n\n- [jsonref](https://github.com/vivocha/jsonref) - JSON Reference resolution\n- [jsonpolice](https://github.com/vivocha/jsonpolice) - JSON Schema validation  \n- [openapi-police](https://github.com/vivocha/openapi-police) - OpenAPI validation utilities\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvivocha%2Farrest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvivocha%2Farrest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvivocha%2Farrest/lists"}