Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ricardocasares/koats
Experimenting with Koa and TypeScript
https://github.com/ricardocasares/koats
branching error error-handling flow koa middleware typescript
Last synced: 12 days ago
JSON representation
Experimenting with Koa and TypeScript
- Host: GitHub
- URL: https://github.com/ricardocasares/koats
- Owner: ricardocasares
- Created: 2019-05-16T17:41:24.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2022-12-09T02:47:35.000Z (about 2 years ago)
- Last Synced: 2024-11-23T11:15:17.478Z (2 months ago)
- Topics: branching, error, error-handling, flow, koa, middleware, typescript
- Language: TypeScript
- Size: 838 KB
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# koats
Experimenting with Koa and TypeScript
## Stack
- Koa
- Koa router
- TypeScript
- Jest## Getting started
### Running the app
- `npm install`
- `npm start dev`### Testing
- `npm run test`
- `npm run test -- --watchAll`
- `npm run test -- --coverage`### Debugging
There's a `launch.json` available to use from `VSCode`, simply start the debugger and add breakpoints to the code.
## Concepts
### Dependencies
Dependencies are injected to the `Context` when the application starts.
Inside the `context` object you'll find a dependency container `dc` where all your services/dependencies will be instantiated.
### Middleware
I'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.
Instead, error handling and branching is achieved by composition of other middleware.
#### safe
Decouples error handling logic from middleware.
This runs your middleware code in the `catch` block of another middleware that has previously `throw`'ed.
This way we keep the "happy path" and the "error path" separated.
```ts
const safe = (a: Middleware) => (b: Middleware): Middleware => async (
ctx,
next
) => {
let flag = false;
const call = async () => {
flag = true;
await next();
};try {
await a(ctx, call);
} catch (err) {
ctx.err = err;if (!flag) {
await b(ctx, next);
} else {
throw err;
}
}
};
```##### Example
```ts
import { safe } from "@/middleware/safe";
import { Middleware } from "koa";// Your heart tells you to keep it simple
const heart: Middleware = (ctx, next) => {
throw new Error("But things can go wrong");
};// Then you can fix it
const withYourMind: Middleware = (ctx, next) => {
if (ctx.err.message.includes("wrong")) {
// Our app keeps running
return next();
}
// Sometimes there's nothing we can do about it!
throw err;
};const saveYourHeart = safe(heart);
// Combine them into one
app.use(saveYourHeart(withYourMind)).use((ctx, next) => {
// To keep doing what you need
return next();
});
```#### branch
Decouples conditional logic from other middleware.
The middleware you pass in will only run when the predicate function returns `true`.
```ts
const branch = (p: Predicate) => (mw: Middleware): Middleware => async (
ctx,
next
) => {
p(ctx) ? await mw(ctx, next) : await next();
};
```##### Example
```ts
import { branch } from "@/middleware/branch";
import { Middleware } from "koa";const html: Middleware = async (ctx, next) => {
ctx.type = "html";
ctx.body = "Hello world
";
await next();
};const json: Middleware = async (ctx, next) => {
ctx.type = "json";
ctx.body = { hello: "world" };
await next();
};const htmlIf = branch(html);
const jsonIf = branch(json);app
.use(htmlIf(ctx => !!ctx.accepts("html"))
.use(jsonIf(ctx => !!ctx.accepts("json"));
```