@stiviar/modeled-react 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +48 -16
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @stiviar/modeled-react
2
2
 
3
- React bindings for [modeled](https://github.com/szlkeve/stiviar) — a declarative, type-safe API layer.
3
+ A declarative, type-safe API layer for React. Register your API once — as a type, a URL, and a Zod schema — and get a fully typed data-fetching hook with built-in caching and runtime validation.
4
+
5
+ No codegen, no backend language restrictions, no manual `any` casting between fetch and component. Just describe the shape of what you're calling, and `modeled-react` handles the rest.
4
6
 
5
7
  ## Install
6
8
 
@@ -10,23 +12,42 @@ npm i @stiviar/modeled-react
10
12
 
11
13
  ## Usage
12
14
 
13
- Define your models once:
15
+ Define your models once — covering a simple list, a single resource by id, a filtered list, and an OData-style query:
14
16
 
15
17
  ```ts
16
18
  // api.ts
17
19
  import { register } from "@stiviar/modeled-react";
18
- import { Todos, TodosSchema, Users, UsersSchema } from "./types";
20
+ import { Todo, TodoSchema, Todos, TodosSchema } from "./types";
19
21
 
20
22
  export const api = register<{
21
- users: { type: Users };
22
- todos: { type: Todos };
23
+ todos: { type: Todos }; // simple endpoint — no params
24
+ todo: { type: Todo; params: { id: string } }; // single resource, accessed by id
25
+ todosByStatus: { type: Todos; params: { completed: boolean } }; // filtered list, plain query params
26
+ todosOData: {
27
+ type: Todos;
28
+ params: { $filter?: string; $orderby?: string; $top?: number };
29
+ }; // OData-style query
23
30
  }>({
24
- users: {
25
- url: "https://api.example.com/users",
26
- schema: UsersSchema,
27
- },
28
31
  todos: {
29
32
  url: "https://api.example.com/todos",
33
+ schema: TodosSchema, // schema is type checked agains the registered type - no mismatch possible
34
+ },
35
+ todo: {
36
+ url: (p) => `https://api.example.com/todos/${p.id}`, // type of p: {id: string}
37
+ schema: TodoSchema,
38
+ },
39
+ todosByStatus: {
40
+ url: (p) => `https://api.example.com/todos?completed=${p.completed}`, // type of p: {completed: boolean}
41
+ schema: TodosSchema,
42
+ },
43
+ todosOData: {
44
+ url: (p) => {
45
+ // type of p: { $filter?: string; $orderby?: string; $top?: number }
46
+ const query = new URLSearchParams(
47
+ Object.entries(p).map(([k, v]) => [k, String(v)]),
48
+ );
49
+ return `https://api.example.com/odata/todos?${query.toString()}`;
50
+ },
30
51
  schema: TodosSchema,
31
52
  },
32
53
  });
@@ -41,16 +62,27 @@ Use it in a component:
41
62
  import { useData } from "./api";
42
63
 
43
64
  export function Component() {
44
- const { data, isLoading, error } = useData("users");
45
-
46
- if (isLoading) return <p>Loading…</p>;
47
- if (error) return <p>Something went wrong.</p>;
48
-
49
- return <div>user count: {data?.length}</div>;
65
+ const { data: allTodos } = useData("todos");
66
+ const { data: oneTodo } = useData("todo", { id: "42" });
67
+ const { data: activeTodos } = useData("todosByStatus", { completed: false });
68
+ const { data: topTodos } = useData("todosOData", {
69
+ $filter: "completed eq false",
70
+ $orderby: "createdAt desc",
71
+ $top: 10,
72
+ });
73
+
74
+ return <div>{allTodos?.length} todos total</div>;
50
75
  }
51
76
  ```
52
77
 
53
- Types declared for a model flow through the whole codebase — hover `data` in your IDE and see the inferred type, with a compile-time error if your schema and type ever drift apart.
78
+ Types declared for a model flow through the whole codebase — hover `data` in your IDE and see the inferred type, with a compile-time error if your schema and type ever drift apart. Every response is also validated at runtime against the same schema, so a change on the backend fails loudly instead of silently breaking the UI.
79
+
80
+ ## Features
81
+
82
+ - Full type safety, no manual casting
83
+ - Runtime validation on every fetch
84
+ - Built-in caching (TanStack Query under the hood)
85
+ - Typed error handling
54
86
 
55
87
  ## Testing
56
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stiviar/modeled-react",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Declarative, type-safe API layer for frontend apps. Register your API once, get validated data, caching, and testable contracts everywhere",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -26,7 +26,7 @@
26
26
  "test": "vitest run",
27
27
  "test:watch": "vitest",
28
28
  "build": "tsup src/index.ts --format cjs,esm --dts",
29
- "patch": "npm version patch && npm run build && npm publish"
29
+ "patch": "npm run test && npm version patch && npm run build && npm publish"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@testing-library/jest-dom": "^7.0.0",