{"id":13469549,"url":"https://github.com/zaiste/wren","last_synced_at":"2025-08-20T23:04:12.723Z","repository":{"id":58092582,"uuid":"528495586","full_name":"zaiste/wren","owner":"zaiste","description":"A small, but powerful HTTP library for Deno \u0026 Deno Deploy, built for convenience and simplicity","archived":false,"fork":false,"pushed_at":"2023-01-28T12:57:37.000Z","size":98,"stargazers_count":77,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-30T07:49:48.483Z","etag":null,"topics":["deno","deno-deploy","deno-library","http","typescript","typescript-library"],"latest_commit_sha":null,"homepage":"","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/zaiste.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":"2022-08-24T16:00:52.000Z","updated_at":"2024-08-30T00:07:03.000Z","dependencies_parsed_at":"2023-02-15T16:10:44.943Z","dependency_job_id":null,"html_url":"https://github.com/zaiste/wren","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaiste%2Fwren","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaiste%2Fwren/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaiste%2Fwren/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaiste%2Fwren/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zaiste","download_url":"https://codeload.github.com/zaiste/wren/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251666226,"owners_count":21624290,"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":["deno","deno-deploy","deno-library","http","typescript","typescript-library"],"created_at":"2024-07-31T15:01:44.367Z","updated_at":"2025-04-30T07:49:53.470Z","avatar_url":"https://github.com/zaiste.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","Modules"],"sub_categories":["Web framework"],"readme":"# Wren\n\n\u003cimg align=\"right\" src=\"./static/wren.png\" height=\"150px\" alt=\"wren: a small, but powerful HTTP library for Deno \u0026 Deno Deploy\"\u003e\n\n**Wren** is a small, but powerful HTTP library for Deno \u0026 Deno Deploy, built for convenience and simplicity.\n\n- convenient [aliases for HTTP responses](#aliases-for-http-responses)\n- automatic [serialization of plain objects into JSON responses](#serialization-of-plain-objects-into-json-responses)\n- optionally [typed JSON responses](#typed-json-responses) for additional checks\n- built-in [router](#router) based on `URLPattern`\n- out-of-the-box [parsing for request's body](#parsing-of-requests-body) (form \u0026 multipart)\n- easily [composable middlewares](#composable-middlewares)\n- [TypeScript types](#typescript-types) augmenting the HTTP experiences, e.g. `Middleware`, `Pipeline`, `RequestExtension`, `Route` et more\n\n```ts\nimport { serve } from \"wren/mod.ts\";\nimport { GET, POST } from \"wren/route.ts\";\nimport * as Response from 'wren/response.ts';\n\nconst routes = [\n  GET('/', () =\u003e Response.OK('Hello, Wren')),\n  POST('/form-post', ({ params }) =\u003e \n    Response.Created(`Received: ${JSON.stringify(params)}`)),\n];\n\nserve(routes);\n```\n\n## Getting Started\n\n```\ndeno run -A -r https://wren.deno.dev my-wren-project\n```\n\n```\ncd my-wren-project\n```\n\n```\ndeno task start\n```\n\n## Features\n\n### Aliases for HTTP Responses\n\nWren adds aliases for HTTP responses so that you can not only write them faster (less characters to write), but you use the actual HTTP status names and not the codes.\n\n```tsx\n// with Wren\n\nimport * as Response from 'wren/response.ts'; // don't forget to configure the import map for this to work\n\n// ...somewhere in your handler (type: `Handler`)\n\nreturn Response.OK('Your Body');\nreturn Response.InternalServerError('Your Body');\nreturn Response.MethodNotAllowed();\n\n// without Wren\n\nimport { Status } from \"http/http_status.ts\"; // Deno's HTTP module\n\nreturn new Response('Your body', { status: 200 });\nreturn new Response('Your body', { status: Status.OK });\n\nreturn new Response('Something bad ', { status: 500 });\nreturn new Response('Something bad ', { status: Status.InternalServerError });\n\nreturn new Response('', { status: 405 });\nreturn new Response('', { status: Status.MethodNotAllowed });\n\n// etc...\n```\n\n### Serialization of Plain Objects into JSON Responses\n\nIn addition to having less to write when using Wren's aliases for HTTP responses, you can also pass a plain JavaScript object directly to these alias functions, and it will be automatically serialized to a JSON response with the proper `Content-Type` headers (using Deno's `Response.json()` underneath)\n\n```tsx\nreturn Response.OK({ source: 'XYZ', message: 'Something went wrong', code: 1234 })\nreturn Response.Created({ message: 'Widget created successfully', code: 777 })\n```\n\n### Typed JSON Responses\n\nWhen you return plain JavaScript objects as the body of your responses, it would be great to have a way to define the shape of this data in order to avoid silly mistakes with typos or just to have a tighter interface between your API and its consumers. In Wren, responses can be *optionally* typed:\n\n```tsx\ninterface ReturnPayload {\n  source: string;\n  message: string;\n  code: number\n}\n\nreturn Response.OK\u003cReturnPayload\u003e({ \n  source: 'XYZ',\n  message: 'Something went wrong',\n  code: 1234,\n}); // this is OK\n\nreturn Response.OK\u003cReturnPayload\u003e({\n  surce: 'XYZ', // squiggly lines for `surce` + autocomplete in your editor\n  message: 'Something went wrong',\n  code: 1234,\n}); \n```\n\n### Router \n\nWren comes with a simple router built around the [URLPattern API](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern). It provides two methods: (1) `add` to register a handler for a specific HTTP method and pathname, and (2) `find` to retrieve a handler for given HTTP method and `Request`'s URL.\n\nWren builds on top of this `Router` to provide the `Routing` object in a form of a `Middleware` (more about middlewares in Wren below).\n\nWith `Routing` you can define your routing as an array of (potentially nested) `Route`s.\n\n```tsx\nimport { serve } from \"http/server.ts\"; // Deno's HTTP server\nimport { GET, POST } from \"wren/route.ts\";\nimport { Routing } from \"wren/routing.ts\";\nimport * as Response from 'wren/response.ts';\n\nconst routes = [\n  GET('/', () =\u003e Response.OK('Hello, Wren')),\n  GET('/hello', () =\u003e Response.Accepted('Hello, again Wren')),\n  POST('/form-post', ({ params }) =\u003e\n    Response.Created(`Received: ${JSON.stringify(params)}`)),\n]\n\nconst routing = Routing(routes);\nserve(routing);\n```\n\nInstead of using the `serve` function from Deno's standard library, you can swap it with the `serve` provided by Wren to pass the `routes` array directly.\n\n```tsx\nimport { serve } from \"wren/mod.ts\";\nimport { GET, POST } from \"wren/route.ts\";\nimport * as Response from 'wren/response.ts';\n\nconst routes = [\n  ...\n];\n\nserve(routes);\n```\n\n### Parsing of `Request`'s Body\n\nWren automatically parses the URL and the body (based on its `Content-Type`) of the incoming request to combine the search params, body params and path params into the additional `params` field of the request. \n\nQuick Reminder:\n- search params come from the URL and are defined after the `?` sign with each separated by the `\u0026` sign; e.g. `http://example.com?foo=1\u0026bar=baz` will be transformed into `{ foo: 1, bar: 'baz' }` in Wren,\n- body params are the fields sent in the request body and can come from the form submissions, JSON requests or as fields in `multipart`,\n- path params are segments of the `pathname` designated as dynamic e.g. `/something/:name` will be transformed into `{ name: 'wren' }` in Wren when invoked as `/something/wren`,\n\nFor convenience, Wren combines all those params into a single, readily available object.\n\nIn addition to that, when you sent `multipart` requests, Wren also provides the uploaded files as the additional `files` field of the request.\n\n```tsx\nconst handler: Handler = (request) =\u003e {\n  const { params, files } = request;\n\n  for (const file of files) {\n    // iterate over files to process them or to save them on disk\n  }\n\n  return Response.OK(params)\n}\n```\n\nThe shape for both `params` and `files` is provided as the `RequestExtension` type not to obscure the built-in Deno's `Request` as defined in the Fetch API.\n\n### Composable Middlewares\n\nIn Wren middlewares are functions that take a handler as input and return a handler as output - as simple as that! :)\n\nThus, the `Middleware` type is defined as:\n\n```ts\ntype Middleware = (handler: Handler) =\u003e Handler\n```\n\n(TBD soon. Check the source code or examples in the meantime)\n\n### TypeScript Types\n\n#### Middleware\n\nTBD\n\n#### Pipeline\n\n`Pipeline` represents composition of a series of middlewares over a handler; thus:\n\n```tsx\ntype Pipeline = [...Middleware[], Handler];\n```\n\nA pipeline is folded or composed in a single handler by applying each middleware onto another and finally on the last handler.\n\nIn Wren, middlewares can be defined globally (similar to the `.use` method in Express), but also locally, i.e. per particular route, which makes it much more flexible in practice than alternatives.\n\nTDB\n\n#### `RequestExtension`\n\n`RequestExtension` defines `params` and `files` that are automatically extracted from the incoming request. This type is defined as an intersection type to the built-in Request type so it's less intrusive.\n\n```tsx\ntype Handler = (request: Request \u0026 RequestExtension, context: Context) =\u003e Response | Promise\u003cResponse\u003e;\n```\n\n#### `Context`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaiste%2Fwren","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzaiste%2Fwren","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaiste%2Fwren/lists"}