@jokio/rpc 0.1.0 → 0.1.2
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.
- package/README.md +42 -39
- package/dist/index.d.mts +7 -10
- package/dist/index.d.ts +7 -10
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -8
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @jokio/rpc
|
|
2
2
|
|
|
3
3
|
A type-safe RPC framework for TypeScript with Zod validation, designed for Express servers and HTTP clients.
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ A type-safe RPC framework for TypeScript with Zod validation, designed for Expre
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install
|
|
18
|
+
npm install @jokio/rpc zod express
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
@@ -23,14 +23,14 @@ npm install jokrpc zod express
|
|
|
23
23
|
### 1. Define Your Router Configuration
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
import { defineRouterConfig } from
|
|
27
|
-
import { z } from
|
|
26
|
+
import { defineRouterConfig } from "@jokio/rpc";
|
|
27
|
+
import { z } from "zod";
|
|
28
28
|
|
|
29
29
|
const routerConfig = defineRouterConfig({
|
|
30
30
|
GET: {
|
|
31
|
-
|
|
31
|
+
"/users/:id": {
|
|
32
32
|
query: z.object({
|
|
33
|
-
include: z.enum([
|
|
33
|
+
include: z.enum(["posts", "comments"]).optional(),
|
|
34
34
|
}),
|
|
35
35
|
result: z.object({
|
|
36
36
|
id: z.string(),
|
|
@@ -40,7 +40,7 @@ const routerConfig = defineRouterConfig({
|
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
POST: {
|
|
43
|
-
|
|
43
|
+
"/users": {
|
|
44
44
|
body: z.object({
|
|
45
45
|
name: z.string(),
|
|
46
46
|
email: z.string().email(),
|
|
@@ -55,66 +55,66 @@ const routerConfig = defineRouterConfig({
|
|
|
55
55
|
}),
|
|
56
56
|
},
|
|
57
57
|
},
|
|
58
|
-
})
|
|
58
|
+
});
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### 2. Set Up the Server
|
|
62
62
|
|
|
63
63
|
```typescript
|
|
64
|
-
import express from
|
|
65
|
-
import { applyConfigToExpressRouter } from
|
|
64
|
+
import express from "express";
|
|
65
|
+
import { applyConfigToExpressRouter } from "@jokio/rpc";
|
|
66
66
|
|
|
67
|
-
const app = express()
|
|
68
|
-
app.use(express.json())
|
|
67
|
+
const app = express();
|
|
68
|
+
app.use(express.json());
|
|
69
69
|
|
|
70
|
-
const router = express.Router()
|
|
70
|
+
const router = express.Router();
|
|
71
71
|
|
|
72
72
|
applyConfigToExpressRouter(router, routerConfig, {
|
|
73
73
|
GET: {
|
|
74
|
-
|
|
74
|
+
"/users/:id": async ({ query }) => {
|
|
75
75
|
// Handler implementation
|
|
76
76
|
return {
|
|
77
|
-
id:
|
|
78
|
-
name:
|
|
79
|
-
email:
|
|
80
|
-
}
|
|
77
|
+
id: "1",
|
|
78
|
+
name: "John Doe",
|
|
79
|
+
email: "john@example.com",
|
|
80
|
+
};
|
|
81
81
|
},
|
|
82
82
|
},
|
|
83
83
|
POST: {
|
|
84
|
-
|
|
84
|
+
"/users": async ({ body, query }) => {
|
|
85
85
|
// Handler implementation
|
|
86
86
|
return {
|
|
87
|
-
id:
|
|
87
|
+
id: "2",
|
|
88
88
|
name: body.name,
|
|
89
89
|
email: body.email,
|
|
90
|
-
}
|
|
90
|
+
};
|
|
91
91
|
},
|
|
92
92
|
},
|
|
93
|
-
})
|
|
93
|
+
});
|
|
94
94
|
|
|
95
|
-
app.use(
|
|
96
|
-
app.listen(3000)
|
|
95
|
+
app.use("/api", router);
|
|
96
|
+
app.listen(3000);
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
### 3. Create a Type-Safe Client
|
|
100
100
|
|
|
101
101
|
```typescript
|
|
102
|
-
import { createClient } from
|
|
102
|
+
import { createClient } from "@jokio/rpc";
|
|
103
103
|
|
|
104
104
|
const client = createClient(routerConfig, {
|
|
105
|
-
baseUrl:
|
|
105
|
+
baseUrl: "http://localhost:3000/api",
|
|
106
106
|
validateRequest: true, // Optional: validate requests on client-side
|
|
107
|
-
})
|
|
107
|
+
});
|
|
108
108
|
|
|
109
109
|
// Fully typed API calls
|
|
110
|
-
const user = await client.GET[
|
|
111
|
-
query: { include:
|
|
112
|
-
})
|
|
110
|
+
const user = await client.GET["/users/:id"]({
|
|
111
|
+
query: { include: "posts" },
|
|
112
|
+
});
|
|
113
113
|
|
|
114
|
-
const newUser = await client.POST[
|
|
115
|
-
body: { name:
|
|
114
|
+
const newUser = await client.POST["/users"]({
|
|
115
|
+
body: { name: "Jane Doe", email: "jane@example.com" },
|
|
116
116
|
query: { sendEmail: true },
|
|
117
|
-
})
|
|
117
|
+
});
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
## API Reference
|
|
@@ -128,6 +128,7 @@ Helper function to define a router configuration with type inference.
|
|
|
128
128
|
Applies route handlers to an Express router with automatic validation.
|
|
129
129
|
|
|
130
130
|
**Parameters:**
|
|
131
|
+
|
|
131
132
|
- `router`: Express Router instance
|
|
132
133
|
- `config`: Router configuration object
|
|
133
134
|
- `handlers`: Handler functions for each route
|
|
@@ -137,6 +138,7 @@ Applies route handlers to an Express router with automatic validation.
|
|
|
137
138
|
Creates a type-safe HTTP client.
|
|
138
139
|
|
|
139
140
|
**Options:**
|
|
141
|
+
|
|
140
142
|
- `baseUrl`: Base URL for API requests
|
|
141
143
|
- `headers`: Optional default headers
|
|
142
144
|
- `fetch`: Optional custom fetch function (useful for Node.js or testing)
|
|
@@ -148,26 +150,27 @@ The library provides end-to-end type safety:
|
|
|
148
150
|
|
|
149
151
|
```typescript
|
|
150
152
|
// TypeScript knows the exact shape of requests and responses
|
|
151
|
-
const result = await client.POST[
|
|
153
|
+
const result = await client.POST["/users"]({
|
|
152
154
|
body: {
|
|
153
|
-
name:
|
|
154
|
-
email:
|
|
155
|
+
name: "John",
|
|
156
|
+
email: "invalid-email", // Zod will catch this at runtime
|
|
155
157
|
},
|
|
156
|
-
})
|
|
158
|
+
});
|
|
157
159
|
|
|
158
160
|
// result is typed as { id: string; name: string; email: string }
|
|
159
|
-
console.log(result.id)
|
|
161
|
+
console.log(result.id);
|
|
160
162
|
```
|
|
161
163
|
|
|
162
164
|
## Error Handling
|
|
163
165
|
|
|
164
166
|
The library throws errors for:
|
|
167
|
+
|
|
165
168
|
- HTTP errors (non-2xx responses)
|
|
166
169
|
- Validation errors (invalid request/response data)
|
|
167
170
|
|
|
168
171
|
```typescript
|
|
169
172
|
try {
|
|
170
|
-
await client.POST[
|
|
173
|
+
await client.POST["/users"]({ body: invalidData });
|
|
171
174
|
} catch (error) {
|
|
172
175
|
// Handle validation or HTTP errors
|
|
173
176
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -7,20 +7,20 @@ type RouteConfig = {
|
|
|
7
7
|
result: z.ZodType;
|
|
8
8
|
};
|
|
9
9
|
type RouterConfig = {
|
|
10
|
-
GET: Record<string, Omit<RouteConfig,
|
|
10
|
+
GET: Record<string, Omit<RouteConfig, "body">>;
|
|
11
11
|
POST: Record<string, RouteConfig>;
|
|
12
12
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
type InferRouteConfig$1<T extends RouteConfig | Omit<RouteConfig, "body">> = {
|
|
13
|
+
type InferRouteConfig<T extends RouteConfig | Omit<RouteConfig, "body">> = {
|
|
16
14
|
[K in keyof T]: T[K] extends z.ZodType ? z.infer<T[K]> : never;
|
|
17
15
|
};
|
|
16
|
+
declare const defineRouterConfig: <T extends RouterConfig>(config: T) => T;
|
|
17
|
+
|
|
18
18
|
type RouterClient<T extends RouterConfig> = {
|
|
19
19
|
GET: {
|
|
20
|
-
[K in keyof T["GET"]]: (data?: Omit<Omit<InferRouteConfig
|
|
20
|
+
[K in keyof T["GET"]]: (data?: Omit<Omit<InferRouteConfig<T["GET"][K]>, "body">, "result">) => Promise<InferRouteConfig<T["GET"][K]>["result"]>;
|
|
21
21
|
};
|
|
22
22
|
POST: {
|
|
23
|
-
[K in keyof T["POST"]]: (data: Omit<InferRouteConfig
|
|
23
|
+
[K in keyof T["POST"]]: (data: Omit<InferRouteConfig<T["POST"][K]>, "result">) => Promise<InferRouteConfig<T["POST"][K]>["result"]>;
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
26
|
type FetchFunction = (url: string, options: RequestInit) => Promise<Response>;
|
|
@@ -32,9 +32,6 @@ type CreateClientOptions = {
|
|
|
32
32
|
};
|
|
33
33
|
declare const createClient: <T extends RouterConfig>(config: T, options: CreateClientOptions) => RouterClient<T>;
|
|
34
34
|
|
|
35
|
-
type InferRouteConfig<T extends RouteConfig | Omit<RouteConfig, "body">> = {
|
|
36
|
-
[K in keyof T]: T[K] extends z.ZodType ? z.infer<T[K]> : never;
|
|
37
|
-
};
|
|
38
35
|
type RouterHandlerConfig<T extends RouterConfig> = {
|
|
39
36
|
GET: {
|
|
40
37
|
[K in keyof T["GET"]]: (data: Omit<Omit<InferRouteConfig<T["GET"][K]>, "body">, "result">) => Promise<InferRouteConfig<T["GET"][K]>["result"]>;
|
|
@@ -45,4 +42,4 @@ type RouterHandlerConfig<T extends RouterConfig> = {
|
|
|
45
42
|
};
|
|
46
43
|
declare const applyConfigToExpressRouter: <T extends RouterConfig>(router: Router, config: T, handlers: RouterHandlerConfig<T>) => Router;
|
|
47
44
|
|
|
48
|
-
export { type RouteConfig, type RouterClient, type RouterConfig, type RouterHandlerConfig, applyConfigToExpressRouter, createClient, defineRouterConfig };
|
|
45
|
+
export { type InferRouteConfig, type RouteConfig, type RouterClient, type RouterConfig, type RouterHandlerConfig, applyConfigToExpressRouter, createClient, defineRouterConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,20 +7,20 @@ type RouteConfig = {
|
|
|
7
7
|
result: z.ZodType;
|
|
8
8
|
};
|
|
9
9
|
type RouterConfig = {
|
|
10
|
-
GET: Record<string, Omit<RouteConfig,
|
|
10
|
+
GET: Record<string, Omit<RouteConfig, "body">>;
|
|
11
11
|
POST: Record<string, RouteConfig>;
|
|
12
12
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
type InferRouteConfig$1<T extends RouteConfig | Omit<RouteConfig, "body">> = {
|
|
13
|
+
type InferRouteConfig<T extends RouteConfig | Omit<RouteConfig, "body">> = {
|
|
16
14
|
[K in keyof T]: T[K] extends z.ZodType ? z.infer<T[K]> : never;
|
|
17
15
|
};
|
|
16
|
+
declare const defineRouterConfig: <T extends RouterConfig>(config: T) => T;
|
|
17
|
+
|
|
18
18
|
type RouterClient<T extends RouterConfig> = {
|
|
19
19
|
GET: {
|
|
20
|
-
[K in keyof T["GET"]]: (data?: Omit<Omit<InferRouteConfig
|
|
20
|
+
[K in keyof T["GET"]]: (data?: Omit<Omit<InferRouteConfig<T["GET"][K]>, "body">, "result">) => Promise<InferRouteConfig<T["GET"][K]>["result"]>;
|
|
21
21
|
};
|
|
22
22
|
POST: {
|
|
23
|
-
[K in keyof T["POST"]]: (data: Omit<InferRouteConfig
|
|
23
|
+
[K in keyof T["POST"]]: (data: Omit<InferRouteConfig<T["POST"][K]>, "result">) => Promise<InferRouteConfig<T["POST"][K]>["result"]>;
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
26
|
type FetchFunction = (url: string, options: RequestInit) => Promise<Response>;
|
|
@@ -32,9 +32,6 @@ type CreateClientOptions = {
|
|
|
32
32
|
};
|
|
33
33
|
declare const createClient: <T extends RouterConfig>(config: T, options: CreateClientOptions) => RouterClient<T>;
|
|
34
34
|
|
|
35
|
-
type InferRouteConfig<T extends RouteConfig | Omit<RouteConfig, "body">> = {
|
|
36
|
-
[K in keyof T]: T[K] extends z.ZodType ? z.infer<T[K]> : never;
|
|
37
|
-
};
|
|
38
35
|
type RouterHandlerConfig<T extends RouterConfig> = {
|
|
39
36
|
GET: {
|
|
40
37
|
[K in keyof T["GET"]]: (data: Omit<Omit<InferRouteConfig<T["GET"][K]>, "body">, "result">) => Promise<InferRouteConfig<T["GET"][K]>["result"]>;
|
|
@@ -45,4 +42,4 @@ type RouterHandlerConfig<T extends RouterConfig> = {
|
|
|
45
42
|
};
|
|
46
43
|
declare const applyConfigToExpressRouter: <T extends RouterConfig>(router: Router, config: T, handlers: RouterHandlerConfig<T>) => Router;
|
|
47
44
|
|
|
48
|
-
export { type RouteConfig, type RouterClient, type RouterConfig, type RouterHandlerConfig, applyConfigToExpressRouter, createClient, defineRouterConfig };
|
|
45
|
+
export { type InferRouteConfig, type RouteConfig, type RouterClient, type RouterConfig, type RouterHandlerConfig, applyConfigToExpressRouter, createClient, defineRouterConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// src/types.ts
|
|
4
|
-
var defineRouterConfig = (config) => config;
|
|
5
|
-
|
|
6
3
|
// src/client.ts
|
|
7
4
|
var createClient = (config, options) => {
|
|
8
5
|
const {
|
|
@@ -101,6 +98,9 @@ var applyConfigToExpressRouter = (router, config, handlers) => {
|
|
|
101
98
|
return router;
|
|
102
99
|
};
|
|
103
100
|
|
|
101
|
+
// src/types.ts
|
|
102
|
+
var defineRouterConfig = (config) => config;
|
|
103
|
+
|
|
104
104
|
exports.applyConfigToExpressRouter = applyConfigToExpressRouter;
|
|
105
105
|
exports.createClient = createClient;
|
|
106
106
|
exports.defineRouterConfig = defineRouterConfig;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/server.ts","../src/types.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { InferRouteConfig, RouterConfig } from \"./types\";\n\nexport type RouterClient<T extends RouterConfig> = {\n GET: {\n [K in keyof T[\"GET\"]]: (\n data?: Omit<Omit<InferRouteConfig<T[\"GET\"][K]>, \"body\">, \"result\">\n ) => Promise<InferRouteConfig<T[\"GET\"][K]>[\"result\"]>;\n };\n POST: {\n [K in keyof T[\"POST\"]]: (\n data: Omit<InferRouteConfig<T[\"POST\"][K]>, \"result\">\n ) => Promise<InferRouteConfig<T[\"POST\"][K]>[\"result\"]>;\n };\n};\n\ntype FetchFunction = (url: string, options: RequestInit) => Promise<Response>;\n\ntype CreateClientOptions = {\n baseUrl: string;\n headers?: Record<string, string>;\n fetch?: FetchFunction;\n validateRequest?: boolean;\n};\n\nexport const createClient = <T extends RouterConfig>(\n config: T,\n options: CreateClientOptions\n): RouterClient<T> => {\n const {\n baseUrl,\n headers = {},\n fetch: customFetch = fetch,\n validateRequest = false,\n } = options;\n\n const client = {\n GET: {} as RouterClient<T>[\"GET\"],\n POST: {} as RouterClient<T>[\"POST\"],\n };\n\n Object.keys(config.GET).forEach((path) => {\n (client.GET[path as keyof T[\"GET\"]] as any) = async (data?: any) => {\n if (validateRequest && data?.query) {\n config.GET[path]?.query?.parse(data.query);\n }\n const queryString = data?.query\n ? \"?\" + new URLSearchParams(data.query).toString()\n : \"\";\n const response = await customFetch(`${baseUrl}${path}${queryString}`, {\n method: \"GET\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const json = await response.json();\n\n return config.GET[path]?.result.parse(json);\n };\n });\n\n Object.keys(config.POST).forEach((path) => {\n (client.POST[path as keyof T[\"POST\"]] as any) = async (data: any) => {\n if (validateRequest) {\n if (data?.body) {\n config.POST[path]?.body?.parse(data.body);\n }\n if (data?.query) {\n config.POST[path]?.query?.parse(data.query);\n }\n }\n const queryString = data?.query\n ? \"?\" + new URLSearchParams(data.query).toString()\n : \"\";\n const response = await customFetch(`${baseUrl}${path}${queryString}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: JSON.stringify(data.body),\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const json = await response.json();\n\n return config.POST[path]?.result.parse(json);\n };\n });\n\n return client;\n};\n","import type { Router } from \"express\";\nimport type { InferRouteConfig, RouterConfig } from \"./types\";\n\nexport type RouterHandlerConfig<T extends RouterConfig> = {\n GET: {\n [K in keyof T[\"GET\"]]: (\n data: Omit<Omit<InferRouteConfig<T[\"GET\"][K]>, \"body\">, \"result\">\n ) => Promise<InferRouteConfig<T[\"GET\"][K]>[\"result\"]>;\n };\n POST: {\n [K in keyof T[\"POST\"]]: (\n data: Omit<InferRouteConfig<T[\"POST\"][K]>, \"result\">\n ) => Promise<InferRouteConfig<T[\"POST\"][K]>[\"result\"]>;\n };\n};\n\nexport const applyConfigToExpressRouter = <T extends RouterConfig>(\n router: Router,\n config: T,\n handlers: RouterHandlerConfig<T>\n) => {\n const routes = Object.keys(config.GET);\n\n routes.reduce(\n (r, x) =>\n r.get(x, async (req, res, next) => {\n try {\n const data = {\n query: config.GET[x]?.query?.parse(req.query),\n };\n const result = await handlers.GET[x]?.(data as any);\n const validatedResult = config.GET[x]?.result.parse(result);\n res.json(validatedResult);\n } catch (err) {\n next(err);\n }\n }),\n router\n );\n\n routes.reduce(\n (r, x) =>\n r.post(x, async (req, res, next) => {\n try {\n const data = {\n body: config.POST[x]?.body.parse(req.body),\n query: config.POST[x]?.query?.parse(req.query),\n };\n const result = await handlers.POST[x]?.(data as any);\n const validatedResult = config.POST[x]?.result.parse(result);\n res.json(validatedResult);\n } catch (err) {\n next(err);\n }\n }),\n router\n );\n\n return router;\n};\n","import type z from \"zod\";\n\nexport type RouteConfig = {\n body: z.ZodType;\n query?: z.ZodType;\n result: z.ZodType;\n};\n\nexport type RouterConfig = {\n GET: Record<string, Omit<RouteConfig, \"body\">>;\n POST: Record<string, RouteConfig>;\n};\n\nexport type InferRouteConfig<\n T extends RouteConfig | Omit<RouteConfig, \"body\">\n> = {\n [K in keyof T]: T[K] extends z.ZodType ? z.infer<T[K]> : never;\n};\n\nexport const defineRouterConfig = <T extends RouterConfig>(config: T): T =>\n config;\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// src/types.ts
|
|
2
|
-
var defineRouterConfig = (config) => config;
|
|
3
|
-
|
|
4
1
|
// src/client.ts
|
|
5
2
|
var createClient = (config, options) => {
|
|
6
3
|
const {
|
|
@@ -99,6 +96,9 @@ var applyConfigToExpressRouter = (router, config, handlers) => {
|
|
|
99
96
|
return router;
|
|
100
97
|
};
|
|
101
98
|
|
|
99
|
+
// src/types.ts
|
|
100
|
+
var defineRouterConfig = (config) => config;
|
|
101
|
+
|
|
102
102
|
export { applyConfigToExpressRouter, createClient, defineRouterConfig };
|
|
103
103
|
//# sourceMappingURL=index.mjs.map
|
|
104
104
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/server.ts","../src/types.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { InferRouteConfig, RouterConfig } from \"./types\";\n\nexport type RouterClient<T extends RouterConfig> = {\n GET: {\n [K in keyof T[\"GET\"]]: (\n data?: Omit<Omit<InferRouteConfig<T[\"GET\"][K]>, \"body\">, \"result\">\n ) => Promise<InferRouteConfig<T[\"GET\"][K]>[\"result\"]>;\n };\n POST: {\n [K in keyof T[\"POST\"]]: (\n data: Omit<InferRouteConfig<T[\"POST\"][K]>, \"result\">\n ) => Promise<InferRouteConfig<T[\"POST\"][K]>[\"result\"]>;\n };\n};\n\ntype FetchFunction = (url: string, options: RequestInit) => Promise<Response>;\n\ntype CreateClientOptions = {\n baseUrl: string;\n headers?: Record<string, string>;\n fetch?: FetchFunction;\n validateRequest?: boolean;\n};\n\nexport const createClient = <T extends RouterConfig>(\n config: T,\n options: CreateClientOptions\n): RouterClient<T> => {\n const {\n baseUrl,\n headers = {},\n fetch: customFetch = fetch,\n validateRequest = false,\n } = options;\n\n const client = {\n GET: {} as RouterClient<T>[\"GET\"],\n POST: {} as RouterClient<T>[\"POST\"],\n };\n\n Object.keys(config.GET).forEach((path) => {\n (client.GET[path as keyof T[\"GET\"]] as any) = async (data?: any) => {\n if (validateRequest && data?.query) {\n config.GET[path]?.query?.parse(data.query);\n }\n const queryString = data?.query\n ? \"?\" + new URLSearchParams(data.query).toString()\n : \"\";\n const response = await customFetch(`${baseUrl}${path}${queryString}`, {\n method: \"GET\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const json = await response.json();\n\n return config.GET[path]?.result.parse(json);\n };\n });\n\n Object.keys(config.POST).forEach((path) => {\n (client.POST[path as keyof T[\"POST\"]] as any) = async (data: any) => {\n if (validateRequest) {\n if (data?.body) {\n config.POST[path]?.body?.parse(data.body);\n }\n if (data?.query) {\n config.POST[path]?.query?.parse(data.query);\n }\n }\n const queryString = data?.query\n ? \"?\" + new URLSearchParams(data.query).toString()\n : \"\";\n const response = await customFetch(`${baseUrl}${path}${queryString}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...headers,\n },\n body: JSON.stringify(data.body),\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const json = await response.json();\n\n return config.POST[path]?.result.parse(json);\n };\n });\n\n return client;\n};\n","import type { Router } from \"express\";\nimport type { InferRouteConfig, RouterConfig } from \"./types\";\n\nexport type RouterHandlerConfig<T extends RouterConfig> = {\n GET: {\n [K in keyof T[\"GET\"]]: (\n data: Omit<Omit<InferRouteConfig<T[\"GET\"][K]>, \"body\">, \"result\">\n ) => Promise<InferRouteConfig<T[\"GET\"][K]>[\"result\"]>;\n };\n POST: {\n [K in keyof T[\"POST\"]]: (\n data: Omit<InferRouteConfig<T[\"POST\"][K]>, \"result\">\n ) => Promise<InferRouteConfig<T[\"POST\"][K]>[\"result\"]>;\n };\n};\n\nexport const applyConfigToExpressRouter = <T extends RouterConfig>(\n router: Router,\n config: T,\n handlers: RouterHandlerConfig<T>\n) => {\n const routes = Object.keys(config.GET);\n\n routes.reduce(\n (r, x) =>\n r.get(x, async (req, res, next) => {\n try {\n const data = {\n query: config.GET[x]?.query?.parse(req.query),\n };\n const result = await handlers.GET[x]?.(data as any);\n const validatedResult = config.GET[x]?.result.parse(result);\n res.json(validatedResult);\n } catch (err) {\n next(err);\n }\n }),\n router\n );\n\n routes.reduce(\n (r, x) =>\n r.post(x, async (req, res, next) => {\n try {\n const data = {\n body: config.POST[x]?.body.parse(req.body),\n query: config.POST[x]?.query?.parse(req.query),\n };\n const result = await handlers.POST[x]?.(data as any);\n const validatedResult = config.POST[x]?.result.parse(result);\n res.json(validatedResult);\n } catch (err) {\n next(err);\n }\n }),\n router\n );\n\n return router;\n};\n","import type z from \"zod\";\n\nexport type RouteConfig = {\n body: z.ZodType;\n query?: z.ZodType;\n result: z.ZodType;\n};\n\nexport type RouterConfig = {\n GET: Record<string, Omit<RouteConfig, \"body\">>;\n POST: Record<string, RouteConfig>;\n};\n\nexport type InferRouteConfig<\n T extends RouteConfig | Omit<RouteConfig, \"body\">\n> = {\n [K in keyof T]: T[K] extends z.ZodType ? z.infer<T[K]> : never;\n};\n\nexport const defineRouterConfig = <T extends RouterConfig>(config: T): T =>\n config;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jokio/rpc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Type-safe RPC framework with Zod validation for Express and TypeScript",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -33,17 +33,13 @@
|
|
|
33
33
|
],
|
|
34
34
|
"author": "",
|
|
35
35
|
"license": "MIT",
|
|
36
|
-
"peerDependencies": {
|
|
37
|
-
"express": "^4.0.0",
|
|
38
|
-
"zod": "^4.0.0"
|
|
39
|
-
},
|
|
40
36
|
"devDependencies": {
|
|
41
37
|
"@types/express": "^4.17.21",
|
|
42
38
|
"@types/node": "^20.10.0",
|
|
43
|
-
"express": "^
|
|
39
|
+
"express": "^5.2.1",
|
|
44
40
|
"tsup": "^8.0.1",
|
|
45
|
-
"typescript": "^5.
|
|
46
|
-
"zod": "^3.
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"zod": "^4.3.4"
|
|
47
43
|
},
|
|
48
44
|
"repository": {
|
|
49
45
|
"type": "git",
|