{"id":20482170,"url":"https://github.com/leaonline/http-factory","last_synced_at":"2026-06-09T14:31:54.228Z","repository":{"id":113209134,"uuid":"260935522","full_name":"leaonline/http-factory","owner":"leaonline","description":"Create Meteor connect HTTP middleware. Lightweight. Simple.","archived":false,"fork":false,"pushed_at":"2026-02-10T14:52:22.000Z","size":46,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-02-10T18:57:06.363Z","etag":null,"topics":["factory","hacktoberfest","http","routes","webapp"],"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/leaonline.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":"2020-05-03T14:02:19.000Z","updated_at":"2024-06-25T01:24:57.000Z","dependencies_parsed_at":null,"dependency_job_id":"78251d92-95d9-4dae-ba8c-462ca479aa7c","html_url":"https://github.com/leaonline/http-factory","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/leaonline/http-factory","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fhttp-factory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fhttp-factory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fhttp-factory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fhttp-factory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leaonline","download_url":"https://codeload.github.com/leaonline/http-factory/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leaonline%2Fhttp-factory/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34112225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["factory","hacktoberfest","http","routes","webapp"],"created_at":"2024-11-15T16:11:52.294Z","updated_at":"2026-06-09T14:31:54.220Z","avatar_url":"https://github.com/leaonline.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Meteor HTTP Factory\n\n[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)\n[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)\n![GitHub file size in bytes](https://img.shields.io/github/size/leaonline/http-factory/http-factory.js)\n![GitHub](https://img.shields.io/github/license/leaonline/http-factory)\n\nCreate Meteor `WebApp` (express) HTTP middleware. Lightweight. Simple.\n\nWith this package you can define factory functions to create a variety of Meteor HTTP routes.\nDecouples definition from instantiation (also for the schema) and allows different configurations for different\ntypes of HTTP routes.\n\n**Minified size \u003c 2KB!**\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Why do I want this?](#why-do-i-want-this)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Basic example](#basic-example)\n  - [Use `WebApp.rawConnectHandlers`](#use-webapprawconnecthandlers)\n  - [Create universal handlers](#create-universal-handlers)\n  - [Specify a method](#specify-a-method)\n  - [Passing data to the next handler](#passing-data-to-the-next-handler)\n- [Responding with errors](#responding-with-errors)\n  - [Throwing 500 errors](#throwing-500-errors)\n  - [Handle custom error responses](#handle-custom-error-responses)\n- [With schema](#with-schema)\n    - [Using SimpleSchema](#using-simpleschema)\n    - [Overriding `validate` when using schema](#overriding-validate-when-using-schema)\n    - [Using check](#using-check)\n- [Using middleware](#using-middleware)\n  - [Define global middleware](#define-global-middleware)\n  - [Define route-specific middleware](#define-route-specific-middleware)\n  - [Define middleware using the internal environment](#define-middleware-using-the-internal-environment)\n- [Codestyle](#codestyle)\n      - [via npm](#via-npm)\n      - [via Meteor npm](#via-meteor-npm)\n- [Test](#test)\n      - [Watch mode](#watch-mode)\n- [Changelog](#changelog)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Why do I want this?\n\n- Decouple definition from instantiation\n- Easy management between own and externally defined middleware on a local or global level\n- Validate http request arguments (query/body) the same way as you do with `mdg:validated-method`\n- Just pass in the schema as plain object, instead of manually instantiating a new `SimpleSchema` instance\n- Easy builtin response schema, allowing you to either return a value (to create 200 responses) or throw an Error \n(for 500 responses). You can still customize responses via `req`, `res` and `next`.\n- Easy data access and update between handlers using `this.data()` \n\n\n## Installation\n\nSimply add this package to your meteor packages\n\n```bash\nmeteor add leaonline:http-factory\n```\n\n**Enable encoded URLs and JSON body parsing**\n\n```javascript\nimport { WebApp } from 'meteor/webapp'\n\nWebApp.express.urlencoded({ extended: true })\nWebApp.handlers.use(WebApp.express.json())\n```\n\n## Usage\n\nImport the `createHTTPFactory` function and create the factory function from it. \nThe factory function can obtain the following arguments (*=optional):\n\n- `path: String*`\n- `schema: Object*` - depends on, if `schemaFactory` is defined\n- `method: String*` - if defined, one of `['get', 'head', 'post', 'put', 'delete', 'options', 'trace', 'patch']`\n- `validate: Function*` - if defined, a validation function that should throw an Error if validation fails\n- `run: Function` - always required, the middleware handler to run on the current request \n\n### Basic example\n\nTo make life easier for you, the requests' `query` or `body` data is wrapped before the `run` call into a universal \nobject. No need to directly access `req.query` or `req.body` and check for properties.\nYou can instead use the `function` environment's `data` method:\n\n```javascript\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\nconst createHttpRoute = createHTTPFactory() // default, no params\n\ncreateHttpRoute({\n  path: '/greetings',\n  run: function (/* req, res, next */) {\n    const { name } = this.data() // use this to get the current query/body data\n    return `Hello, ${name}`\n  }\n})\n```\n\nThis code creates a http route, that is handled on any incoming HTTP request (`get`, `post` etc.) and assumes either in query\nor on body (depending on request type) to find a parameter, named `name`. Try it via the following client code:\n\n```javascript\nimport { fetch } from 'meteor/fetch'\n\nconst res = await fetch('/greetings', { params: { name: 'Ada' }})\nawait res.text() // 'Hello, Ada'\n```\n\n### Use `WebApp.rawHandlers`\n\nIf you need to define handlers before any other handler (even Meteor-internal), just pass in the `raw` option:\n\n```javascript\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\nconst createHttpRoute = createHTTPFactory() // default, no params\n\ncreateHttpRoute({\n  raw: true,\n  path: '/greetings',\n  run: function (/* req, res, next */) {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n``` \n\nBeware, though this may have side effects on other packages and core functionality,\nthat expect to run before your handler.\n\n### Create universal handlers\n\nYou can omit `path` on order to run the handler at the root level. This is often used for\nmiddleware like `cors`. \n\n### Specify a method\n\nIf you specify a HTTP method (one of `['get', 'head', 'post', 'put', 'delete', 'options', 'trace', 'patch']`) your\nrequest will only be handled with the correct request method:\n\n```javascript\nimport { WebApp } from 'meteor/webapp'\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\n\nconst createHttpRoute = createHTTPFactory() // default, no params\ncreateHttpRoute({\n  path: '/greetings',\n  method: 'post',\n  run: function (/* req, res, next */) {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\nThe `data` will now contain the `body` data. Note, that you may need to install npm `body-parser` to \nwork with body content, that is not form data encoded.\n\n### Passing data to the next handler\n\nWe also made updating data much easier for you. You can pass an `Object` to the `this.data()` method in order to\nattach new properties to a request or update existsing ones:\n\n\n```javascript\nimport { WebApp } from 'meteor/webapp'\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\n\nconst createHttpRoute = createHTTPFactory() // default, no params\ncreateHttpRoute({\n  path: '/greetings',\n  method: 'get',\n  run: function (req, res, next ) {\n    const { name } = this.data()\n    const updateData = {}\n    if (name === 'Ada') {\n      updateData.title = 'Mrs.'\n    }\n    if (name === 'Bob') {\n      updateData.title = 'Mr.'\n    }\n    this.data(updateData)\n    next()\n  }\n})\n\ncreateHttpRoute({\n  path: '/greetings',\n  method: 'get',\n  run: function (/* req, res, next */) {\n    const { name, title } = this.data()\n    return `Hello, ${title} ${name}`\n  }\n})\n```\n\nIf you call the route, it will contain now the updated data:\n\n```javascript\nimport { HTTP } from 'meteor/http'\n\nconst url = (path, params) =\u003e {\n  const query = new URLSearchParams().toString()\n  for (const key of Object.keys(params)) {\n    query.append(key, params[key])\n  }\n  return `${path}?${query}`\n}\n\nlet res = await fetch(url('/greetings', { name: 'Ada' }))\nawait res.text() // 'Hello, Mrs. Ada'\n\nres = await fetch(url('/greetings', { name: 'Bob' }))\nawait res.text() // 'Hello, Mr. Bob'\n```\n\n## Responding with errors\n\nIf a requests is intended to return a fail / error response (400/500 types) you may use our simple solutions, that cover\nmost of the cases, while ensuring your `run` code contains **logic** and not response handling. \n\n### Throwing 500 errors\n\nIf your `run` method is throwing an Error, then it will be catched and transformed to a `500`response:\n\n```javascript\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\nconst createHttpRoute = createHTTPFactory() // default, no params\n\ncreateHttpRoute({\n  path: '/greetings',\n  run: function (/* req, res, next */) {\n    const { name } = this.data()\n    if (!name) throw new Error('Expected name')\n    return `Hello, ${name}`\n  }\n})\n```\n\nThe `err` param in the callback will then not be `null` but contain the error response:\n\n```javascript\nimport { HTTP } from 'meteor/http'\n\nconst res = await fetch('/greetings', {})\nconst error = await res.json()\nconsole.log(res.status) // 500\nconsole.log(error.title) // 'Internal Server Error'\nconsole.log(error.description) // 'An unintended error occurred.'\nconsole.log(error.info) // Expected name\n```\n\n### Handle custom error responses\n\nIf you have a custom error response to return, you can use the builtin `this.handleError` method:\n\n```javascript\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\nconst createHttpRoute = createHTTPFactory() // default, no params\n\ncreateHttpRoute({\n  path: '/greetings',\n  run: function (req, res, next) {\n    const data = this.data()\n    if (!data.name) {\n      return this.error({ \n          code: 400,\n          title: 'Bad Request',\n          description: 'Malformed query or body.'\n      })\n    }\n    return `Hello, ${data.name}`\n  }\n})\n```\n\n## With schema\n\nIn order to take the burden of input validation from you, we have added a nice `schema` validation mechanism.\nIt works similar to the way `mdg:validated-method`.\n\nWe support various ways to validate an input schema. To **decouple** schema definition from instantiation, we introduced a `shemaFactory`, which\nis basically a function that creates your schema for this collection. This also ensures, that\ndifferent HTTP routes don't share the same schema instances.\n\n#### Using SimpleSchema\n\n```javascript\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\nimport SimpleSchema from 'simpl-schema'\n\nconst schemaFactory = definitions =\u003e new SimpleSchema(definitions)\nconst createHttpRoute = createHTTPFactory({ schemaFactory })\n\ncreateHttpRoute({\n  path: '/greetings',\n  schema: {\n    name: String\n  },\n  run: function (req, res, next) {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\nCall the method via\n\n```javascript\nconst res = await fetch('/greetings', { params: { name: 'Ada' }})\nawait res.text() // 'Hello, Ada'\n```\n\nprovoke a fail via\n\n```javascript\nconst res = await fetch('/greetings')\nconst error = await res.json()\nconsole.log(error.status) // 400\nconsole.log(error.title) // 'Bad request'\nconsole.log(error.description) // 'Malformed query or body.'\nconsole.log(error.info) // Name is required \u003c-- SimpleSchema error message\n```\n\n#### Overriding `validate` when using schema\n\nYou can also override the internal `validate` when using `schema` by passing a `validate` function.\nThis, however, disables the schema validation and is then your responsibility:\n\n```javascript\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\nimport SimpleSchema from 'simpl-schema'\n\nconst schemaFactory = definitions =\u003e new SimpleSchema(definitions)\nconst createHttpRoute = createHTTPFactory({ schemaFactory })\n\ncreateHttpRoute({\n  path: '/greetings',\n  schema: {\n    name: String\n  },\n  validate: () =\u003e {},\n  run: function (/* req, res, next */) {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\nand then call via\n\n\n```javascript\nconst res = await fetch('/greetings')\nawait res.text() // 'Hello, undefined'\n```\n\nIf none of these cover your use case, you can still create your own validation middleware.\n\n#### Using check\n\nYou can also use Meteor's builtin `check` and `Match` for schema validation:\n\n```javascript\nimport { check } from 'meteor/check'\nimport { MyCollection } from '/path/to/MyCollection'\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\n\nconst schemaFactory = schema =\u003e ({\n  validate (args) {\n    check(args, schema)\n  }\n})\n\nconst createHttpRoute = createHTTPFactory({ schemaFactory })\ncreateHttpRoute({\n  path: '/greetings',\n  schema: {\n    name: String\n  },\n  run: function (/* req, res, next */) {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\nNote, that some definitions for `SimpleSchema` and `check`/`Match` may differ.\n\n## Using middleware\n\nOften you need to use third-party middle ware, such as `cors` or `jwt`. This package makes it\nsuper easy to do so.\n\n### Define global middleware\n\nFirst, you can define global middleware that is not bound to the factory environment, \nwhich allows for highest compatibility.\nJust define it with a property name, that is not one of `schemaFactory, raw`:\n\n\n```javascript\nimport { Meteor } from 'meteor/meteor'\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\n\n// is is just some simple example validation\n// of non-standard a-auth-token header\nconst isValidToken = req =\u003e req.headers['x-auth-token'] === Meteor.settings.xAuthToken\nconst simpleAuthExternal = function (req, res, next) {\n  if (!isValidToken(req)) {\n    // external middleware is neither bound to the environment\n    // nor affected in any way, so it can 100% maintin it's logic\n    // however, this.error is not available here\n    const body = JSON.stringify({ title: 'Permission Denied' })\n    res.writeHead(403, { 'Content-Type': 'application/json' })\n    res.end(body)\n  }\n  next()\n}\n\n// pass in this middleware on the abstract factory level\n// to make all routes of all methods to use this\n// additionally, use raw: true in order to ensure this is \n// run at the very first, before any package-level handlers\nconst createHttpRoute = createHTTPFactory({\n  simpleAuth: simpleAuthExternal,\n  raw: true\n})\n\ncreateHttpRoute({\n  path: '/greetings',\n  method: 'get',\n  run: function () {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\nnow your  requests will run through this middleware:\n\n```javascript\nconst res = await fetch('/greetings')\nconst error = await res.json()\nconsole.log(error.status) // 403\nconsole.log(error.title) // 'Permission Denid'\n\nconst params = { name: 'Ada' }\nconst headers = { 'x-auth-token': Meteor.settings.xAuthToken } // warning: passing secrets to the client is unsafe\nconst res = await fetch('/greetings', { params, headers })\nawait res.text() // Hello, Ada\n```\n\n### Define route-specific middleware\n\nYou can also define external middleware on a specific route without affecting other routes. \nJust define it with a property name, that is not one of `path, schema, method, run, validate`:\n\n```javascript\nimport { Meteor } from 'meteor/meteor'\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\nimport { simpleAuthExternal } from '/path/to/simpleAuthExternal'\n\nconst createHttpRoute = createHTTPFactory()\n\ncreateHttpRoute({\n  path: '/greetings',\n  simpleAuth: simpleAuthExternal,\n  method: 'get',\n  run: function () {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\nIt will work only on this route with this method, other routes won't be affected.\n\n### Define middleware using the internal environment\n\nThis becomes a bit redundant, but if you like to run middlware using the internal enviroment,\nyou need to place as the `run` method:\n\n ```javascript\nimport { Meteor } from 'meteor/meteor'\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\n\nconst createHttpRoute = createHTTPFactory()\n\n// is is just some simple example validation\n// of non-standard a-auth-token header\nconst isValidToken = req =\u003e req.headers['x-auth-token'] === Meteor.settings.xAuthToken\nconst simpleAuthInternal = function (req, res, next) {\n  if (!isValidToken(req)) {\n    // internally defined middleware can make use of the environment\n    return this.error({\n      code: 403,\n      title: 'Permission Denied'\n    })\n  }\n  next()\n}\n\ncreateHttpRoute({\n  path: '/greetings',\n  method: 'get',\n  run: simpleAuthInternal\n})\n \ncreateHttpRoute({\n  path: '/greetings',\n  method: 'get',\n  run: function () {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\n### Hooks\n\nYou can currently only hook into error handling:\n\n```javascript\nimport { Meteor } from 'meteor/meteor'\nimport { createHTTPFactory } from 'meteor/leaonline:http-factory'\n\nconst createHttpRoute = createHTTPFactory({\n  // global error hook for all routes\n  onError: e =\u003e {\n    MyCoolLogger.error(e)\n  }\n})\n\ncreateHttpRoute({\n  path: '/greetings',\n  method: 'get',\n  run: simpleAuthInternal,\n  // local error hook only for this route, note\n  // that this route will now use only this hook and not\n  // the global hook, if such is defined\n  onError: e =\u003e {\n    MyCoolLogger.error(e)\n  }\n})\n \ncreateHttpRoute({\n  path: '/greetings',\n  method: 'get',\n  run: function () {\n    const { name } = this.data()\n    return `Hello, ${name}`\n  }\n})\n```\n\n\n## Codestyle\n\nWe use `standard` as code style and for linting.\n\n##### via npm\n\n```bash\nnpm install --global standard snazzy\nstandard | snazzy\n```\n\n##### via Meteor npm\n\n```bash\nmeteor npm install --global standard snazzy\nstandard | snazzy\n```\n\n\n## Test\n\nWe use `meteortesting:mocha` to run our tests on the package.\n\n##### Watch mode\n\n```bash\nTEST_WATCH=1 TEST_CLIENT=0 meteor test-packages ./ --raw-logs --driver-package meteortesting:mocha\n```\n\n\n## Changelog\n- **2.0.0**\n  - Meteor 3 compatibility (breaking)\n- **1.1.0**\n  - fix: tests when run method returns undefined values or no value (=undefined)\n  - feature: `onError` hook can be attached to global factory and factories \n- **1.0.1**\n  - use `EJSON` to stringify results in order to comply with any formats, that\n    can be resolved via EJSON\n\n## License\n\nMIT, see [LICENSE](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleaonline%2Fhttp-factory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleaonline%2Fhttp-factory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleaonline%2Fhttp-factory/lists"}