{"id":18408267,"url":"https://github.com/protectwise/apone","last_synced_at":"2025-04-07T08:32:58.450Z","repository":{"id":57182429,"uuid":"101441433","full_name":"protectwise/apone","owner":"protectwise","description":"Configure and validate express routes with objects. Register routes in any order. Extend as necessary.","archived":false,"fork":false,"pushed_at":"2018-08-29T00:11:50.000Z","size":28,"stargazers_count":6,"open_issues_count":2,"forks_count":3,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-22T16:02:07.425Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/protectwise.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":"2017-08-25T20:53:37.000Z","updated_at":"2018-08-29T00:11:52.000Z","dependencies_parsed_at":"2022-09-11T22:40:19.158Z","dependency_job_id":null,"html_url":"https://github.com/protectwise/apone","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protectwise%2Fapone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protectwise%2Fapone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protectwise%2Fapone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/protectwise%2Fapone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/protectwise","download_url":"https://codeload.github.com/protectwise/apone/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247620823,"owners_count":20968295,"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":[],"created_at":"2024-11-06T03:18:05.039Z","updated_at":"2025-04-07T08:32:55.155Z","avatar_url":"https://github.com/protectwise.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Apone\n\nConfigure and validate express routes with objects. Register routes in any order. Extend as necessary. \n\n## Install and Quick Start\n\n`npm install apone`\n\n```js\nconst Express = require('express')\nconst Apone = require('apone')\n\nlet app = Express()\nlet apone = new Apone(app)\n\napone.register({\n  path: '/hello',\n  method: 'get',\n  handle: (req, res, next) =\u003e {\n\n    res.send('world')\n    return next()\n  }\n})\n\napp.listen(3000)\n```\n\n# table of contents\n- [api](#api)\n- [routes](#routes)\n- [schemas](#schemas)\n- [validationResults](#validationresult)\n- [extensions](#extensions)\n- [route-behavior](#route-behavior)\n- [contributing](#contributing)\n- [faq](#faq)\n\n# API\n\n### `new Apone(app, options)`\n  - `app` - `Express` instance\n  - `options` - `object` (optional)\n    - `prefix` - `string` prepends all routes without prefixs\n    - `extensions` - `array` of [extensions](#extensions)\n  - throws `AponeValidationError` for invalid options\n\n### `register(routes, state)`\n  - `routes` - `array|object`\n  - `state` - `object` (optional) if a route declares `handle(state)` this will be passed in (simple DI)\n  - throws `AponeValidationError` for invalid or duplicate routes\n\n# Routes\n\nBy defining the following properties, routes opt-into different behaviors. Each property name resolves to a function and each property value is an argument to that function. Custom properties, or [extensions](#extensions), can be specified in Apone's [constructor options](#api).\n\n## Properties\n  - `method` - `string` http verbs: GET, POST, PATCH, DELETE\n  - `path` - `string` endpoint at which `handle` will be invoked (no regex allowed)\n  - `handle` - `function|array` middleware OR function which returns middleware and accepting one potential argument. If an argument is specified, `state` (from the [register](#api) method) is passed in\n  - `validation` - `object` object with up to three properties, each a valid [schema](#schemas)\n    - `params` - `Schema`\n    - `query` - `Schema`\n    - `body` - `Schema`\n  - `prefix` - `string` path prefix which takes priority over Apone's [constructor option](#api) global prefix\n  - `metadata` - `object` - flexible bucket for anything else\n\n### example:\n\n```js\n  let route = {\n    method: 'PUT',\n    path: '/puppers/:id',\n    metadata: {\n      favorite: true\n    },\n    prefix: '/api/v2',\n    validation: {\n      params: Joi.object().keys({ id: Joi.number().integer().min(1).max(1).required() }),\n      body: {\n        validate: (query) =\u003e {\n\n          // can coerce a more complicated type here, and return valid payload only\n          if (query.name \u0026\u0026 typeof query.name === 'string') {\n            return { value: { name: query.name } }\n          }\n          return new { error: Error('name is required!') }\n        }\n      }\n    },\n    // injected dependency makes for easy testing\n    handle: (state) =\u003e [\n\n      // if you regularly need internalOnly, create an extension!\n      state.internalOnly,\n      (req, res, next) =\u003e {\n\n        res.send(state.pupperRepository.update(req.body))\n        return next()\n      }\n    ]\n  }\n```\n\n# Schemas\nSchemas are used to validate user requests. [Joi](https://github.com/hapijs/joi) is reccomended, but any schema can be used. Coerce and sanitize requests here.\n\n## Properties\n  - `validate` - `function` which must return a [ValidationResult](#validationresult)\n\n### example:\n```js\n  let schema = {\n    validate: (body) =\u003e {\n\n      if (body.id === 1) {\n        return { value: { id: body.id } }\n      }\n      return { error: new Error('value was not 1!') }\n    }\n  }\n```\n\n# ValidationResult\n\nReturned by [schema](#schemas) validation\n\n## Properties\n  - `error` - `Error` object\n  - `value` - `any` coerced and sanitized result\n\n### example:\n\n```js\n  let validationResult = {\n    error: isValid ? null : new Error(),\n    value: isValid ? value : null\n  }\n```\n\n# Extensions\n\nExtensions are custom [route properties](#routes). They must be defined in the Apone [constructor options](#api) and will execute in their array order, according to [request behavior](#route-behavior).\n\n## Properties\n  - `name` - `string` used by routes to opt into this extension behavior\n  - `factoryFunc` - `function` invoked by Apone during route registration, returning [middleware](#https://expressjs.com/en/guide/using-middleware.html) which is inserted into [request behavior](#route-behavior).\n  - `type` - `string` defaults to `pre`, or optionally `post`.\n\n\n### example:\nThe following extension adds a traceId to each request prior to validation. First, Apone is instantiated with the `trace` extension. During registration, `trace(true)` is called and the return function is added to the stack. Extensions execute in order, by type.\n\n```js\nconst Express = require('express')\nconst Apone = require('apone')\n\nlet app = Express()\nlet extensions = [{\n  name: 'trace',\n  factoryFunc: (doTag) =\u003e  (req, res, next) =\u003e {\n\n    if (doTag) {\n      res.locals.traceId = Math.random()\n      return next()\n    }\n    else {\n      next()\n    }\n  }\n}]\n\nlet apone = new Apone(app, { extensions })\n\napone.register({\n  path: '/hello'\n  method: 'get',\n  trace: true,\n  handle: (req, res, next) =\u003e {\n\n    console.log(res.locals.traceId) // 0.23456...\n    res.send('world')\n    return next()\n  }\n})\n```\n\n# Route Behavior\nThe lifecycle of middleware steps assembled by Apone for routes\n\n1. Apone appends the finished route object to `res.locals.route` for logging, etc\n2. `pre` [extensions](#Extensions)\n3. request `param` validation\n4. request `query` validation\n5. request `body` validation\n6. `handle` middleware\n7. `post` [extensions](#Extensions)\n\n# Contributing\nContributions are welcome.\n\n## Reporting Bugs\nPlease open an issue and describe the situation clearly. Include reproduction instructions, expected outcomes, and project version number.\n\n## Pull Requests\n - One feature per request\n - Descriptive commit messages\n - Test coverage is manditory\n - Pass project linter\n\n# FAQ\n1) If I use this with an existing application will the dupe check work\n  - Not currently\n\n2) Any plans to expand the framework?\n  - Apone was designed to be small and flexible, but feel free to open an issue\n\n3) What about express-router\n  - You can probably live without it. This is a simple alternative.\n\n4) How do I pronounce it\n - like in [Aliens](https://www.youtube.com/watch?v=woB1zvaSXag)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprotectwise%2Fapone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprotectwise%2Fapone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprotectwise%2Fapone/lists"}