{"id":15295980,"url":"https://github.com/ufukbakan/bootpress","last_synced_at":"2026-03-17T04:36:41.133Z","repository":{"id":65836026,"uuid":"600999122","full_name":"ufukbakan/bootpress","owner":"ufukbakan","description":"Express.js but Spring Boot like","archived":false,"fork":false,"pushed_at":"2026-02-14T21:34:45.000Z","size":111,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-15T05:22:40.209Z","etag":null,"topics":["backend","bootpress","express","expressjs","framework","nodejs","npm","package"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/create-bootpress-app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ufukbakan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-02-13T06:29:51.000Z","updated_at":"2024-06-18T03:04:27.000Z","dependencies_parsed_at":"2024-06-17T23:01:35.789Z","dependency_job_id":"5bed0816-a7b2-40db-9d56-1c7a3fd6eda5","html_url":"https://github.com/ufukbakan/bootpress","commit_stats":{"total_commits":80,"total_committers":1,"mean_commits":80.0,"dds":0.0,"last_synced_commit":"0b75ec9bcb7027351930d91905576962e6509034"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ufukbakan/bootpress","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufukbakan%2Fbootpress","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufukbakan%2Fbootpress/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufukbakan%2Fbootpress/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufukbakan%2Fbootpress/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ufukbakan","download_url":"https://codeload.github.com/ufukbakan/bootpress/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ufukbakan%2Fbootpress/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30610127,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-16T23:44:20.790Z","status":"online","status_checked_at":"2026-03-17T02:00:07.764Z","response_time":56,"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":["backend","bootpress","express","expressjs","framework","nodejs","npm","package"],"created_at":"2024-09-30T18:08:50.320Z","updated_at":"2026-03-17T04:36:41.091Z","avatar_url":"https://github.com/ufukbakan.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\" style=\"margin-bottom: 0\" \u003e\n\u003cimg src=\"https://raw.githubusercontent.com/ufukbakan/bootpress/main/bootpress.svg\" width=500 alt=\"bootpress\"\u003e\n\u003c/h1\u003e\n\u003cp align=center\u003eExpress but Spring Boot like\u003c/p\u003e\n\u003ccenter\u003e\n\n[![npm](https://img.shields.io/npm/v/bootpress)](https://www.npmjs.com/package/bootpress)\n![npm downloads](https://img.shields.io/npm/dt/bootpress)\n![MIT license](https://img.shields.io/npm/l/bootpress)\n\n\u003c/center\u003e\n\n## Quick Start\nRecommended tool: **[create-bootpress-app](https://www.npmjs.com/package/create-bootpress-app)**\n___\n\n## Methods\n### **\u003cu\u003eRestService\u003c/u\u003e**: Converts all methods to Express RequestHandlers.\nHttp Response Status will inherit status field of returned value by method or 200 by default.\n\nHttp Response Body will be mapped to 'data' field or value itself by default.\n\nIf you want to explicitly specify a field named 'data' or 'status' it's recommended to encapsulate your value with HttpResponse class.\n#### Basic usage:\n```ts\nimport express from \"express\";\nimport { HttpError, PassParam, RestService } from \"bootpress\";\nimport { as, getOrThrow } from \"bootpress/helpers\";\n\nconst app = express();\napp.use(express.json());\n\nconst UserServiceImpl = {\n    users: [1, 2, 3, 4],\n    findAllUsers(): number[] {\n        return this.users;\n    },\n    findUserById(idInParams: string | undefined) {\n        const id = as(idInParams, \"integer\");\n        return getOrThrow(this.users.find(user =\u003e user === id), new HttpError(404, \"Not Found\"));\n    }\n};\n\nconst UserService = RestService(UserServiceImpl);\n\napp.get(\"/users\", UserService.findAllUsers());\napp.get(\"/users/:id\", PassParam(\"id\")(UserService.findUserById));\n```\n\n#### Advanced usage:\n```ts\nimport { HttpError, HttpResponse, PassBody, PassParam, PassQuery, RestService } from \"bootpress\";\nimport { as, asStrict, getOrThrow } from \"bootpress/helpers\";\n\nclass PostServiceImpl {\n    posts = [1, 2, 3, 4, 5];\n    findById(id: string) {\n        return getOrThrow(\n            this.posts.find(p =\u003e p === as(id, \"integer\")),\n            new HttpError(404, \"Post is not found\")\n        );\n    }\n    add(body: any) {\n        let casted = asStrict(body, {\n            \"id\": \"number\"\n        });\n        this.posts.push(casted.id);\n        return new HttpResponse(201, casted.id);\n    }\n    delete(deleteInQuery: string | undefined, idInQuery: string | undefined) {\n        if (deleteInQuery === \"yes\") {\n            const id = as(idInQuery, \"integer\");\n            const index = this.posts.indexOf(id);\n            if (index \u003e -1) {\n                this.#logDeleted(idInQuery!);\n                return this.posts.splice(index, 1);\n            } else {\n                throw new HttpError(404, \"Post is not found\")\n            }\n        }\n        throw new HttpError(400, \"Bad Request\");\n    }\n    // use private methods to  \n    #logDeleted(id: number | string) {\n        console.warn(`post ${id} is deleted`)\n    }\n    findAll() {\n        return this.posts;\n    }\n}\n\nconst PostService = RestService(PostServiceImpl);\n// this is valid too:\n// const PostService = RestService(new PostServiceImpl());\n\napp.get(\"/posts\", PostService.findAll())\napp.post(\"/posts\", PassBody(PostService.add));\napp.delete(\"/posts\", PassQuery(\"delete\")(PassQuery(\"id\")(PostService.delete)));\napp.get(\"/posts/:id\", PassParam(\"id\")(PostService.findById));\n```\n\n### **\u003cu\u003eRestMethod\u003c/u\u003e**: Converts single method to RequestHandler\n#### Usage:\n```ts\nimport { HttpError, RestMethod } from \"bootpress\";\nimport { getOrThrow } from \"bootpress/helpers\";\n\nclass UserService {\n    users = [1, 2, 3, 4];\n    findAll()  {\n        return RestMethod(() =\u003e {\n            return this.users;\n        })\n    }\n    findById(id: number) {\n        return RestMethod(() =\u003e {\n            return getOrThrow(this.users.find(u =\u003e u == id), new HttpError(404, \"Not Found\"));\n        })\n    }\n}\n\nconst userService = new UserService();\n\napp.get(\"/users\", userService.findAll())\napp.get(\"/users/:id\", (req) =\u003e userService.findById(+req.params.id))\n```\n\n### **\u003cu\u003eRestify\u003c/u\u003e**: Decorator to convert a single method to RequestHandler\n#### Note that currently decorators in Typescript doesn't support changing the return type of applied method. So you have to provide RequestHandler as an \"or type\":\n\n```ts\nimport { Restify } from \"bootpress\";\nimport { RequestHandler } from \"express\";\n\nclass LogServiceImpl {\n    logs = [\"log1\", \"log2\", \"log3\"];\n\n    @Restify\n    findAll(): string[] | RequestHandler {\n        return this.logs;\n    }\n}\n\nconst LogService = new LogServiceImpl();\n\napp.get(\"/logs\", LogService.findAll() as RequestHandler)\n```\n\n### **Argument Passers**\n\n```PassBody(serviceFunction)``` -\u003e Passes body to service function without any validation\n\n```ParseBodyAs(type, config?)(serviceFunction)``` -\u003e Parses body to specified type then passes it to service function. Config object is optional and has messageTemplate field which represents a string with details placeholder: ```{0}```\n\n```PassBodyAs(type, config?)(serviceFunction)``` -\u003e Validates body with provided type and passes it to service function. Config object is optional and has messageTemplate field which represents a string with details placeholder: ```{0}```\n\n```PassAllParams(serviceFunction)``` -\u003e Passes all path parameters to service function as a Record\u003cstring, string\u003e (pure js object that contains key-value pairs)\n\n```PassAllQueries(serviceFunction)``` -\u003e Passes query to service function as Record\u003cstring, string\u003e\n\n```PassAllCookies(serviceFunction)``` -\u003e Passes cookies to service function as Record\u003cstring, string\u003e\n\n```PassParam(pathParam)(serviceFunction)``` -\u003e Passes specified parameter as arguments to service function\n\n```PassQuery(searchQueryName)(serviceFunction)``` -\u003e Passes specified query as arguments to service function\n\n```PassCookie(cookieName)(serviceFunction)``` -\u003e Passes specified cookie as arguments to service function\n\n```PassRequest(serviceFunction)``` -\u003e Passes express request object to service function\n\n```PassResponse(serviceFunction)``` -\u003e Passes express response object to service function\n\n### Chaining argument passers:\nArgument passers can be chained by passing result of one as serviceFunction parameter to other. e.g.:\n```ts\n// in rest service class:\nfunction serviceFunction(cookies, body){\n    ...\n}\n// in router:\nrouter.post(\"/\", PassAllCookies( PassBodyAs(yourSchema)(restService.serviceFunction) ));\n```\n\n\n## Helper Methods\n\n### **getOrThrow(value, httpError)**\nReturns the value back if it's not null, undefined or empty array.\n### **getOrElse(value, defaultValue)**\nReturns the value if it's not null or undefined otherwise returns the default value.\n### **schema(object)**\nHelps you to define a JS Schema.\n### **as(target: any, [type: string | object | array](#type-paramter-for-as--asstrict-methods), [config? object](#config-object-optional-for-as--asstrict-methods))**\nTries to parse target value to provided type.\n#### Type paramter (for as \u0026 asStrict methods)\nIf type of provided type is string then it's a primitive key and valid values are:\n```ts\n\"string\"\n\"string[]\"\n\"boolean\"\n\"boolean[]\"\n\"number\"\n\"number[]\"\n\"integer\"\n\"integer[]\"\n\"string?\"\n\"string[]?\"\n\"boolean?\"\n\"boolean[]?\"\n\"number?\"\n\"number[]?\"\n\"integer?\"\n\"integer[]?\"\n```\nIf typeof provided type is object then it's a JS Schema and structure must follow:\n```ts\n{\n    \"property\": string | object | Array // Nullable primitives not allowed here instead use question mark end of the property key\n    \"nullableProperty?\": string | object | Array\n}\n```\n\nIf typeof provided type is an array the structure must follow: \n```ts\n[\n    yourJsSchemaObject\n]\n```\nThere must be only one element in an array schema which defines ````ArrayOf\u003cSchema\u003e````\n#### Config object (optional for as \u0026 asStrict methods)\nConfig object is optional and structure follows:\n```ts\n{\n    errorVariableName: string | undefined, // variable name in the error message\n    messageTemplate: string | undefined // default values is \"{0}\" where it directly writes error details.\n    // an messageTemplate example is: \"Parse error:\\n{0}\"\n}\n```\n### **asStrict(target: any, [type: string | object | array](#type-parameter), [config? object](#config-object-optional-for-as--asstrict-methods))**\nSame as 'as' method but doesn't try to parse different types instead throws error.\n\n# Release Notes\n\n## v10.0.0:\n- Configuration support for as, asStrict, passBodyAs and parseBodyAs methods.\n- Integrated logger. (Changeable via setLogger method)\n- 500 server errors are logged with error level.\n\n## v9.1.0:\n- Fixed chained argument type error bugs\n- Improvements in argument passer type declarations\n\n## v9.0.2:\n- Added support for null/undefined returning async functions\n\n## v9.0.1:\n- Fixed errors in argument passers\n\n## v9.0.0:\n- New Feature:\n  - Type checking for each argument while passing arguments to service methods\n- Deprecated:\n  - PassQueries, PassCookies, PassParams\n  - Please use PassAllQueries, PassAllCookies or PassAllParams\n- Added:\n  - PassQuery, PassCookie, PassParam\n\n## v8.0.0:\n- Added support for async service functions. (You don't need to await if you wrapped your service with Bootpress functions)\n- Bugfix for falsy response values\n- Simplified implementation\n\n## v7.1.0:\n- getOrThrow: Throws specified error when value is an empty array too.\n## v7.0.0:\n\n### Deprecated helper methods:\n- asSchema\n- asString\n- asBoolean\n- asInteger\n- asNumber\n\nPlease use \"as\" or \"asStrict\" instead of these functions. For example:\n\n```ts\n//const x: string = asString(o); // deprecated\nconst x: string = as(o, \"string\"); \n```\n\n### Added / Changed helper methods:\n- asStrict : Asserts types strictly\n- PassBodyAs(schema): Body must be as same as schema\n- ParseBodyAs(schema): Body have to be parsable to schema","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fufukbakan%2Fbootpress","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fufukbakan%2Fbootpress","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fufukbakan%2Fbootpress/lists"}