- commit
- 187ff1a
- parent
- d1df279
- author
- Eric Bower
- date
- 2024-02-16 15:08:38 +0000 UTC
docs: copy
4 files changed,
+172,
-1
+59,
-0
1@@ -38,6 +38,65 @@ store.dispatch(fetchUsers());
2 store.dispatch(updateUser({ id: "1", name: "bobby" }));
3 ```
4
5+# Enforcing fetch response type
6+
7+When using `createApi` and `mdw.fetch` we can provide the type that we think
8+will be returned by the fetch response:
9+
10+```ts
11+interface Success {
12+ users: User[];
13+}
14+
15+interface Err {
16+ error: string;
17+}
18+
19+const fetchUsers = api.get<never, Success, Err>(
20+ "/users",
21+ function* (ctx, next) {
22+ yield* next();
23+
24+ if (!ctx.json.ok) {
25+ // we have an error type
26+ console.log(ctx.json.value.error);
27+ return;
28+ }
29+
30+ // we have a success type
31+ console.log(ctx.json.value.users);
32+ },
33+);
34+```
35+
36+When calling `createApi` you can also pass it a generic error type that all
37+endpoints inherit:
38+
39+```ts
40+import type { ApiCtx } from "starfx";
41+
42+type MyApiCtx<P = any, S = any> = ApiCtx<P, S, { error: string }>;
43+
44+const api = createApi<MyApiCtx>();
45+
46+// this will inherit the types from `MyApiCtx`
47+const fetchUsers = api.get<never, Success>(
48+ "/users",
49+ function* (ctx, next) {
50+ yield* next();
51+
52+ if (!ctx.json.ok) {
53+ // we have an error type
54+ console.log(ctx.json.value.error);
55+ return;
56+ }
57+
58+ // we have a success type
59+ console.log(ctx.json.value.users);
60+ },
61+);
62+```
63+
64 # The same API endpoints but different logic
65
66 It is very common to have the same endpoint with different business logic
+1,
-1
1@@ -183,7 +183,7 @@ in your system.
2
3 [Read more about entity factories.](https://bower.sh/entity-factories)
4
5-# `slice.loader`
6+# `loader`
7
8 This is a specialized database table specific to managing loaders in `starfx`.
9 [Read more about loaders here](/loader).
+51,
-0
1@@ -89,3 +89,54 @@ const store = configureStore(schema);
2 store.run(api.bootup);
3 store.dispatch(fetchUsers());
4 ```
5+
6+# How to update state
7+
8+There are **three** ways to update state, each with varying degrees of type
9+safety:
10+
11+```ts
12+import { updateStore } from "starfx/store";
13+
14+function*() {
15+ // good types
16+ yield* schema.update([/* ... */]);
17+ // no types
18+ yield* updateStore([/* ... */]);
19+}
20+
21+store.run(function*() {
22+ // no types
23+ yield* store.update([/* ... */]);
24+});
25+```
26+
27+`schema.update` has the highest type safety because it knows your state shape.
28+The other methods are more generic and the user will have to provide types to
29+them manually.
30+
31+# Updating state from view
32+
33+You cannot directly update state from the view, users can only manipulate state
34+from a thunk, endpoint, or a delimited continuation.
35+
36+This is a design decision that forces everything to route through our mini
37+controllers.
38+
39+However, it is very easy to create a controller to do simple tasks like updating
40+state:
41+
42+```ts
43+import type { StoreUpdater } from "starfx/store";
44+
45+const updater = thunks.create<StoreUpdater[]>("update", function* (ctx, next) {
46+ yield* updateStore(ctx.payload);
47+ yield* next();
48+});
49+
50+store.dispatch(
51+ updater([
52+ schema.users.add({ [user1.id]: user }),
53+ ]),
54+);
55+```
+61,
-0
1@@ -61,3 +61,64 @@ store.dispatch(log("sending log message"));
2 // last mdw in the stack
3 // after all remaining middleware have run
4 ```
5+
6+# Thunk action
7+
8+When creating a thunk, the return value is just an action creator:
9+
10+```ts
11+console.log(log("sending log message"));
12+{
13+ type: "log",
14+ payload: "sending log message"
15+}
16+```
17+
18+A thunk action adheres to the
19+[flux standard action spec](https://github.com/redux-utilities/flux-standard-action).
20+
21+# Thunk payload
22+
23+When calling a thunk, the user can provide a payload that is strictly enforced
24+and accessible via the `ctx.payload` property:
25+
26+```ts
27+const makeItSo = api.get<{ id: string }>("make-it-so", function* (ctx, next) {
28+ console.log(ctx.payload);
29+ yield* next();
30+});
31+
32+makeItSo(); // type error!
33+makeItSo("123"); // type error!
34+makeItSo({ id: "123" }); // nice!
35+```
36+
37+# Custom `ctx`
38+
39+End-users are able to provide a custom `ctx` object to their thunks. It must
40+extend `ThunkCtx` in order for it to pass, but otherwise you are free to add
41+whatever properties you want:
42+
43+```ts
44+import { createThunks, type ThunkCtx } from "starfx";
45+
46+interface MyCtx extends ThunkCtx {
47+ wow: bool;
48+}
49+
50+const thunks = createThunks<MyCtx>();
51+
52+// we recommend a mdw that ensures the property exists since we cannot
53+// make that guarentee
54+thunks.use(function* (ctx, next) {
55+ if (!Object.hasOwn(ctx, "wow")) {
56+ ctx.wow = false;
57+ }
58+ yield* next();
59+});
60+
61+const log = thunks.create("log", function* (ctx, next) {
62+ ctx.wow = true;
63+ yield* next();
64+});
65+```