{"id":32119007,"url":"https://github.com/lengfangbing/min","last_synced_at":"2026-02-19T13:33:31.486Z","repository":{"id":52294648,"uuid":"270349498","full_name":"lengfangbing/min","owner":"lengfangbing","description":"A decorator web framework for deno","archived":false,"fork":false,"pushed_at":"2021-07-15T13:58:37.000Z","size":473,"stargazers_count":22,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-23T03:24:55.309Z","etag":null,"topics":["decorator","http","https","middleware","restful","router"],"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/lengfangbing.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":"2020-06-07T15:32:33.000Z","updated_at":"2024-11-14T02:03:32.000Z","dependencies_parsed_at":"2022-09-05T20:50:41.978Z","dependency_job_id":null,"html_url":"https://github.com/lengfangbing/min","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/lengfangbing/min","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lengfangbing%2Fmin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lengfangbing%2Fmin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lengfangbing%2Fmin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lengfangbing%2Fmin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lengfangbing","download_url":"https://codeload.github.com/lengfangbing/min/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lengfangbing%2Fmin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29588777,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T16:55:40.614Z","status":"ssl_error","status_checked_at":"2026-02-18T16:55:37.558Z","response_time":162,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["decorator","http","https","middleware","restful","router"],"created_at":"2025-10-20T17:54:08.324Z","updated_at":"2026-02-19T13:33:31.480Z","avatar_url":"https://github.com/lengfangbing.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# min\n\n\u003e **A** **decorator** Deno web framework \u003cbr/\u003e\n\n[![tag](https://img.shields.io/github/tag/lengfangbing/min.svg)](https://github.com/lengfangbing/min)\n[![license](https://img.shields.io/github/license/lengfangbing/min.svg)](https://github.com/lengfangbing/min)\n[![tag](https://img.shields.io/badge/deno-\u003e=1.0.0-green.svg)](https://github.com/denoland/deno)\n[![tag](https://img.shields.io/badge/std-0.75.0-green.svg)](https://github.com/denoland/deno)\n## \u003cstrong\u003eNow, I'm working on rewriting decorator's usage for more convenient and faster use!\u003c/strong\u003e\n## \u003cstrong\u003eit supports decorator now! you could find demos in examples directory~\u003c/strong\u003e\n## introduction\nA framework for Deno's http server, combine Koa's middleware and Express's internal feature. If you are used to using Koa or Express, it'll be easy for you~\u003cbr/\u003e\n## Application, Req, Res\n### Application\nThe Application class provides some apis for user. Most use use() to add one middleware async function. Other like get(), post() and ...\u003cbr\u003e \n`.use(function: Function)`\u003cbr\u003e\nadd one async function in middleware, it works for global routers;\n\n`.start(config: any)` \nand\n`.listen(config: string | ListenOptions)`\n```typescript\ninterface MinConfig{\n  server: ListenOptions,\n  routes: RoutesConfig[],\n  cors?: CorsOptions,\n  assets?: string\n}\ninterface ListenOptions {\n  port: number,\n  hostname: string,\n  certFile?: string,\n  isHttps?: boolean,\n  secure?: boolean,\n  keyFile?: string\n}\ninterface CorsOptions {\n  allowMethods?: string[],\n  allowHeaders?: string[],\n  origin?: string | Function,\n  allowCredentials?: boolean,\n  maxAge?: number,\n  exposeHeaders?: string[]\n}\ninterface RoutesConfig {\n  url: string,\n  method: string,\n  func: string | Function\n  middleware?: Function[]\n}\n```\nYou should provide a config for start function. You can check the demo in ***examples/demo2*** .\n\n`support methods`\n\n* get(...args)\n* post(...args)\n* put(...args)\n* delete(...args)\n* options(...args)\n* head(...args)\n* patch(...args)\n\nargs means the first argument is router path, the last argument is router handler, the rest is middleware only for this router\n### Req\n* `.query`\nthe url search query -- Record\u003cstring, any\u003e\n* `.params`\nthe dynamic router params value -- Record\u003cstring, any\u003e\n* `.url`\nthe request url without url search -- string\n* `.method`\nthe request method as lowercase -- string\n* `.headers`\nthe request headers -- Headers\n* `.body`\nthe request body -- {type: string,value: any}\n* `.request`\nthe server request -- ServerRequest\n* `.cookies`\nthe request cookie -- Map\u003cstring, any\u003e\n### Res\n* `.body`\nthe response body -- any | null\n* `.headers`\nthe response headers -- Headers\n* `.status`\nthe response status, default set one of these (404, 200, 500) -- number\n* `.cookies`\nthe response cookie -- Cookie\n* `.redirect(url: string)`\nrespond a status with 301(default), and set Location header in url. -- Function\n* `.render(path: string)`\nrespond a file such as index.html -- AsyncFunction\n* `.send(req: Req, res: Res)`\nmanual respond with the req and res that user provided\n\n\n## usage\n### You can use all these ways to create new http server\n```typescript\nimport {\n  App,\n  StartApplication,\n  ApplyMiddleware,\n  Middleware,\n  assets,\n  cors,\n  Get,\n  Post,\n  Req,\n  Res,\n  Query,\n  Param,\n  Body,\n  Start,\n} from \"https://raw.githubusercontent.com/lengfangbing/min/master/decorator/mod.ts\";\n\n@StartApplication\nexport class TestClass extends App {\n\n  @ApplyMiddleware([assets('/static'), cors()])\n  @Middleware\n  async middle1(req: Req, res: Res, next: Function) {\n    console.log('middle1');\n    await next();\n    console.log('middle1 end');\n  }\n\n  @Get('/test_query_params/:age')\n  async queryHandler(\n    @Query('id') id: string,\n    @Query('name') name: string,\n    @Param('age') age: string,\n    @Query() query: { id: string; name: string },\n    @Param() params: { age: string },\n    res: Res,\n    req: Req\n  ) {\n    // @Query(pid?: string), if pid is string, it will\n    // return the params in req.query; if pid is undefined\n    // it will return req.query.\n    // if you use some ParameterDecorator like Query\n    // the handlers arguments will end with req\n    // and the res is behind the last ParameterDecorator \n    console.log(id);\n    console.log(name);\n    console.log(age);\n    console.log(query);\n    console.log(params);\n    res.body = {\n      path: 'test2'\n    }\n  }\n  \n  @Get('/test_query')\n  async queryHandler2(@Query() query: { id: string; name: string; }, res: Res, req: Req) {\n    console.log(query);\n    console.log(req.url);\n    res.body = {\n      path: 'test3'\n    }\n  }\n\n  @Get('/test')\n  async testHandle(req: Req, res: Res) {\n    // parse Cookie to a object\n    console.log(req.cookies.toObj());\n    // fetch url `${hostname}:${port}/test/?name=myName\u0026age=20`\n    res.cookies.append('name', '123');\n    res.cookies.append('age', '22');\n    res.body = req.query;\n  }\n\n  @Post(\"/login\")\n   async handler(@Body() body: Record\u003cstring, string\u003e, res: Res, req: Req) {\n     console.log(body);\n     res.body = 123;\n  }\n\n  // The same for @Param and @Query and @Body\n\n  @Start({port: 8000, hostname: '127.0.0.1'})\n  async start() {\n    await this.startServer();\n  }\n\n}\n\nconst initial = new TestClass();\n\nawait initial.start();\n```\n\n```typescript\nimport {\n  Application,\n  Req,\n  Res,\n  HandlerFunc,\n  MiddlewareFunc\n}  from 'https://raw.githubusercontent.com/lengfangbing/min/master/mod.ts';\n\nconst app = new Application();\n\nfunction routerLogger(): MiddlewareFunc{\n  return async function(req, res, next){\n    const time1 = performance.now();\n    await next();\n    const time2 = performance.now();\n    res.headers.set('router-response-time', (time2 - time1).toString());\n  }\n}\n// if you dont give function return type, just give return function arguments type\nfunction requestLogger(){\n  return async function(req: Req, res: Res, next: Function){\n    const time1 = performance.now();\n    await next();\n    const time2 = performance.now();\n    res.headers.set('request-response-time', (time2 - time1).toString());\n  }\n}\n\napp\n  .use(async (request, response, next) =\u003e {\n    console.log(request.url);\n    await next();\n    console.log(response.body);\n  })\n  .use(requestLogger())\n  .get('/', routerLogger(), (request, response) =\u003e {\n    response.body = 'hello world';\n  })\n  .post('/post', async (req, res, next) =\u003e {\n    console.log(`body is ${req.body}`);\n    await next();\n  }, (req, res) =\u003e {\n    res.body = {\n      isPost: true,\n      requestBody: req.body\n    }\n  })\n  .get('/cookie', (request, response) =\u003e {\n    // parse cookie to object\n    console.log(request.cookies.toObj());\n    // name value options\n    response.cookies.append('name', 'fangbing', {\n      domain: '127.0.0.1',\n      secure: true\n    });\n    response.cookies.append('age', '22', {\n      domain: '127.0.0.1',\n      httpOnly: true\n    });\n    response.body = {\n      name: 'test-cookie',\n      cookies: request.cookies.toObj()\n    }\n  })\n  .get('/render', async (req, res) =\u003e {\n    // if you want, you could give function arguments type\n    await res.render('template/index.html');\n  });\n\nawait app.listen('http://127.0.0.1:8000');\n\n```\n# TODO\n* [x] add Query and Param and Body decorator\n* [ ] add global error resolve\n* [ ] add cli for create api or web app\n* [ ] make a doc for min (it's a private repository, will open it when finished. This would take a few months)\n\n### **You can find more demos in ***examples*** directory .\u003cnr\u003e**\n**If you are interested in min.config.ts, just look at demo2**\n#### **if you have some issues or suggestions, you could contact me**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flengfangbing%2Fmin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flengfangbing%2Fmin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flengfangbing%2Fmin/lists"}