{"id":20750748,"url":"https://github.com/kapelianovych/mountain","last_synced_at":"2026-05-17T17:45:04.748Z","repository":{"id":36985851,"uuid":"233366858","full_name":"Kapelianovych/mountain","owner":"Kapelianovych","description":"HTTP/2-ready server and client.","archived":false,"fork":false,"pushed_at":"2023-03-06T02:37:15.000Z","size":1377,"stargazers_count":1,"open_issues_count":16,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-09T02:11:51.051Z","etag":null,"topics":["client","http2","nodejs","server"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@prostory/mountain","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kapelianovych.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2020-01-12T09:20:45.000Z","updated_at":"2023-01-31T17:49:48.000Z","dependencies_parsed_at":"2023-02-16T14:01:05.584Z","dependency_job_id":"e70c36a7-2032-4608-b524-acac1566e69d","html_url":"https://github.com/Kapelianovych/mountain","commit_stats":null,"previous_names":["yevhenkap/mountain"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/Kapelianovych/mountain","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kapelianovych%2Fmountain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kapelianovych%2Fmountain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kapelianovych%2Fmountain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kapelianovych%2Fmountain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kapelianovych","download_url":"https://codeload.github.com/Kapelianovych/mountain/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kapelianovych%2Fmountain/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266312188,"owners_count":23909745,"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","status":"online","status_checked_at":"2025-07-21T11:47:31.412Z","response_time":64,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":["client","http2","nodejs","server"],"created_at":"2024-11-17T08:28:34.383Z","updated_at":"2026-05-17T17:45:04.690Z","avatar_url":"https://github.com/Kapelianovych.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mountain ⛰️ - HTTP/2-ready server and client\n\nThis library is written and designed as set of ES modules.\n\n## Intentions\n\nWhy not? 🙃\n\n## Prerequisites\n\n**HTTP/2** was introduced in NodeJS **8.5.0**.\n\nIn order to use this library, you should have Node version at least **12.17.0** and above.\n\n## Get started\n\nIt is wrapper under _HTTP/2_ module of `NodeJS`.\n\n**HTTP/2** (originally named HTTP/2.0) is a major revision of the HTTP network protocol used by the World Wide Web. It was derived from the earlier experimental SPDY protocol, originally developed by Google. _HTTP/2_ was developed by the Hypertext Transfer Protocol working group _httpbis_ (where bis means \"second\") of the Internet Engineering Task Force. The _HTTP/2_ specification was published as RFC 7540 in May 2015.\n\nThe standardization effort was supported by Chrome, Opera, Firefox, Internet Explorer 11, Safari, Amazon Silk, and Edge browsers. Most major browsers had added _HTTP/2_ support by the end of 2015. And NodeJS did.\n\n## API\n\n### Server\n\nAs browsers support only encrypted _HTTP/2_ connection and this is desirable for all clients, so only secure server can be created. For this you must provide key and certificate.\n\n```js\nimport { readFileSync } from 'fs';\n\nimport { server } from '@prostory/mountain';\n\nconst serverInstance = server({\n  key: readFileSync('path/to/key.pem'),\n  cert: readFileSync('path/to/cert.pem'),\n});\n```\n\nServer instance has such public interface:\n\n```ts\ninterface Server {\n  /** Adds listeners to stream's events. */\n  on: \u003cT extends keyof Http2ServerEventMap\u003e(\n    event: T,\n    listener: Http2ServerEventMap[T]\n  ) =\u003e Server;\n  /** Adds routes to server.  */\n  use: (...routes: ReadonlyArray\u003cRoute\u003e) =\u003e Server;\n  /** Stops the server from establishing new sessions */\n  close: (callback?: (error?: Error) =\u003e void) =\u003e Server;\n  /** Starts the server listening to requests. */\n  listen: (port?: number, host?: string, listerner?: VoidFunction) =\u003e Server;\n}\n```\n\nTo let server instance handle requests you should provide routes with `use` method.\n\n```js\nserverInstance.use(\n  route1,\n  route2,\n  route3\n  // and so on`\n);\n```\n\n`Route` has such interface:\n\n```ts\ninterface Route {\n  readonly path: string;\n  readonly method: string;\n  handle: RequestHandler;\n}\n```\n\nwhere `RequestHandler` is a function 👇\n\n```ts\ntype RequestHandler = (request: Request) =\u003e void;\n```\n\nWhere `Request` is an object with a few properties:\n\n```ts\ninterface Context {\n  readonly flags: number;\n  readonly stream: Http2Stream;\n  readonly headers: IncomingHttpHeaders \u0026 IncomingHttpStatusHeader;\n}\n\ninterface Request extends Context {\n  readonly stream: ServerHttp2Stream;\n  /** Holds values of capturing groups of path. */\n  readonly parameters: ReadonlyArray\u003cstring\u003e;\n}\n```\n\nTo create a route use a `route` function.\n\n```js\nimport { constants } from 'http2';\n\nimport { route } from '@prostory/mountain';\n\nconst mainRoute = route(constants.HTTP2_METHOD_GET, '/', (request) =\u003e {\n  /* ... */\n});\n```\n\nThere is a bunch of predefined route functions for most popular methods: `get`, `put`, `post`, `head`, `del`(_delete_) and `options`.\n\n```js\nconst updateRoute = put('/put', (request) =\u003e {\n  /* ... */\n});\n```\n\n\u003e _path_ parameter is converted to `RegExp` to match against path of incoming requests, so for declaring variable parts of URL use valid `RegExp` syntax.\n\n```js\nget('/article/\\\\d+', (request) =\u003e {\n  /* ... */\n});\n```\n\nAnd if you want to receive some values from path, then declare capturing groups and its value will be in _parameters_ property of `Request` object.\n\n```js\nget('/article/(\\\\d+)', (request) =\u003e {\n  // accessRequest is a helper that simplify getting headers and body from\n  // request through its methods.\n  const [id] = accessRequest(request).parameters; // or just request.parameters\n  /* ... */\n});\n```\n\n`accessRequest` function returns `RequestAccessor`:\n\n```ts\ninterface Accessor {\n  body: GetBodyFunction;\n  header: GetHeaderFunction;\n}\n\ninterface RequestAccessor extends Accessor {\n  readonly url: URL;\n  readonly method: string;\n  readonly parameters: ReadonlyArray\u003cstring\u003e;\n}\n```\n\nTo start listening to incoming requests call `listen` function.\n\n```js\n// By default it will start server on localhost:3333\nserverInstance.listen();\n```\n\nIf you have many routes with same prefix, you can use `group` function to gather such routes.\n\n```js\nimport { group } from '@prostory/mountain';\n\nconst testGroup = group('/test');\n\n// It will attach prefix `/test` to each route.\nconst routes = testGroup(\n  route1,\n  route2,\n  route3\n  // and so on\n);\n```\n\nThere is a predefined route creator for handling static assets - `files` function. It takes name of the directory relative to current working directory into which server should search for files.\n\n```js\n// Now all requests to static assets will be catched and handled.\nserverInstance.use(files());\n```\n\nTo respond to incoming request use `responseFor` function:\n\n```ts\nfunction responseFor(request: Request): ResponseBuilder;\n```\n\nIt creates `ResponseBuilder` that allows you simply creating a response.\n\n```ts\ninterface ResponseBuilder {\n  /** Sends response to client. */\n  end: () =\u003e void;\n  /** Define chunks of body to be sent to client. */\n  body: (chunk: string) =\u003e ResponseBuilder;\n  /** Sends JSON to client. */\n  json: (payload: object) =\u003e void;\n  /**\n   * Sends file to client. _path_ should be an\n   * absolute path to file.\n   */\n  file: (path: string) =\u003e void;\n  /** Define a header for response. */\n  header: (name: string, value: string) =\u003e ResponseBuilder;\n}\n```\n\nTo send response to client you should always call `end` method at the end. Otherwise, client will not receive any response. `json` and `file` automatically close response.\n\n```js\nimport { constants } from 'http2';\n\n// ...\n\nresponseFor(request)\n  // By default, status is **200**, if you do not provide any.\n  .header(constants.HTTP2_HEADER_STATUS, String(200))\n  // Body will be sent to client as stream of chunks,\n  // so you can divide you data as much as you want.\n  .body('Hello')\n  .body('world!')\n  .end();\n```\n\n#### Cookies\n\nFor easier creating of cookies there is a `cookies` object. It has two methods: `parse` and `create`:\n\n```ts\nfunction parse(data: string): Cookies;\n\n// Can create only one key/value pair per one method application.\nfunction create(key: string, value: string, attributes: Cookies = {}): string;\n```\n\n`Cookies` is an object with a key/value pairs and [cookie attributes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies).\n\nTo add multiple cookies to response just call `header` method many times with new created cookie.\n\n```ts\nresponseFor(request)\n  .header('set-cookie', create(pid, 'asldkfjlsjdflaskjdflkajfd'))\n  .header('set-cookie', create(name, 'Ben'))\n  .end();\n```\n\n### Client\n\n`client` function is use to create `Client`:\n\n```ts\nfunction client(\n  authority: string | URL,\n  options?: SecureClientSessionOptions\n): Client;\n```\n\nWhere `Client` is:\n\n```ts\ninterface Client {\n  readonly closed: boolean;\n\n  body: (chunk: string) =\u003e Client;\n  header: (name: string, value: string) =\u003e Client;\n\n  /** Removes headers and body from previous request. */\n  fresh: () =\u003e Client;\n  /**\n   * Makes a request to remote peer on opened connection.\n   * By default it performs **GET** request to _path_ URL.\n   */\n  request: (\n    path: string,\n    options?: ClientSessionRequestOptions\n  ) =\u003e Promise\u003cResponse\u003e;\n  /** Closes connection with remote peer. */\n  close: (callback?: VoidFunction) =\u003e void;\n  on: \u003cT extends keyof ClientHttp2SessionEventMap\u003e(\n    event: T,\n    listener: ClientHttp2SessionEventMap[T]\n  ) =\u003e Client;\n}\n```\n\nClient establishes connection with remote peer (usually server). The same client can make multiple requests to remote peer. In order to do that after every request you should re`fresh` client.\n\n```ts\nimport { constants } from 'http2';\nimport { client } from '@prostory/mountain';\n\nconst articleClient = client('...');\n\nconst result1: Promise\u003cResponse\u003e = articleClient\n  .header(constants.HTTP2_HEADER_METHOD, constants.HTTP2_METHOD_GET)\n  .request('/article/1');\n\nconst result2: Promise\u003cResponse\u003e = articleClient\n  .fresh()\n  .header(constants.HTTP2_HEADER_METHOD, constants.HTTP2_METHOD_PUT)\n  .body('some text')\n  .request('/put/article');\n```\n\nClient receives response from remote peer as an object:\n\n```ts\ninterface Response extends Context {\n  readonly stream: ClientHttp2Stream;\n}\n```\n\nThe same as `accessRequest` there is `accessResponse` function that helps to get information from response.\n\n```ts\ninterface Accessor {\n  body: GetBodyFunction;\n  header: GetHeaderFunction;\n}\n\ninterface ResponseAccessor extends Accessor {}\n\nfunction accessResponse(context: Response): ResponseAccessor;\n```\n\n\u003e Do not forget to close client at the end of work.\n\n## Word from author\n\nHave fun ✌️\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkapelianovych%2Fmountain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkapelianovych%2Fmountain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkapelianovych%2Fmountain/lists"}