{"id":21477859,"url":"https://github.com/ricardocasares/koats","last_synced_at":"2026-05-17T20:12:17.322Z","repository":{"id":44142590,"uuid":"187074473","full_name":"ricardocasares/koats","owner":"ricardocasares","description":"Experimenting with Koa and TypeScript","archived":false,"fork":false,"pushed_at":"2022-12-09T02:47:35.000Z","size":858,"stargazers_count":3,"open_issues_count":11,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-01-23T17:55:46.210Z","etag":null,"topics":["branching","error","error-handling","flow","koa","middleware","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/ricardocasares.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}},"created_at":"2019-05-16T17:41:24.000Z","updated_at":"2023-08-29T23:49:44.000Z","dependencies_parsed_at":"2023-01-25T10:32:33.918Z","dependency_job_id":null,"html_url":"https://github.com/ricardocasares/koats","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/ricardocasares%2Fkoats","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardocasares%2Fkoats/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardocasares%2Fkoats/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricardocasares%2Fkoats/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ricardocasares","download_url":"https://codeload.github.com/ricardocasares/koats/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243997168,"owners_count":20380981,"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":["branching","error","error-handling","flow","koa","middleware","typescript"],"created_at":"2024-11-23T11:15:23.315Z","updated_at":"2026-05-17T20:12:12.291Z","avatar_url":"https://github.com/ricardocasares.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# koats\n\nExperimenting with Koa and TypeScript\n\n## Stack\n\n- Koa\n- Koa router\n- TypeScript\n- Jest\n\n## Getting started\n\n### Running the app\n\n- `npm install`\n- `npm start dev`\n\n### Testing\n\n- `npm run test`\n- `npm run test -- --watchAll`\n- `npm run test -- --coverage`\n\n### Debugging\n\nThere's a `launch.json` available to use from `VSCode`, simply start the debugger and add breakpoints to the code.\n\n## Concepts\n\n### Dependencies\n\nDependencies are injected to the `Context` when the application starts.\n\nInside the `context` object you'll find a dependency container `dc` where all your services/dependencies will be instantiated.\n\n### Middleware\n\nI've tried to keep middleware as simple as possible, without handling any errors or conditional logic in the main body, this makes them highly reusable between different routes or apps.\n\nInstead, error handling and branching is achieved by composition of other middleware.\n\n#### safe\n\nDecouples error handling logic from middleware.\n\nThis runs your middleware code in the `catch` block of another middleware that has previously `throw`'ed.\n\nThis way we keep the \"happy path\" and the \"error path\" separated.\n\n```ts\nconst safe = (a: Middleware) =\u003e (b: Middleware): Middleware =\u003e async (\n  ctx,\n  next\n) =\u003e {\n  let flag = false;\n  const call = async () =\u003e {\n    flag = true;\n    await next();\n  };\n\n  try {\n    await a(ctx, call);\n  } catch (err) {\n    ctx.err = err;\n\n    if (!flag) {\n      await b(ctx, next);\n    } else {\n      throw err;\n    }\n  }\n};\n```\n\n##### Example\n\n```ts\nimport { safe } from \"@/middleware/safe\";\nimport { Middleware } from \"koa\";\n\n// Your heart tells you to keep it simple\nconst heart: Middleware = (ctx, next) =\u003e {\n  throw new Error(\"But things can go wrong\");\n};\n\n// Then you can fix it\nconst withYourMind: Middleware = (ctx, next) =\u003e {\n  if (ctx.err.message.includes(\"wrong\")) {\n    // Our app keeps running\n    return next();\n  }\n  // Sometimes there's nothing we can do about it!\n  throw err;\n};\n\nconst saveYourHeart = safe(heart);\n\n// Combine them into one\napp.use(saveYourHeart(withYourMind)).use((ctx, next) =\u003e {\n  // To keep doing what you need\n  return next();\n});\n```\n\n#### branch\n\nDecouples conditional logic from other middleware.\n\nThe middleware you pass in will only run when the predicate function returns `true`.\n\n```ts\nconst branch = (p: Predicate) =\u003e (mw: Middleware): Middleware =\u003e async (\n  ctx,\n  next\n) =\u003e {\n  p(ctx) ? await mw(ctx, next) : await next();\n};\n```\n\n##### Example\n\n```ts\nimport { branch } from \"@/middleware/branch\";\nimport { Middleware } from \"koa\";\n\nconst html: Middleware = async (ctx, next) =\u003e {\n  ctx.type = \"html\";\n  ctx.body = \"\u003ch1\u003eHello world\u003c/h1\u003e\";\n  await next();\n};\n\nconst json: Middleware = async (ctx, next) =\u003e {\n  ctx.type = \"json\";\n  ctx.body = { hello: \"world\" };\n  await next();\n};\n\nconst htmlIf = branch(html);\nconst jsonIf = branch(json);\n\napp\n  .use(htmlIf(ctx =\u003e !!ctx.accepts(\"html\"))\n  .use(jsonIf(ctx =\u003e !!ctx.accepts(\"json\"));\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricardocasares%2Fkoats","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fricardocasares%2Fkoats","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricardocasares%2Fkoats/lists"}