{"id":21915535,"url":"https://github.com/terotests/ts2swagger","last_synced_at":"2025-04-18T21:50:12.589Z","repository":{"id":33193542,"uuid":"148321823","full_name":"terotests/ts2swagger","owner":"terotests","description":"Create Swagger API from TypeScript classes","archived":false,"fork":false,"pushed_at":"2022-12-09T05:03:59.000Z","size":611,"stargazers_count":4,"open_issues_count":10,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-26T03:44:07.037Z","etag":null,"topics":["express","koa","openapi2","swagger","typescript"],"latest_commit_sha":null,"homepage":null,"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/terotests.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":"2018-09-11T13:28:48.000Z","updated_at":"2020-02-12T21:32:46.000Z","dependencies_parsed_at":"2023-01-14T23:51:33.221Z","dependency_job_id":null,"html_url":"https://github.com/terotests/ts2swagger","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terotests%2Fts2swagger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terotests%2Fts2swagger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terotests%2Fts2swagger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terotests%2Fts2swagger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/terotests","download_url":"https://codeload.github.com/terotests/ts2swagger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249553419,"owners_count":21290221,"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","koa","openapi2","swagger","typescript"],"created_at":"2024-11-28T19:12:40.063Z","updated_at":"2025-04-18T21:50:12.554Z","avatar_url":"https://github.com/terotests.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TypeScript to Swagger\n\nAutomatically create Swagger documentation and Express endpoints from TypeScript classes.\nThe tool supports\n\n- Automatically inferring types from TypeScript function declarations\n- Generating callable service endpoint for Express\n- Reading class, interface and method and JSDoc comments into Swagger docs\n- Defining parameter and return value Models using TypeScript\n- Defining types returned at specific error codes\n- Grouping functions using Tags\n- `Request` and `Response` as private Service object members (does not pollute interface signature)\n- Rewrites only functions, not entire files\n- Optional values support\n- Private service methods\n\n## How it works?\n\nAt command line you run\n\n```\nts2swagger \u003cdirectory\u003e\n```\n\nThen the tool will\n\n1. Locate all classes with **JSDoc** comment `@service \u003cserviceid\u003e`\n2. Locate all intefaces or classes with **JSDoc** comment `/** model true */`\n3. It will create Swagger documentation specified with `@swagger \u003cfilename\u003e`\n4. It will locate all functions having comment `@service \u003cserviceid\u003e` and create express endpoint in them\n\nIt is possible to automate creation of interface with tools like nodemon etc.\n\n## Installing\n\n```\nnpm -g i ts2swagger\n```\n\n## Service class JSDoc params\n\n- `@swagger \u003cfilename\u003e` output Swagger (OpenAPI 2) file\n- `@title \u003cdocument title\u003e` title of the service\n- `@service \u003cserviceid\u003e` used to identify service\n- `@endpoint \u003cpath\u003e` path where service is located\n- `@version \u003cversionnumber\u003e` for example 1.0.0 or something\n\n```typescript\n/**\n * Freeform test of the API comes here\n *\n * @swagger /src/swagger/sample.json\n * @title The title of the Doc\n * @service myserviceid\n * @endpoint /sometest/v1/\n * @version 1.0.1\n */\nexport class MyService {\n  constructor(private req: express.Request, private res: express.Response) {}\n  ping(message: string): string {\n    return `you sent ${message}`;\n  }\n  async getDevices(): Promise\u003cDevice[]\u003e {\n    return [];\n  }\n}\n```\n\nIt will compile that file into Swagger JSON file `/src/swagger/service.json` and write the\nExpress endpoint if it finds a file with a function which is declared using the service ID\n\n## inteface/class flags\n\nIn the service / interface parameters are mapped to GET, POST, PUT, DELETE or PATCH methods\nautomatically by detecting their value and with some information from the JSDoc comments.\n\n- `@alias \u003cpath\u003e` the url where this method is located\n- `@queryparam \u003cname\u003e` is the first parameter to be placed to GET query var `?varname=value`\n- `@method \u003chttpMethod\u003e` used http method GET, POST, PUT, DELETE or PATCH\n- `@tag \u003cname\u003e` group where this function belongs\n- `@tagdescription \u003ctext\u003e` description for the tag (multiple occurrence override each other)\n- `@error \u003ccode\u003e \u003cmodel\u003e` HTTP response code and message which is returned\n\n```typescript\n  /**\n   *\n   * @alias user\n   * @method delete\n   * @param id set user to some value\n   * @tag user\n   * @tagdescription System users\n   * @error 401 MyErrorClass\n   * @error 404 MyErrorClass\n   */\n  deleteUser(id:string) : TestUser {\n    return {name:'foobar'}\n  }\n```\n\n### Private methods\n\nYou can declare methods private in TypeScript and they will not be part of the\n\n```typescript\n  private getUser() {\n    return this.req.user\n  }\n```\n\n## Model intefaces / class flags\n\nIf interface is returning models\n\n- `@model true` indicate model is ment to be used with services\n\nexample\n\n```typescript\n/**\n * @model true\n */\nexport class Device {\n  id: number;\n  name: string;\n  description?: string;\n}\n```\n\n## Express bootstrap definition\n\n```typescript\nimport * as express from \"express\";\nconst app = express();\n/**\n * @service myserviceid\n */\nfunction bootstrap(app: any, server: (req, res) =\u003e MyService) {\n  // The code will be generated in here\n}\n```\n\nAfter running `ts2swagger` the function `bootstrap` will be overwritten to have contents\n\n```typescript\nfunction bootstrap(app: any, server: (req, res) =\u003e MyService) {\n  // Service endpoint for ping\n  app.get(\"/sometest/v1/ping/:message/\", async function(req, res) {\n    try {\n      res.json(await server(req, res).ping(req.params.message));\n    } catch (e) {\n      res.status(e.statusCode || 400);\n      res.json(e);\n    }\n  });\n}\n```\n\nIt is almost ready to be used as a service, but you have to start it by adding line\n\n```typescript\nbootstrap(app, (req, res) =\u003e new MyService(req, res));\n// and start listening to some port with express\napp.listen(1337);\nconsole.log(\"listening on port 1337\");\n```\n\nThe if you navigate to `http://localhost:1337/sometest/v1/ping/hello` you get\n\n```\n\"you sent hello\"\n```\n\nYou will also get Swagger documentation in JSON\n\n## Handling Errors\n\nDefine Error model, which must have `statusCode` set to the HTTP status code value.\n\n```typescript\n/**\n * @model true\n */\nexport class ErrorNotFound {\n  statusCode = 404;\n  message?: string;\n}\n```\n\nThat class can then be thrown in the service handler\n\n```typescript\nif (somethingwrong) throw ErrorNotFound();\n```\n\nThe Inteface declaration has error defined like this\n\n```typescript\n  /**\n   * @error 403 ErrorForbidden\n   * @error 404 ErrorNotFound\n   */\n  async hello(name:string) : Promise\u003cstring\u003e {\n    if(name==='foo') throw { errorCode:404, message:'User not found'}\n    return `Hello ${name}!!!`\n  }\n```\n\n### Upload parameters\n\nFile upload must be handled manually at the moment, no server code is generated for it. Swagger documentation can have three variables\n\n- `@upload` which defines the name of the uploaded file variable in form data\n- `@uploadmeta` is name for text field which can contain encoded JSON metadata\n- `@uploadmetadesc` Documentation for the uploadmeta contents\n\n```typescript\n  /**\n   * @method post\n   * @upload file\n   * @uploadmeta into\n   * @uploadmetadesc send JSON encoded string here...\n   */\n  async upload(): Promise\u003cany\u003e {\n    // handle upload params manually\n  }\n```\n\n## Full Generated Frontend Sample\n\nThis is example whith is bootstrap from `src/backend` sample code and includes function\nnamed `bootstrap` which is generated from the `src/backend/sample.ts` and used as both\n\n1. Functional backend service\n2. Swagger Document served by `swagger-ui-express`\n\nAlso the public folder is served as static so you can begin developing the client app based on\nthe service.\n\n**NOTE**: only the function `bootstrap` in the example will be overwritten by `ts2swagger`,\nyou can manually edit other parts of the code as a programmer.\n\n```typescript\nimport * as express from \"express\";\nimport { MyService } from \"./sample\";\n\nconst app = express();\n\nconst bodyParser = require(\"body-parser\");\napp.use(bodyParser.json());\napp.use(express.static(\"public\"));\nconst swaggerUi = require(\"swagger-ui-express\");\n\n// sample server...\napp.use(\n  \"/api-docs\",\n  swaggerUi.serve,\n  swaggerUi.setup(require(\"../../swagger/sample.json\"))\n);\n\ntype serverFactory = (req, res) =\u003e MyService;\n\n/**\n * @service myserviceid\n */\nfunction bootstrap(app: any, server: serverFactory) {\n  // Automatically generated endpoint for ping\n  app.get(\"/sometest/v1/ping/:message/\", async function(req, res) {\n    try {\n      res.json(await server(req, res).ping(req.params.message));\n    } catch (e) {\n      res.status(e.statusCode || 400);\n      res.json(e);\n    }\n  });\n  // Automatically generated endpoint for sayHello\n  app.get(\"/sometest/v1/hello/:name/\", async function(req, res) {\n    try {\n      res.json(await server(req, res).sayHello(req.params.name));\n    } catch (e) {\n      res.status(e.statusCode || 400);\n      res.json(e);\n    }\n  });\n  // Automatically generated endpoint for getDevices\n  app.get(\"/sometest/v1/getDevices/\", async function(req, res) {\n    try {\n      res.json(await server(req, res).getDevices());\n    } catch (e) {\n      res.status(e.statusCode || 400);\n      res.json(e);\n    }\n  });\n  // Automatically generated endpoint for upload\n  app.post(\"/sometest/v1/upload/\", async function(req, res) {\n    try {\n      res.json(await server(req, res).upload());\n    } catch (e) {\n      res.status(e.statusCode || 400);\n      res.json(e);\n    }\n  });\n}\n\n// initialize the API endpoint\nbootstrap(app, (req, res) =\u003e new MyService(req, res));\n\nif (!module.parent) {\n  app.listen(1337);\n  console.log(\"listening on port 1337\");\n}\n```\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fterotests%2Fts2swagger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fterotests%2Fts2swagger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fterotests%2Fts2swagger/lists"}