{"id":16232368,"url":"https://github.com/lukasz-pluszczewski/simple-express","last_synced_at":"2026-02-13T13:29:32.371Z","repository":{"id":53826632,"uuid":"182383380","full_name":"Lukasz-pluszczewski/simple-express","owner":"Lukasz-pluszczewski","description":"Simple micro framework based on Express, allowing you to prototype APIs blazingly quickly","archived":false,"fork":false,"pushed_at":"2024-10-29T14:26:25.000Z","size":1360,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-29T17:27:33.177Z","etag":null,"topics":["express","framework","node","plugins-api","routes"],"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/Lukasz-pluszczewski.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-20T08:55:51.000Z","updated_at":"2024-10-29T14:21:33.000Z","dependencies_parsed_at":"2024-06-22T21:57:55.199Z","dependency_job_id":"6fe934d2-b3f2-4ecd-ac3d-dc286c63d215","html_url":"https://github.com/Lukasz-pluszczewski/simple-express","commit_stats":{"total_commits":29,"total_committers":3,"mean_commits":9.666666666666666,"dds":0.2068965517241379,"last_synced_commit":"c2c56acbd2fc5502fb7ddd97c9576b9977018234"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukasz-pluszczewski%2Fsimple-express","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukasz-pluszczewski%2Fsimple-express/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukasz-pluszczewski%2Fsimple-express/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lukasz-pluszczewski%2Fsimple-express/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lukasz-pluszczewski","download_url":"https://codeload.github.com/Lukasz-pluszczewski/simple-express/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243997107,"owners_count":20380980,"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","framework","node","plugins-api","routes"],"created_at":"2024-10-10T13:08:49.246Z","updated_at":"2026-02-13T13:29:32.358Z","avatar_url":"https://github.com/Lukasz-pluszczewski.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simple Express framework\n\u003e Simple micro framework based on Express, allowing you to prototype APIs blazingly quickly\n\n![Express Logo](logo.png)\n\nMicro-framework that let's you create more readable route structure, use simple async functions as route handlers, with clear error handling and run fully functional express app in seconds.\n\n## Getting started\n### Requirements\n- Modern version of Node (\u003e18 recommended but will probably work on older versions too)\n- Express (ver 5 recommended but ver 4 is still supported)\n- (optional) body-parser, cookie-parser, cors, helmet\n\n### Install the library\n`npm i simple-express-framework`\n\n### Run the hello-world app\n```js\nimport simpleExpress from 'simple-express-framework';\n\nsimpleExpress({\n  port: 8080,\n  routes: [\n    ['/hello', {\n      get: () =\u003e ({ status: 200, body: 'Hello world' }),\n    }],\n  ],\n});\n```\n\nAnd that's all! Express server, listening on selected port, with [reasonable default settings](#config) is up and running in seconds!\n\nBut there's more! Dive in in the [Examples](#more-usage-examples) section to see the power of [simple and readable route handlers](#examples-of-handlers), [clear error handling](#error-Handlers) and more - everything just works and is nearly infinitely expandable thanks to [plugin](#plugins) support!\n\n## Table of contents\n\n* [Getting started](#getting-started)\n* [Table of contents](#table-of-contents)\n* [Usage](#usage)\n    * [simpleExpress function](#simpleexpress-function)\n    * [simpleExpress config](#simpleexpress-config)\n    * [Handlers](#handlers)\n        * [Response objects](#response-objects)\n        * [Returning error](#returning-error)\n        * [Examples of handlers:](#examples-of-handlers)\n        * [Multiple handlers (middlewares)](#multiple-handlers-middlewares)\n    * [Error Handlers](#error-handlers)\n        * [handleError helper](#handleerror-helper)\n    * [Routes](#routes)\n        * [Array of tuples (recommended)](#array-of-tuples-recommended)\n        * [Array of objects](#array-of-objects)\n        * [Object of objects](#object-of-objects)\n        * [Reserved object keys](#reserved-object-keys)\n    * [Config](#config)\n    * [Global Middlewares](#global-middlewares)\n        * [Context](#context)\n            * [Accessing contexts](#accessing-contexts)\n            * [Context object](#context-object)\n            * [globalContext examples](#globalcontext-examples)\n            * [requestContext examples](#requestcontext-examples)\n* [Plugins](#plugins)\n* [Typescript](#typescript)\n    * [Type parameters](#type-parameters)\n    * [mapResponse plugin](#mapresponse-plugin)\n    * [getHandlerParams and getErrorHandlerParams plugins](#gethandlerparams-and-geterrorhandlerparams-plugins)\n* [More usage examples](#more-usage-examples)\n    * [Hello world](#hello-world)\n    * [Simple users CRUD](#simple-users-crud)\n    * [Adding authentication](#adding-authentication)\n    * [Adding authentication to one route only](#adding-authentication-to-one-route-only)\n    * [Error handling](#error-handling)\n    * [Disabling default middlewares](#disabling-default-middlewares)\n    * [Applying express middlewares](#applying-express-middlewares)\n    * [Sending response manually](#sending-response-manually)\n    * [Request validation](#request-validation)\n    * [Logging](#logging)\n    * [Testing the app](#testing-the-app)\n* [Development](#development)\n* [Changelog](#changelog)\n\n## Usage\n\n### simpleExpress function\nYou run the app by executing simpleExpress function. All options are optional.\n\n```js\nimport simpleExpress from 'simple-express-framework';\n\nsimpleExpress({\n  port: 8080,\n  routes,\n  expressMiddleware,\n  middleware,\n  errorHandlers,\n  config,\n  routeParams,\n  app,\n  server,\n})\n  .then(({ app, server }) =\u003e console.log('App started :)'))\n  .catch(error =\u003e console.error('App starting failed :(', error));\n```\n\n### simpleExpress config\nSimple express accepts the following options:\n- **port**: *number* Port for the app to listen on. Not required, you can run the app without a port (useful for testing)\n- **routes**: *object|array* See [Routes](#routes)\n- **expressMiddleware**: *array* Array of express middlewares (functions accepting req, res, and next as arguments)\n- **middleware**: *array* Array of simpleExpress middlewares (see [Handlers](#handlers))\n- **errorHandlers**: *array* Array of simpleExpress error middlewares (see [Error Handlers](#error-handlers))\n- **config**: *object* See [Config](#config)\n- **routeParams**: *object* Object of additional parameters passed to handlers\n- **app**: **object** Custom app to be used (by default new express app is created)\n- **server**: **object** Custom http server to be used (by default new http server is created)\n- **globalContext**: **object** Global context object with values that are going to be available in all handlers\n- **requestContext**: **function** Function returning request context object with values that are going to be available in all handlers. It receives all the fields that handlers receive except, obviously, both context objects.\n\nResolves to an object with the following fields:\n- **app**: *object* Express app\n- **server**: *object* Http server\n- **stats**: *object* SimpleExpress stats object\n- **port**: *number* Port if the app is listening or undefined otherwise\n- **address**: *object|string|null* [Server address](https://nodejs.org/api/net.html#serveraddress)\n\n### Handlers\nSimpleExpress handlers are similar to express handlers except they accept one argument: object with the following fields:\n- **body**: *any* Request's body (by default, the json parser is enabled)\n- **query**: *object* Request's query parameters\n- **params**: *object* Route params\n- **method**: *string* Request's method\n- **originalUrl**: *string* Request's original url\n- **protocol**: *string* Request's protocol\n- **xhr**: *boolean* Flag indicating the request is XHR request\n- **get**: *function* Function returning the request header\n- **getHeader**: *function* Alias for get()\n- **locals**: *object* [res.locals](https://expressjs.com/en/api.html#res.locals) object for storing data for current request\n- **next**: *function* Express' next function, triggers next middleware\n- **req**: *object* Express' req object\n- **res**: *object* Express' res object\n- **globalContext**: *object* Global context object with values, provided in simpleExpress config\n- **requestContext**: *object* Request context object with values, created by *requestContext* function provided in simpleExpress config\n\n#### Response objects\nHandlers should return falsy value (to not send any response) or the response object:\n- **status**: *number* (default: 200) Response http status code\n- **body**: *any* Response body (By default weill be sent using [res.send() method](https://expressjs.com/en/api.html#res.send). Can be changed with method field)\n- **headers**: *object* Response headers\n- **redirect**: *string* Url to redirect to\n- **type**: *string* Passes the value to `res.type()` express method which sets the Content-Type HTTP header to the value received if it contains '/' (e.g. `application/json`), or determines the mime type by [mime.lookup\\(\\)](https://github.com/broofa/node-mime#mimelookuppath)\n- **method**: *string* Response method, one of the following values:\n  - *json* - the response will be sent using `res.json` method\n  - *send* - the response will be sent using `res.send` method\n  - *default* - alias for send\n  - *none* - the response will not be sent (useful if you want to send a response manually), the same effect can be achieved by returning falsy value from handler.\n\n#### Returning error\nTo pass an error to error handlers, handler can either pass an error to next() function:\n```js\n({ next }) =\u003e {\n  next(new Error('Something went wrong'));\n};\n```\n\nOr just return an Error object (or instance of a class extending Error)\n```js\n() =\u003e {\n  return new Error('Something went wrong');\n};\n```\n\nOr throw an Error (or instance of a class extending Error)\n```js\n() =\u003e {\n  throw new Error('Something went wrong');\n};\n```\n\n#### Examples of handlers:\n```js\n({ req, res, params, body, query, originalUrl, protocol, locals, xhr, getHeader, next }) =\u003e {\n  ...\n  return {\n    status: 500,\n    body: { message: 'Server error' },\n    headers: { customHeader: 'customHeaderValue' },\n  };\n};\n```\n\n```js\n({ params, body }) =\u003e {\n  ...\n  return {\n    status: 301,\n    redirect: '/test',\n  };\n};\n```\n\n```js\n({ params, body }) =\u003e {\n  ...\n  return {\n    body: Buffer.from('LoremIpsum')\n  };\n};\n```\n\n```js\n({ res, next }) =\u003e {\n  ...\n  res.sendFile('path/to/file', error =\u003e {\n    if (error) {\n      next(error);\n    }\n  });\n};\n```\n\n#### Multiple handlers (middlewares)\nAll handlers can work as middlewares if they trigger next() instead of returning a response. You can pass an array of handlers. They will be executed in order just like express middlewares.\n\n```js\n{\n  get: [\n    ({ getHeader, locals, next }) =\u003e {\n      const user = verifyToken(getHeader('authentication'))\n      if (user) {\n        locals.user = user;\n        return next();\n      }\n\n      // the same as \"next(new AuthenticationError('Unauthenticated'))\"\n      return new AuthenticationError('Unauthenticated');\n    },\n    ({ locals }) =\u003e ({\n      body: 'You are logged in as ' + locals.user.username,\n    }),\n  ]\n}\n```\n\nMiddlewares can be chained in different ways:\n```js\n[\n  [\n    '/foo',\n    ({ getHeader, locals, next }) =\u003e {\n      const user = verifyToken(getHeader('authentication'))\n      if (user) {\n        locals.user = user;\n        return next();\n      }\n\n      // the same as \"next(new AuthenticationError('Unauthenticated'))\"\n      return new AuthenticationError('Unauthenticated');\n    },\n    {\n      get: ({ locals }) =\u003e ({\n        body: 'You are logged in as ' + locals.user.username,\n      }),\n    }\n  ]\n]\n```\n\n```js\n[\n  '/foo',\n  [\n    ({ getHeader, locals, next }) =\u003e {\n      const user = verifyToken(getHeader('authentication'))\n      if (user) {\n        locals.user = user;\n        return next();\n      }\n\n      // the same as \"next(new AuthenticationError('Unauthenticated'))\"\n      return new AuthenticationError('Unauthenticated');\n    },\n    {\n      get: ({ locals }) =\u003e ({\n        body: 'You are logged in as ' + locals.user.username,\n      }),\n    }\n  ]\n]\n```\n\n### Error Handlers\nAll errors are being caught by the simpleExpress and passed to error handlers. Error handlers are identical to handlers, except they receive error as first argument, and object of handler parameters as second. To pass an error to error handlers you can trigger `next()` with an error as an argument, throw an error, or return an error in a handler.\n\n*Please note, that handler parameters object is exactly the same as in case of handlers, except 'params' field which is for whatever reason stripped by express.*\n\n```js\nroutes: [\n  ['/foo', {\n    get: () =\u003e {\n      throw new AuthenticationError();\n    }\n  }],\n  ['/bar', {\n    get: () =\u003e new AuthenticationError(),\n  }],\n  ['/baz', {\n    get: ({ next }) =\u003e next(new AuthenticationError()),\n  }]\n]\n\nerrorHandlers: [\n  (error, { next }) =\u003e {\n    if (error instanceOf AuthenticationError) {\n      return {\n        status: 401,\n        body: 'Unauthorized',\n      };\n    }\n\n    return error;\n  },\n  (error) =\u003e {\n    return {\n      status: 500,\n      body: 'Ups :('\n    };\n  }\n]\n```\n\n#### handleError helper\nTo make it easier for you to handle different types of errors, simpleExpress provides you with handleError helper:\n```js\nimport { handleError } from 'simple-express-framework';\n\n//...\n\nerrorHandlers: [\n  handleError(\n    AuthenticationError,\n    (error, { query, body, params, ... }) =\u003e ({\n      status: 401,\n      body: 'Unauthorized',\n    })\n  ),\n  (error) =\u003e ({\n    status: 500,\n    body: 'Ups :('\n  }),\n]\n```\n\nYou can also pass an array of error - errorHandler pairs to handleError helper function\n```js\nimport { handleError } from 'simple-express-framework';\n\n//...\n\nerrorHandlers: [\n  handleError([\n    [AuthenticationError, (error, { query, body, params, ... }) =\u003e ({\n      status: 401,\n      body: 'Unauthorized',\n    })],\n    [\n      (error) =\u003e ({\n        status: 500,\n        body: 'Ups :('\n      }),\n    ]\n  ])\n]\n```\n\n```js\nimport { handleError } from 'simple-express-framework';\n\n//...\n\nerrorHandlers: handleError([\n  [AuthenticationError, (error, { query, body, params, ... }) =\u003e ({\n    status: 401,\n    body: 'Unauthorized',\n  })],\n  (error) =\u003e ({\n    status: 500,\n    body: 'Ups :('\n  }),\n])\n```\n\n\n### Routes\nThe simpleExpress supports different formats of routes (in first two formats, paths can be either strings or regular expressions):\n\n#### Array of tuples (recommended)\n```js\nsimpleExpress({\n  routes: [\n    ['/foo', {\n      get: [authenticate, () =\u003e ({ body: 'some data' })]\n      post: [authenticate, () =\u003e ({ status: 201 })]\n    }],\n    ['/bar/:baz', {\n      get: ({ params }) =\u003e ({ body: `Got ${params.baz}` })\n    }]\n  ]\n});\n```\n\n#### Array of objects\n```js\n\nsimpleExpress({\n  routes: [\n    {\n      path: '/foo',\n      handlers: {\n        get: [authenticate, () =\u003e ({ body: 'some data' })],\n        post: [authenticate, () =\u003e ({ status: 201 })],\n      },\n    },\n    {\n      path: '/bar/:baz',\n      handlers: {\n        get: ({ params }) =\u003e ({ body: `Got ${params.baz}` }),\n      },\n    },\n  ],\n});\n```\n\n#### Object of objects\n**Warning:** *Object keys' order is preserved only for string and symbols keys, not for integers (integers, including in strings like \"1\", will always be before all other keys)!*\n```js\n\nsimpleExpress({\n  routes: {\n    '/foo': {\n      get: [authenticate, () =\u003e ({ body: 'some data' })]\n      post: [authenticate, () =\u003e ({ status: 201 })]\n    },\n    '/bar/:baz': {\n      get: ({ params }) =\u003e ({ body: `Got ${params.baz}` }),\n    },\n  },\n});\n```\n\n#### Reserved object keys\nBecause object keys can be used as route paths but also as method names (like get, post etc.) or as names like path, handlers and routes here is the list of reserved key names that can't be used as route paths when you use \"Object of objects\" format:\n\n*Please note that all of those can be used with slash at the beginning like `/path` or `/post`. Only exactly listed strings are reserved.*\n- path\n- handlers\n- routes\n- use\n- get\n- post\n- put\n- delete\n- del\n- options\n- patch\n- head\n- checkout\n- copy\n- lock\n- merge\n- mkactivity\n- mkcol\n- move\n- m-search\n- notify\n- purge\n- report\n- search\n- subscribe\n- trace\n- unlock\n- unsubscribe\n\nIf you need to register a route with one of these strings as path, you can use one of the other route formats.\n\n### Config\nBy default, JSON body parser, cors, cookie parser and helmet middlewares are configured. You can change their configuration or disable them.\n`config` object consist of the following fields:\n- **cors**: *object|false* cors config. If set to `false`, the cors middleware will not by applied.\n- **jsonBodyParser**: *object|false* JSON body parser config. If set to `false` the body parser middleware will not be applied.\n- **cookieParser**: *[secret, options]|false* Arguments for cookie-parser middleware. If set to `false` the cookie parser middleware will not be applied.\n- **helmet**: *object|false* Helmet config. If set to `false` the helmet middleware will not be applied.\n\n*Note: Default middlewares are applied only if corresponding libraries are installed. body-parser is an express dependency so it will be installed with it.*\n\n### Global Middlewares\nGlobal middlewares can be added in `middleware` field. It is array of handlers (each handlers looks exactly like route handlers, with the same parameters).\n```js\nsimpleExpress({\n  port: 8080,\n  middleware,\n  ...\n})\n```\n\nIf you need to use express middlewares directly, you can pass them to expressMiddleware array.\n\n### Context\nSimpleExpress allows you to pass two types of contexts to handlers: global and request. Both use asyncLocalStorage under the hood, meaning that most of the time you can access them in any place in your code without needed to pass them as parameters.\n\n**Global context** is available in all handlers if you set the globalContext option in simpleExpress config. It has the same lifetime as the app. The object you provided in globalContext, is shallowly cloned once, and that one object is available (and mutable) in all handlers.\n\n**Request context** is generated for each request and has the same lifetime as the request. requestContext option is a function, that receives all the fields that handlers receive except, obviously, both context objects and should return a plain JS object.\n\n#### Accessing contexts\nBoth globalContext and requestContext can be accessed via\n\n1. Route parameter (globalContext and requestContext respectively)\n2. getGlobalContext and getRequestContext helpers (exported from the simple-express-framework package) can be used to access respective contexts anywhere in the code, as long as the call tree includes the handler.\n**!Note:** getGlobalContext and getRequestContext functions will work only if you have only one simpleExpress app running in one Node process. Use other methods if you need to run multiple apps.\n3. getGlobalContext and getRequestContext functions are also included in the returned object from simpleExpress function call. You can save them to some global object and make them available to your handlers or services. **This is strongly discouraged, as it will make your code hard to test and maintain. Use other methods if possible.**\n\n#### Context object\nEach context object has the following shape:\n- **get**: *function* Function returning the value of the context object for the provided key\n- **set**: *function* Function setting the value of the context object for the provided key by mutating the context object\n- **nativeLocalStorage**: *object* native asyncLocalStorage object https://nodejs.org/docs/latest-v22.x/api/async_context.html#class-asynclocalstorage\n- **run**: *function* Function running the provided function in the context object's context (just like asyncLocalStorage.run, but without the need to provide the value)\n\n\n#### globalContext examples\n**Simple example**\n```js\nsimpleExpress({\n  port: 8080,\n  globalContext: {\n    foo: 'bar',\n  },\n  routes: [\n    ['/', {\n      get: ({ globalContext }) =\u003e {\n        return {\n          body: {\n            foo: globalContext.get('foo'), // bar\n          },\n        };\n      },\n    }],\n  ],\n})\n```\n\n\n**Advanced example**\n```js\nimport { simpleExpress, getGlobalContext } from 'simple-express-framework';\n\nconst handler = async () =\u003e {\n  const context = getGlobalContext();\n  const foo = context.get('foo');\n  context.set('foo', 'bam');\n  return foo;\n}\n\nlet getGlobalContext;\n\nsimpleExpress({\n  port: 8080,\n  globalContext: {\n    foo: 'bar',\n  },\n  routes: [\n    ['/bar', {\n      get: ({ globalContext }) =\u003e {\n        const foo = globalContext.get('foo');\n        globalContext.set('foo', 'baz');\n        return {\n          body: {\n            foo, // bar\n          },\n        };\n      },\n    }],\n    ['/baz', async () =\u003e {\n      const foo = await handler();\n      return {\n        body: {\n          foo, // baz\n        },\n      };\n    }],\n    ['/bam', async () =\u003e ({\n      body: {\n        foo: getGlobalContext().get('foo'), // bam\n      }\n    })],\n  ],\n}).then(({ port, getGlobalContext: globalContext }) =\u003e {\n  getGlobalContext = globalContext;\n});\n```\n\n#### requestContext examples\n**Simple example**\n```js\nsimpleExpress({\n  port: 8080,\n  requestContext: ({ req }) =\u003e ({\n    foo: 'bar',\n  }),\n  routes: [\n    ['/', {\n      get: () =\u003e ({\n        body: {\n          foo: getRequestContext().get('foo'),\n        },\n      }),\n    }],\n  ],\n})\n```\n\n**Advanced example**\n```js\nimport { simpleExpress, getRequestContext } from 'simple-express-framework';\n\nconst handler = async () =\u003e {\n  const context = getRequestContext();\n  const foo = context.get('foo');\n  context.set('foo', 'bam');\n  return foo;\n}\n\nlet getRequestContext;\n\nsimpleExpress({\n  port: 8080,\n  requestContext: ({ req }) =\u003e ({\n    foo: 'bar',\n  }),\n  routes: [\n    ['/bar', {\n      get: ({ requestContext }) =\u003e {\n        const foo = requestContext.get('foo');\n        requestContext.set('foo', 'baz');\n        return {\n          body: {\n            foo, // bar\n          },\n        };\n      },\n    }],\n    ['/baz', async () =\u003e {\n      const foo = await handler();\n      return {\n        body: {\n          foo, // baz\n        },\n      };\n    }],\n    ['/bam', async () =\u003e ({\n      body: {\n        foo: getRequestContext().get('foo'), // bam\n      }\n    })],\n  ],\n}).then(({ port, getRequestContext: requestContext }) =\u003e {\n  getRequestContext = requestContext;\n});\n```\n\n## Plugins\nYou can modify the behaviour of simpleExpress with plugins. Each plugin is a factory, that receives simpleExpress config (all parameters passed to simpleExpress function) as parameter, and returns plugin object.\n\nPlugin object is an object of one or more of the following functions:\n- getHandlerParams\n- getErrorHandlerParams\n- mapResponse\n\nPlugins' functions are triggered in the order they appear in the plugins array. In case of mapResponse, not all plugins are necessarily triggered (see details below).\n\nHere is an example of plugin that adds cookies parsed by cookie-parser to handler params.\n```js\nconst cookiesPlugin = (simpleExpressConfig) =\u003e ({\n  getHandlerParams: params =\u003e ({\n    ...params,\n    cookies: params.req.cookies,\n  }),\n  getErrorHandlerParams: params =\u003e ({\n    ...params,\n    cookies: params.req.cookies,\n  }),\n});\n\nsimpleExpress({\n  port: 8080,\n  plugins: [\n    cookiesPlugin,\n  ],\n  ...\n});\n```\n\n\nAnd yet another plugin, this time, allowing you to serve file easily:\n```js\nconst serveFilePlugin = simpleExpressConfig =\u003e ({\n  mapResponse: (responseObject, { res }) =\u003e {\n    if (responseObject.file) {\n      res.sendFile(responseObject.file, err =\u003e {\n        if (err) {\n          return next(err)\n        }\n      })\n      return null;\n    }\n\n    return responseObject;\n  },\n});\n\nsimpleExpress({\n  port: 8080,\n  plugins: [\n    serveFilePlugin,\n  ],\n  ...\n});\n```\n\n### getHandlerParams, getErrorHandlerParams\nThese functions are triggered for all plugins, in order. Each plugin gets what the previous returned (default handler params are passed to the first plugin in chain)\n**Arguments**\n- **handlerParams**: *object* [Handler params](#handlers) returned by the previous plugin\n\n**Returns**\n- **handlerParams**: *object* [Handler params](#handlers) passed to the next plugin\n\n### mapResponse\nEach plugin gets what previous returned (response object returned from handler is passed to the first plugin in chain). Chain can be broken if a plugin returns null or `{ type: 'none' }` object. Also, no plugins are triggered, if handler returned null or `{ type: 'none' }`. That way we prevent sending the response twice by two different plugins.\n\n**Arguments**\n- **responseObject**: *object* [Response object](#response-objects) returned by the previous plugin\n\n**Returns**\n- **responseObject**: *object* [Response object](#response-objects) passed to the next plugin\n\n## Typescript\nSimpleExpress is written in typescript and most of the code is fully typed. One exception is the plugin system which does not correctly infer the types resulting in applying the plugins. Below are examples of how to handle that until the issue is fixed.\n\n### Type parameters\nWhile most types are going to be correctly inferred, you can also pass the type parameters for additional route params and res.locals object.\n```ts\nimport simpleExpress, { Routes } from 'simple-express-framework';\n\ntype AdditionalRouteParams = { foo: string };\ntype Locals = { bar: string };\n\nexport const router: Routes\u003cAdditionalRouteParams, Locals\u003e = [\n  ['/', {\n    get: [\n      ({ foo, locals }) =\u003e ({ body: `${foo} ${locals.bar}` }),\n    ]\n  }],\n];\n\n// when passing routes with correct types, you can omit the generics in simpleExpress function\nsimpleExpress({\n  routes: router,\n});\n\n// but you can also pass them explicitly\nsimpleExpress\u003cAdditionalRouteParams, Locals\u003e({\n  routes: router,\n});\n```\n\n### mapResponse plugin\nThis is the most difficult, and for now, the only way is to just use type assertion:\n```ts\nimport { ResponseDefinition, Routes } from 'simple-express-framework';\n\nconst mapResponse = response =\u003e ({\n  ...response,\n  body: response.alternativeBody,\n});\nconst plugin = () =\u003e ({ mapResponse });\n\nconst routes: Routes = [\n  ['/', {\n    get: [\n      () =\u003e ({ alternativeBody: 'works' } as ResponseDefinition),\n    ]\n  }],\n];\n\nconst { app } = await simpleExpress({ routes, plugins: [ plugin ] });\n```\n\n### getHandlerParams and getErrorHandlerParams plugins\nIf you just add new parameters to handlers then you can use type parameter in route like this:\n```ts\nconst getHandlerParams = routeParams =\u003e ({\n  ...routeParams,\n  additionalParam: 'works',\n});\nconst plugin = () =\u003e ({ getHandlerParams });\n\nconst routes: Routes\u003c{ additionalParam: string }\u003e = [\n  ['/', {\n    get: [\n      ({ additionalParam }) =\u003e ({ body: additionalParam }),\n    ]\n  }],\n];\n\nconst { app } = await simpleExpress({ routes, plugins: [ plugin ] });\n```\n\nIn case you remove some parameters from handlers, unfortunately, your routes will not be type-safe:\n```ts\nconst getHandlerParams = routeParams =\u003e ({\n  theOnlyParam: 'works',\n});\nconst plugin = () =\u003e ({ getHandlerParams });\n\nconst routes: Routes\u003c{ theOnlyParam: string }\u003e = [\n  ['/', {\n    get: [\n      // typescript allows you to use `req` param here although it won't be present at runtime \n      ({ req, theOnlyParam }) =\u003e ({ body: theOnlyParam }),\n    ]\n  }],\n];\n\nconst { app } = await simpleExpress({ routes, plugins: [ plugin ] });\n```\n\n## More usage examples\n### Hello world\n```js\nimport simpleExpress from 'simple-express-framework';\n\nsimpleExpress({\n  port: 8080,\n  routes: [\n    ['/', {\n      get: () =\u003e ({ body: 'Hello world!' }),\n    }],\n  ],\n})\n```\n\n### Simple users CRUD\n```js\nimport simpleExpress from 'simple-express-framework';\n\nimport connectDb from './connectDb';\nimport createUsersRepository from './usersRepository';\n\nconst db = connectDb();\nconst usersRepository = createUsersRepository(db);\n\nsimpleExpress({\n  port: 8080,\n  routeParams: { users: usersRepository },\n  routes: [\n    ['/users', {\n      get: async ({ query: { search }, users }) =\u003e {\n        const allUsers = await users.getAll(search);\n\n        return {\n          body: allUsers,\n        };\n      },\n      post: async ({ body, users }) =\u003e {\n        const results = await users.create(body);\n\n        return {\n          status: 201,\n          body: results,\n        };\n    }],\n    ['users/:id', {\n      get: async ({ params: { id }, users }) =\u003e {\n        const user = await users.getById(id);\n\n        if (user) {\n          return {\n            body: user,\n          };\n        }\n\n        return {\n          status: 404,\n          body: 'User not found',\n        };\n      },\n      put: async ({ params: { id }, body, users }) =\u003e {\n        const { id } = params;\n        const result = await users.updateById(id, body);\n\n        if (result) {\n          return {\n            status: 204,\n          };\n        }\n\n        return {\n          status: 404,\n          body: 'User not found',\n        };\n      },\n    }],\n  ],\n})\n```\n\n### Adding authentication\n```js\nimport simpleExpress from 'simple-express-framework';\nimport verifyToken from 'verifyToken';\n\nsimpleExpress({\n  port: 8080,\n  middleware: [\n    ({ get, next }) =\u003e {\n      if (verifyToken(get('authentication'))) {\n        return next();\n      }\n\n      return {\n        status: 401,\n        body: 'Unauthorized',\n      };\n    },\n  ],\n  routes: [\n    ...\n  ],\n})\n```\n\n### Adding authentication to one route only\n```js\nimport simpleExpress from 'simple-express-framework';\nimport users from 'users';\nimport verifyToken from 'verifyToken';\n\nsimpleExpress({\n  port: 8080,\n  routes: [\n    ...\n    ['/users', {\n      get: [\n        ({ get, next }) =\u003e {\n          if (verifyToken(get('authentication'))) {\n            return next();\n          }\n\n          return {\n            status: 401,\n            body: 'Unauthorized',\n          };\n        },\n        async ({ query }) =\u003e {\n          const { search } = query;\n          const allUsers = await users.getAll(search);\n\n          return {\n            body: allUsers,\n          };\n        },\n      ]\n    }],\n    ...\n  ],\n});\n```\n\n### Error handling\n```js\nimport simpleExpress from 'simple-express-framework';\nimport users from 'users';\nimport NotFoundError from 'errors/NotFoundError';\n\nsimpleExpress({\n  port: 8080,\n  routes: [\n    ...\n    ['/users/:id', {\n      get: async ({ params }) =\u003e {\n        const { id } = params;\n        const user = await users.getById(id);\n\n        if (user) {\n          return {\n            body: user,\n          };\n        }\n\n        return new NotFoundError('User not found');\n      },\n    }],\n    ...\n  ],\n  errorHandlers: [\n    (error, { next }) =\u003e {\n      if (error instanceof NotFoundError) {\n        return {\n          status: 404,\n          body: error.message,\n        };\n      }\n\n      return error;\n    },\n    (error) =\u003e (){\n      status: 500,\n      body: error.message || 'Unknown error',\n    }),\n  ],\n});\n```\n\nYou can pass the error to `next` callback, return it or throw. In each case, error handlers will get the error.\n```js\nconst handler = ({ next }) =\u003e {\n  next(new Error('Error'));\n};\n\nconst handler = () =\u003e {\n  return new Error('Error');\n};\n\nconst handler = () =\u003e {\n  throw new Error('Error');\n};\n```\n\n### Disabling default middlewares\n```js\nimport simpleExpress from 'simple-express-framework';\n\nsimpleExpress({\n  port: 8080,\n  config: {\n    jsonBodyParser: false,\n    cors: false,\n    cookieParser: false,\n  },\n  routes: [\n    ...\n  ],\n});\n```\n\n### Applying express middlewares\nCors, JSON body parser and cookie parser are configured by default\n```js\nimport simpleExpress from 'simple-express-framework';\nimport morgan from 'morgan';\n\nsimpleExpress({\n  port: 8080,\n  expressMiddleware: [\n    morgan('combined'),\n  ],\n  routes: [\n    ...\n  ],\n});\n```\n\n### Sending response manually\n```js\nimport simpleExpress from 'simple-express-framework';\n\nsimpleExpress({\n  port: 8080,\n  routes: [\n    ['/foo', {\n      get: ({ res }) =\u003e {\n        res.write('\u003cdiv\u003eHello foo\u003c/div\u003e');\n        res.end();\n      },\n    }],\n    ['/bar', {\n      get: ({ res }) =\u003e {\n        res.write('\u003cdiv\u003eHello baz\u003c/div\u003e');\n        res.end();\n\n        return null;\n      },\n    }],\n    ['/baz', {\n      get: ({ res }) =\u003e {\n        res.write('\u003cdiv\u003eHello baz\u003c/div\u003e');\n        res.end();\n\n        return {\n          format: none,\n        };\n      },\n    }],\n  ],\n})\n```\n\n### Request validation\n#### Express-validator\n```js\nimport simpleExpress, { wrapMiddleware } from 'simple-express';\nconst { check, validationResult } = require('express-validator');\n\nconst { app } = await simpleExpress({\n  port: 8080,\n  routes: [\n    ['/user', {\n      post: [\n        wrapMiddleware([\n          check('username').isEmail(),\n          check('password').isLength({ min: 5 })\n        ]),\n        ({ req, next }) =\u003e {\n          const errors = validationResult(req);\n          if (!errors.isEmpty()) {\n            return res.status(400).json({ errors: errors.array() });\n          }\n\n          next();\n        },\n        () =\u003e ({\n          body: 'works'\n        })\n      ],\n    }],\n  ],\n});\n```\n\n### Logging\nThis library uses [debug](https://github.com/visionmedia/debug) logging utility.\n\nTo enable all logs set the following environment variable:\n```bash\nDEBUG=simpleExpress,simpleExpress:*\n```\n\nYou can enable only some logs:\n- `DEBUG=simpleExpress`: General logs (like \"App started on port\", or \"ERROR: port already in use\")\n- `DEBUG=simpleExpress:request`: Log all requests with response time\n- `DEBUG=simpleExpress:stats`: Simple express statistics, like registered routes, middlewares etc.\n- `DEBUG=simpleExpress:warning`: Unimplemented features, deprecations and other warnings\n\n### Testing the app\nSee the demo app for tests examples.\n\n## Development\n- Clone the repository\n- `npm i`\n\n**Running tests**\n`npm run test`\n\n**Starting demo app**\n`npm run demo`\n\n## Changelog\n### 3.2.0\n- Updated error types\n\n### 3.1.2\n- Added simpleExpress named export \n\n### 3.1.1\n- Fixed incorrect Express app typings\n\n### 3.1.0\n- Added support for asyncLocalStorage: global and for current request\n- Added getRequestContext and getGlobalContext helpers\n\n### 3.0.0\n- Moved express to peerDependencies and included support for express 5\n- Complete rewrite - now the whole codebase is written in typescript\n- Rewritten build process - now using tsup\n- Types improvements\n- Added helmet\n\n### 2.4.1\n- Improved typescript typings (added Promise\u003cvoid\u003e as possible return from handler)\n\n### 2.4.0\n- Added typescript typings\n- Updated dependencies\n\n### 2.3.0\n- Added plugins support\n- Added support for getHandlerParams plugin method\n- Added support for getErrorHandlerParams plugin method\n- Added support for mapResponse plugin method\n\n### 2.2.0\n- Fixed applying custom config for default middlewares\n\n### 2.1.0\n- Added more features to handleError helper (handling unknown error, passing more error - errorHandler pairs)\n- Added more tests for handleError helper\n- Added table of contents in readme\n- Some minor improvements in readme\n- Small fixes\n\n### 2.0.4\n- Added logo\n\n### 2.0.3\n- Fixed issue with `use` method not being supported\n- Updated tests\n\n### 2.0.2\n- DEPRECATION: `simpleExpressMiddlewares` and `middlewares` options are deprecated, use `middleware` instead\n- DEPRECATION: `expressMiddlewares` option is deprecated, use `expressMiddleware` instead\n- All middlewares can now be nested\n- Added more tests\n- Improved readme\n\n### 2.0.1\n- improved stats log\n- updated tests\n\n### 2.0.0\n- Added support for nested routes, in many shapes\n- Updated tests to cover different nested routes schemas\n- Added `type` response field\n- Changed default response method to `send`\n- Updated readme\n- Update demo app - now it's complete application with tests\n\n### 1.0.3\n- Exposed res.locals to route handlers and error handlers as \"locals\"\n- Added more tests for error handlers\n\n### 1.0.2\n- Minor fixes\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasz-pluszczewski%2Fsimple-express","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukasz-pluszczewski%2Fsimple-express","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasz-pluszczewski%2Fsimple-express/lists"}