react-router-define-api 0.1.1 → 0.1.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.
- package/README.md +30 -2
- package/dist/index.cjs +9 -5
- package/dist/index.d.cts +44 -13
- package/dist/index.d.ts +44 -13
- package/dist/index.js +9 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,8 +18,7 @@ import { defineApi } from 'react-router-define-api';
|
|
|
18
18
|
|
|
19
19
|
export const { loader, action } = defineApi({
|
|
20
20
|
GET: async ({ params }) => {
|
|
21
|
-
|
|
22
|
-
return { users };
|
|
21
|
+
return { users: await db.users.findMany() };
|
|
23
22
|
},
|
|
24
23
|
POST: async ({ request }) => {
|
|
25
24
|
const body = await request.formData();
|
|
@@ -38,6 +37,35 @@ export const { loader, action } = defineApi({
|
|
|
38
37
|
- `POST`, `PUT`, `PATCH`, `DELETE` → dispatched inside `action` by `request.method`
|
|
39
38
|
- Undefined methods → `405 Method Not Allowed`
|
|
40
39
|
|
|
40
|
+
### Handler wrapper
|
|
41
|
+
|
|
42
|
+
Wrap all handlers with a higher-order function for error handling, response transformation, logging, etc.:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
export const { loader, action } = defineApi({
|
|
46
|
+
GET: async () => ({ name: 'John' }),
|
|
47
|
+
POST: async ({ request }) => {
|
|
48
|
+
const body = await request.formData();
|
|
49
|
+
return { name: body.get('name') };
|
|
50
|
+
},
|
|
51
|
+
}, {
|
|
52
|
+
handler: loaderActionHandler,
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Example `loaderActionHandler`:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const loaderActionHandler = (fn) => async (args) => {
|
|
60
|
+
try {
|
|
61
|
+
const result = await fn(args);
|
|
62
|
+
return { success: true, status: 200, data: result };
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return { success: false, status: 500, message: String(error) };
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
41
69
|
### Response type helpers
|
|
42
70
|
|
|
43
71
|
Access inferred response types for client-side fetchers or shared contracts:
|
package/dist/index.cjs
CHANGED
|
@@ -31,17 +31,21 @@ var ACTION_METHODS = [
|
|
|
31
31
|
"PATCH",
|
|
32
32
|
"DELETE"
|
|
33
33
|
];
|
|
34
|
-
function defineApi(handlers) {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
34
|
+
function defineApi(handlers, options) {
|
|
35
|
+
const { handler: wrapper } = options ?? {};
|
|
36
|
+
const wrap = (fn) => wrapper ? wrapper(fn) : fn;
|
|
37
|
+
const loader = handlers.GET ? wrap(handlers.GET) : void 0;
|
|
38
|
+
const hasActionHandlers = ACTION_METHODS.some(
|
|
39
|
+
(m) => handlers[m] != null
|
|
40
|
+
);
|
|
41
|
+
const action = hasActionHandlers ? wrap(async (args) => {
|
|
38
42
|
const method = args.request.method.toUpperCase();
|
|
39
43
|
const handler = handlers[method];
|
|
40
44
|
if (typeof handler === "function") {
|
|
41
45
|
return handler(args);
|
|
42
46
|
}
|
|
43
47
|
throw new Response("Method Not Allowed", { status: 405 });
|
|
44
|
-
} : void 0;
|
|
48
|
+
}) : void 0;
|
|
45
49
|
return { loader, action };
|
|
46
50
|
}
|
|
47
51
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -28,22 +28,47 @@ type ActionReturn<H> = (H extends {
|
|
|
28
28
|
} ? HandlerReturn<F> : never);
|
|
29
29
|
/** Extract response type for a specific method, or never if not defined */
|
|
30
30
|
type ResponseOf<H, M extends HttpMethod> = H extends Record<M, infer F> ? HandlerReturn<F> : never;
|
|
31
|
+
/**
|
|
32
|
+
* Higher-order function that wraps each method handler.
|
|
33
|
+
* Receives the original handler and returns a new handler with transformed behavior.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const loaderActionHandler: HandlerWrapper<BaseResponse<unknown>> =
|
|
38
|
+
* (fn) => async (args) => {
|
|
39
|
+
* try {
|
|
40
|
+
* const result = await fn(args);
|
|
41
|
+
* return { success: true, status: 200, data: result };
|
|
42
|
+
* } catch (error) {
|
|
43
|
+
* return { success: false, status: 500, message: String(error) };
|
|
44
|
+
* }
|
|
45
|
+
* };
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
type HandlerWrapper<W = unknown> = (fn: (args: LoaderFunctionArgs | ActionFunctionArgs) => unknown) => (args: LoaderFunctionArgs | ActionFunctionArgs) => Promise<W>;
|
|
49
|
+
/** Options for defineApi */
|
|
50
|
+
interface DefineApiOptions<W = never> {
|
|
51
|
+
/** Higher-order function to wrap all method handlers (e.g. for error handling, response transformation) */
|
|
52
|
+
handler?: HandlerWrapper<W>;
|
|
53
|
+
}
|
|
54
|
+
/** Apply wrapper type: if wrapper is provided, use its return type; otherwise use the raw handler return */
|
|
55
|
+
type WithWrapper<R, W> = [W] extends [never] ? R : Awaited<W>;
|
|
31
56
|
/** Return type of defineApi — loader/action presence and return types inferred from handlers */
|
|
32
|
-
type ApiExports<H extends ApiHandlers> = {
|
|
57
|
+
type ApiExports<H extends ApiHandlers, W = never> = {
|
|
33
58
|
loader: H extends {
|
|
34
59
|
GET: infer F;
|
|
35
|
-
} ? (args: LoaderFunctionArgs) => Promise<HandlerReturn<F>> : undefined;
|
|
36
|
-
action: HasActionMethods<H> extends true ? (args: ActionFunctionArgs) => Promise<ActionReturn<H>> : undefined;
|
|
60
|
+
} ? (args: LoaderFunctionArgs) => Promise<WithWrapper<HandlerReturn<F>, W>> : undefined;
|
|
61
|
+
action: HasActionMethods<H> extends true ? (args: ActionFunctionArgs) => Promise<WithWrapper<ActionReturn<H>, W>> : undefined;
|
|
37
62
|
/** Inferred return type of the GET handler */
|
|
38
|
-
GetResponse: ResponseOf<H, 'GET'>;
|
|
63
|
+
GetResponse: WithWrapper<ResponseOf<H, 'GET'>, W>;
|
|
39
64
|
/** Inferred return type of the POST handler */
|
|
40
|
-
PostResponse: ResponseOf<H, 'POST'>;
|
|
65
|
+
PostResponse: WithWrapper<ResponseOf<H, 'POST'>, W>;
|
|
41
66
|
/** Inferred return type of the PUT handler */
|
|
42
|
-
PutResponse: ResponseOf<H, 'PUT'>;
|
|
67
|
+
PutResponse: WithWrapper<ResponseOf<H, 'PUT'>, W>;
|
|
43
68
|
/** Inferred return type of the PATCH handler */
|
|
44
|
-
PatchResponse: ResponseOf<H, 'PATCH'>;
|
|
69
|
+
PatchResponse: WithWrapper<ResponseOf<H, 'PATCH'>, W>;
|
|
45
70
|
/** Inferred return type of the DELETE handler */
|
|
46
|
-
DeleteResponse: ResponseOf<H, 'DELETE'>;
|
|
71
|
+
DeleteResponse: WithWrapper<ResponseOf<H, 'DELETE'>, W>;
|
|
47
72
|
};
|
|
48
73
|
|
|
49
74
|
/**
|
|
@@ -52,18 +77,24 @@ type ApiExports<H extends ApiHandlers> = {
|
|
|
52
77
|
*
|
|
53
78
|
* @example
|
|
54
79
|
* ```ts
|
|
55
|
-
* const
|
|
80
|
+
* export const { loader, action } = defineApi({
|
|
56
81
|
* GET: async ({ params }) => ({ user: params.id }),
|
|
57
82
|
* POST: async ({ request }) => {
|
|
58
83
|
* const body = await request.formData();
|
|
59
84
|
* return { created: true };
|
|
60
85
|
* },
|
|
61
86
|
* });
|
|
87
|
+
* ```
|
|
62
88
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
89
|
+
* @example With handler wrapper
|
|
90
|
+
* ```ts
|
|
91
|
+
* export const { loader, action } = defineApi({
|
|
92
|
+
* GET: async () => ({ data: 'ok' }),
|
|
93
|
+
* }, {
|
|
94
|
+
* handler: loaderActionHandler,
|
|
95
|
+
* });
|
|
65
96
|
* ```
|
|
66
97
|
*/
|
|
67
|
-
declare function defineApi<const H extends ApiHandlers>(handlers: H): ApiExports<H>;
|
|
98
|
+
declare function defineApi<const H extends ApiHandlers, W = never>(handlers: H, options?: DefineApiOptions<W>): ApiExports<H, W>;
|
|
68
99
|
|
|
69
|
-
export { type ApiHandlers, type HttpMethod, type MethodHandler, defineApi };
|
|
100
|
+
export { type ApiHandlers, type DefineApiOptions, type HandlerWrapper, type HttpMethod, type MethodHandler, defineApi };
|
package/dist/index.d.ts
CHANGED
|
@@ -28,22 +28,47 @@ type ActionReturn<H> = (H extends {
|
|
|
28
28
|
} ? HandlerReturn<F> : never);
|
|
29
29
|
/** Extract response type for a specific method, or never if not defined */
|
|
30
30
|
type ResponseOf<H, M extends HttpMethod> = H extends Record<M, infer F> ? HandlerReturn<F> : never;
|
|
31
|
+
/**
|
|
32
|
+
* Higher-order function that wraps each method handler.
|
|
33
|
+
* Receives the original handler and returns a new handler with transformed behavior.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const loaderActionHandler: HandlerWrapper<BaseResponse<unknown>> =
|
|
38
|
+
* (fn) => async (args) => {
|
|
39
|
+
* try {
|
|
40
|
+
* const result = await fn(args);
|
|
41
|
+
* return { success: true, status: 200, data: result };
|
|
42
|
+
* } catch (error) {
|
|
43
|
+
* return { success: false, status: 500, message: String(error) };
|
|
44
|
+
* }
|
|
45
|
+
* };
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
type HandlerWrapper<W = unknown> = (fn: (args: LoaderFunctionArgs | ActionFunctionArgs) => unknown) => (args: LoaderFunctionArgs | ActionFunctionArgs) => Promise<W>;
|
|
49
|
+
/** Options for defineApi */
|
|
50
|
+
interface DefineApiOptions<W = never> {
|
|
51
|
+
/** Higher-order function to wrap all method handlers (e.g. for error handling, response transformation) */
|
|
52
|
+
handler?: HandlerWrapper<W>;
|
|
53
|
+
}
|
|
54
|
+
/** Apply wrapper type: if wrapper is provided, use its return type; otherwise use the raw handler return */
|
|
55
|
+
type WithWrapper<R, W> = [W] extends [never] ? R : Awaited<W>;
|
|
31
56
|
/** Return type of defineApi — loader/action presence and return types inferred from handlers */
|
|
32
|
-
type ApiExports<H extends ApiHandlers> = {
|
|
57
|
+
type ApiExports<H extends ApiHandlers, W = never> = {
|
|
33
58
|
loader: H extends {
|
|
34
59
|
GET: infer F;
|
|
35
|
-
} ? (args: LoaderFunctionArgs) => Promise<HandlerReturn<F>> : undefined;
|
|
36
|
-
action: HasActionMethods<H> extends true ? (args: ActionFunctionArgs) => Promise<ActionReturn<H>> : undefined;
|
|
60
|
+
} ? (args: LoaderFunctionArgs) => Promise<WithWrapper<HandlerReturn<F>, W>> : undefined;
|
|
61
|
+
action: HasActionMethods<H> extends true ? (args: ActionFunctionArgs) => Promise<WithWrapper<ActionReturn<H>, W>> : undefined;
|
|
37
62
|
/** Inferred return type of the GET handler */
|
|
38
|
-
GetResponse: ResponseOf<H, 'GET'>;
|
|
63
|
+
GetResponse: WithWrapper<ResponseOf<H, 'GET'>, W>;
|
|
39
64
|
/** Inferred return type of the POST handler */
|
|
40
|
-
PostResponse: ResponseOf<H, 'POST'>;
|
|
65
|
+
PostResponse: WithWrapper<ResponseOf<H, 'POST'>, W>;
|
|
41
66
|
/** Inferred return type of the PUT handler */
|
|
42
|
-
PutResponse: ResponseOf<H, 'PUT'>;
|
|
67
|
+
PutResponse: WithWrapper<ResponseOf<H, 'PUT'>, W>;
|
|
43
68
|
/** Inferred return type of the PATCH handler */
|
|
44
|
-
PatchResponse: ResponseOf<H, 'PATCH'>;
|
|
69
|
+
PatchResponse: WithWrapper<ResponseOf<H, 'PATCH'>, W>;
|
|
45
70
|
/** Inferred return type of the DELETE handler */
|
|
46
|
-
DeleteResponse: ResponseOf<H, 'DELETE'>;
|
|
71
|
+
DeleteResponse: WithWrapper<ResponseOf<H, 'DELETE'>, W>;
|
|
47
72
|
};
|
|
48
73
|
|
|
49
74
|
/**
|
|
@@ -52,18 +77,24 @@ type ApiExports<H extends ApiHandlers> = {
|
|
|
52
77
|
*
|
|
53
78
|
* @example
|
|
54
79
|
* ```ts
|
|
55
|
-
* const
|
|
80
|
+
* export const { loader, action } = defineApi({
|
|
56
81
|
* GET: async ({ params }) => ({ user: params.id }),
|
|
57
82
|
* POST: async ({ request }) => {
|
|
58
83
|
* const body = await request.formData();
|
|
59
84
|
* return { created: true };
|
|
60
85
|
* },
|
|
61
86
|
* });
|
|
87
|
+
* ```
|
|
62
88
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
89
|
+
* @example With handler wrapper
|
|
90
|
+
* ```ts
|
|
91
|
+
* export const { loader, action } = defineApi({
|
|
92
|
+
* GET: async () => ({ data: 'ok' }),
|
|
93
|
+
* }, {
|
|
94
|
+
* handler: loaderActionHandler,
|
|
95
|
+
* });
|
|
65
96
|
* ```
|
|
66
97
|
*/
|
|
67
|
-
declare function defineApi<const H extends ApiHandlers>(handlers: H): ApiExports<H>;
|
|
98
|
+
declare function defineApi<const H extends ApiHandlers, W = never>(handlers: H, options?: DefineApiOptions<W>): ApiExports<H, W>;
|
|
68
99
|
|
|
69
|
-
export { type ApiHandlers, type HttpMethod, type MethodHandler, defineApi };
|
|
100
|
+
export { type ApiHandlers, type DefineApiOptions, type HandlerWrapper, type HttpMethod, type MethodHandler, defineApi };
|
package/dist/index.js
CHANGED
|
@@ -5,17 +5,21 @@ var ACTION_METHODS = [
|
|
|
5
5
|
"PATCH",
|
|
6
6
|
"DELETE"
|
|
7
7
|
];
|
|
8
|
-
function defineApi(handlers) {
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
8
|
+
function defineApi(handlers, options) {
|
|
9
|
+
const { handler: wrapper } = options ?? {};
|
|
10
|
+
const wrap = (fn) => wrapper ? wrapper(fn) : fn;
|
|
11
|
+
const loader = handlers.GET ? wrap(handlers.GET) : void 0;
|
|
12
|
+
const hasActionHandlers = ACTION_METHODS.some(
|
|
13
|
+
(m) => handlers[m] != null
|
|
14
|
+
);
|
|
15
|
+
const action = hasActionHandlers ? wrap(async (args) => {
|
|
12
16
|
const method = args.request.method.toUpperCase();
|
|
13
17
|
const handler = handlers[method];
|
|
14
18
|
if (typeof handler === "function") {
|
|
15
19
|
return handler(args);
|
|
16
20
|
}
|
|
17
21
|
throw new Response("Method Not Allowed", { status: 405 });
|
|
18
|
-
} : void 0;
|
|
22
|
+
}) : void 0;
|
|
19
23
|
return { loader, action };
|
|
20
24
|
}
|
|
21
25
|
export {
|