make-service 0.0.2 → 0.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.
- package/README.md +8 -5
- package/dist/index.d.ts +39 -30
- package/dist/index.js +10 -10
- package/dist/index.mjs +10 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,9 +40,9 @@ This library exports the `makeService` function and some primitives used to buil
|
|
|
40
40
|
|
|
41
41
|
# makeService
|
|
42
42
|
|
|
43
|
-
The main function of this lib is built on top of the primitives described in the following sections. It allows you to create
|
|
43
|
+
The main function of this lib is built on top of the primitives described in the following sections. It allows you to create a service object with a `baseURL` and common `headers` for every request.
|
|
44
44
|
|
|
45
|
-
This
|
|
45
|
+
This service object can be called with every HTTP method and it will return a [`typedResponse`](#typedresponse) object as it uses the [`enhancedFetch`](#enhancedfetch) internally.
|
|
46
46
|
|
|
47
47
|
```ts
|
|
48
48
|
import { makeService } from 'make-service'
|
|
@@ -107,7 +107,7 @@ await api.options("/users")
|
|
|
107
107
|
## enhancedFetch
|
|
108
108
|
|
|
109
109
|
A wrapper around the `fetch` API.
|
|
110
|
-
It returns a [`
|
|
110
|
+
It returns a [`TypedResponse`](#typedresponse) instead of a `Response`.
|
|
111
111
|
|
|
112
112
|
```ts
|
|
113
113
|
import { enhancedFetch } from 'make-service'
|
|
@@ -123,6 +123,8 @@ const json = await response.json()
|
|
|
123
123
|
|
|
124
124
|
This function accepts the same arguments as the `fetch` API - with exception of [JSON-like body](/src/make-service.ts) -, and it also accepts an object-like [`query`](/src/make-service.ts) and a `trace` function that will be called with the `input` and `requestInit` arguments.
|
|
125
125
|
|
|
126
|
+
This slightly different `RequestInit` is typed as `EnhancedRequestInit`.
|
|
127
|
+
|
|
126
128
|
```ts
|
|
127
129
|
import { enhancedFetch } from 'make-service'
|
|
128
130
|
|
|
@@ -150,9 +152,10 @@ A type-safe wrapper around the `Response` object. It adds a `json` and `text` me
|
|
|
150
152
|
|
|
151
153
|
```ts
|
|
152
154
|
import { typedResponse } from 'make-service'
|
|
155
|
+
import type { TypedResponse } from 'make-service'
|
|
153
156
|
|
|
154
157
|
// With JSON
|
|
155
|
-
const response = new Response(JSON.stringify({ foo: "bar" }))
|
|
158
|
+
const response: TypedResponse = new Response(JSON.stringify({ foo: "bar" }))
|
|
156
159
|
const json = await typedResponse(response).json()
|
|
157
160
|
// ^? unknown
|
|
158
161
|
const json = await typedResponse(response).json<{ foo: string }>()
|
|
@@ -161,7 +164,7 @@ const json = await typedResponse(response).json(z.object({ foo: z.string() }))
|
|
|
161
164
|
// ^? { foo: string }
|
|
162
165
|
|
|
163
166
|
// With text
|
|
164
|
-
const response = new Response("foo")
|
|
167
|
+
const response: TypedResponse = new Response("foo")
|
|
165
168
|
const text = await typedResponse(response).text()
|
|
166
169
|
// ^? string
|
|
167
170
|
const text = await typedResponse(response).text<`foo${string}`>()
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
parse: (d: unknown) => T;
|
|
3
|
-
};
|
|
4
|
-
type JSONValue = string | number | boolean | {
|
|
5
|
-
[x: string]: JSONValue;
|
|
6
|
-
} | Array<JSONValue>;
|
|
7
|
-
type SearchParams = ConstructorParameters<typeof URLSearchParams>[0];
|
|
1
|
+
declare const HTTP_METHODS: readonly ["get", "post", "put", "delete", "patch", "options", "head"];
|
|
8
2
|
|
|
9
3
|
/**
|
|
10
4
|
* It returns the JSON object or throws an error if the response is not ok.
|
|
@@ -18,6 +12,27 @@ declare function getJson(response: Response): <T = unknown>(schema?: Schema<T> |
|
|
|
18
12
|
*/
|
|
19
13
|
declare function getText(response: Response): <T extends string = string>(schema?: Schema<T> | undefined) => Promise<T>;
|
|
20
14
|
|
|
15
|
+
type Schema<T> = {
|
|
16
|
+
parse: (d: unknown) => T;
|
|
17
|
+
};
|
|
18
|
+
type JSONValue = string | number | boolean | {
|
|
19
|
+
[x: string]: JSONValue;
|
|
20
|
+
} | Array<JSONValue>;
|
|
21
|
+
type SearchParams = ConstructorParameters<typeof URLSearchParams>[0];
|
|
22
|
+
type TypedResponse = Omit<Response, 'json' | 'text'> & {
|
|
23
|
+
json: TypedResponseJson;
|
|
24
|
+
text: TypedResponseText;
|
|
25
|
+
};
|
|
26
|
+
type EnhancedRequestInit = Omit<RequestInit, 'body'> & {
|
|
27
|
+
body?: JSONValue;
|
|
28
|
+
query?: SearchParams;
|
|
29
|
+
trace?: (...args: Parameters<typeof fetch>) => void;
|
|
30
|
+
};
|
|
31
|
+
type ServiceRequestInit = Omit<EnhancedRequestInit, 'method'>;
|
|
32
|
+
type HTTPMethod = (typeof HTTP_METHODS)[number];
|
|
33
|
+
type TypedResponseJson = ReturnType<typeof getJson>;
|
|
34
|
+
type TypedResponseText = ReturnType<typeof getText>;
|
|
35
|
+
|
|
21
36
|
/**
|
|
22
37
|
* @param input a string or URL to which the query parameters will be added
|
|
23
38
|
* @param searchParams the query parameters
|
|
@@ -43,27 +58,19 @@ declare function makeGetApiUrl(baseURL: string): (path: string, searchParams?: S
|
|
|
43
58
|
* const typedJson = await response.json<User[]>();
|
|
44
59
|
* // ^? User[]
|
|
45
60
|
*/
|
|
46
|
-
declare function typedResponse(response: Response):
|
|
47
|
-
json: ReturnType<typeof getJson>;
|
|
48
|
-
text: ReturnType<typeof getText>;
|
|
49
|
-
};
|
|
61
|
+
declare function typedResponse(response: Response): TypedResponse;
|
|
50
62
|
/**
|
|
51
63
|
* @param body the JSON-like body of the request
|
|
52
64
|
* @returns the body stringified if it is not a string
|
|
53
65
|
*/
|
|
54
66
|
declare function ensureStringBody(body?: JSONValue): string | undefined;
|
|
55
|
-
type Options = Omit<RequestInit, 'body'> & {
|
|
56
|
-
body?: JSONValue;
|
|
57
|
-
query?: SearchParams;
|
|
58
|
-
trace?: (...args: Parameters<typeof fetch>) => void;
|
|
59
|
-
};
|
|
60
67
|
/**
|
|
61
68
|
*
|
|
62
69
|
* @param input a string or URL to be fetched
|
|
63
|
-
* @param
|
|
64
|
-
* @param
|
|
65
|
-
* @param
|
|
66
|
-
* @param
|
|
70
|
+
* @param requestInit the requestInit to be passed to the fetch request. It is the same as the `RequestInit` type, but it also accepts a JSON-like `body` and an object-like `query` parameter.
|
|
71
|
+
* @param requestInit.body the body of the request. It will be automatically stringified so you can send a JSON-like object
|
|
72
|
+
* @param requestInit.query the query parameters to be added to the URL
|
|
73
|
+
* @param requestInit.trace a function that receives the URL and the requestInit and can be used to log the request
|
|
67
74
|
* @returns a Response with typed json and text methods
|
|
68
75
|
* @example const response = await fetch("https://example.com/api/users");
|
|
69
76
|
* const users = await response.json(userSchema);
|
|
@@ -71,24 +78,26 @@ type Options = Omit<RequestInit, 'body'> & {
|
|
|
71
78
|
* const untyped = await response.json();
|
|
72
79
|
* // ^? unknown
|
|
73
80
|
*/
|
|
74
|
-
declare function enhancedFetch(input: string | URL,
|
|
75
|
-
json: <T = unknown>(schema?: Schema<T> | undefined) => Promise<T>;
|
|
76
|
-
text: <T_1 extends string = string>(schema?: Schema<T_1> | undefined) => Promise<T_1>;
|
|
77
|
-
}>;
|
|
81
|
+
declare function enhancedFetch(input: string | URL, requestInit?: EnhancedRequestInit): Promise<TypedResponse>;
|
|
78
82
|
/**
|
|
79
83
|
*
|
|
80
84
|
* @param baseURL the base URL to the API
|
|
81
85
|
* @param baseHeaders any headers that should be sent with every request
|
|
82
|
-
* @returns
|
|
86
|
+
* @returns a service object with HTTP methods that are functions that receive a path and requestInit and return a serialized json response that can be typed or not.
|
|
83
87
|
* @example const headers = { Authorization: "Bearer 123" }
|
|
84
88
|
* const api = makeService("https://example.com/api", headers);
|
|
85
89
|
* const response = await api.get("/users")
|
|
86
90
|
* const users = await response.json(userSchema);
|
|
87
91
|
* // ^? User[]
|
|
88
92
|
*/
|
|
89
|
-
declare function makeService(baseURL: string, baseHeaders?: HeadersInit):
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
declare function makeService(baseURL: string, baseHeaders?: HeadersInit): {
|
|
94
|
+
get: (path: string, requestInit?: ServiceRequestInit) => Promise<TypedResponse>;
|
|
95
|
+
post: (path: string, requestInit?: ServiceRequestInit) => Promise<TypedResponse>;
|
|
96
|
+
put: (path: string, requestInit?: ServiceRequestInit) => Promise<TypedResponse>;
|
|
97
|
+
delete: (path: string, requestInit?: ServiceRequestInit) => Promise<TypedResponse>;
|
|
98
|
+
patch: (path: string, requestInit?: ServiceRequestInit) => Promise<TypedResponse>;
|
|
99
|
+
options: (path: string, requestInit?: ServiceRequestInit) => Promise<TypedResponse>;
|
|
100
|
+
head: (path: string, requestInit?: ServiceRequestInit) => Promise<TypedResponse>;
|
|
101
|
+
};
|
|
93
102
|
|
|
94
|
-
export { addQueryToInput, enhancedFetch, ensureStringBody, makeGetApiUrl, makeService, typedResponse };
|
|
103
|
+
export { EnhancedRequestInit, HTTPMethod, JSONValue, Schema, SearchParams, ServiceRequestInit, TypedResponse, TypedResponseJson, TypedResponseText, addQueryToInput, enhancedFetch, ensureStringBody, makeGetApiUrl, makeService, typedResponse };
|
package/dist/index.js
CHANGED
|
@@ -94,24 +94,24 @@ function ensureStringBody(body) {
|
|
|
94
94
|
return body;
|
|
95
95
|
return JSON.stringify(body);
|
|
96
96
|
}
|
|
97
|
-
async function enhancedFetch(input,
|
|
98
|
-
const { query, trace, ...reqInit } =
|
|
97
|
+
async function enhancedFetch(input, requestInit) {
|
|
98
|
+
const { query, trace, ...reqInit } = requestInit != null ? requestInit : {};
|
|
99
99
|
const headers = { "content-type": "application/json", ...reqInit.headers };
|
|
100
100
|
const url = addQueryToInput(input, query);
|
|
101
101
|
const body = ensureStringBody(reqInit.body);
|
|
102
|
-
const
|
|
103
|
-
trace == null ? void 0 : trace(url,
|
|
104
|
-
const request = new Request(url,
|
|
102
|
+
const enhancedReqInit = { ...reqInit, headers, body };
|
|
103
|
+
trace == null ? void 0 : trace(url, enhancedReqInit);
|
|
104
|
+
const request = new Request(url, enhancedReqInit);
|
|
105
105
|
const response = await fetch(request);
|
|
106
106
|
return typedResponse(response);
|
|
107
107
|
}
|
|
108
108
|
function makeService(baseURL, baseHeaders) {
|
|
109
|
-
const
|
|
110
|
-
return async (path,
|
|
109
|
+
const service = (method) => {
|
|
110
|
+
return async (path, requestInit = {}) => {
|
|
111
111
|
const response = await enhancedFetch(`${baseURL}${path}`, {
|
|
112
|
-
...
|
|
112
|
+
...requestInit,
|
|
113
113
|
method,
|
|
114
|
-
headers: { ...baseHeaders, ...
|
|
114
|
+
headers: { ...baseHeaders, ...requestInit == null ? void 0 : requestInit.headers }
|
|
115
115
|
});
|
|
116
116
|
return response;
|
|
117
117
|
};
|
|
@@ -119,7 +119,7 @@ function makeService(baseURL, baseHeaders) {
|
|
|
119
119
|
return new Proxy({}, {
|
|
120
120
|
get(_target, prop) {
|
|
121
121
|
if (isHTTPMethod(prop))
|
|
122
|
-
return
|
|
122
|
+
return service(prop);
|
|
123
123
|
throw new Error(`Invalid HTTP method: ${prop.toString()}`);
|
|
124
124
|
}
|
|
125
125
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -63,24 +63,24 @@ function ensureStringBody(body) {
|
|
|
63
63
|
return body;
|
|
64
64
|
return JSON.stringify(body);
|
|
65
65
|
}
|
|
66
|
-
async function enhancedFetch(input,
|
|
67
|
-
const { query, trace, ...reqInit } =
|
|
66
|
+
async function enhancedFetch(input, requestInit) {
|
|
67
|
+
const { query, trace, ...reqInit } = requestInit != null ? requestInit : {};
|
|
68
68
|
const headers = { "content-type": "application/json", ...reqInit.headers };
|
|
69
69
|
const url = addQueryToInput(input, query);
|
|
70
70
|
const body = ensureStringBody(reqInit.body);
|
|
71
|
-
const
|
|
72
|
-
trace == null ? void 0 : trace(url,
|
|
73
|
-
const request = new Request(url,
|
|
71
|
+
const enhancedReqInit = { ...reqInit, headers, body };
|
|
72
|
+
trace == null ? void 0 : trace(url, enhancedReqInit);
|
|
73
|
+
const request = new Request(url, enhancedReqInit);
|
|
74
74
|
const response = await fetch(request);
|
|
75
75
|
return typedResponse(response);
|
|
76
76
|
}
|
|
77
77
|
function makeService(baseURL, baseHeaders) {
|
|
78
|
-
const
|
|
79
|
-
return async (path,
|
|
78
|
+
const service = (method) => {
|
|
79
|
+
return async (path, requestInit = {}) => {
|
|
80
80
|
const response = await enhancedFetch(`${baseURL}${path}`, {
|
|
81
|
-
...
|
|
81
|
+
...requestInit,
|
|
82
82
|
method,
|
|
83
|
-
headers: { ...baseHeaders, ...
|
|
83
|
+
headers: { ...baseHeaders, ...requestInit == null ? void 0 : requestInit.headers }
|
|
84
84
|
});
|
|
85
85
|
return response;
|
|
86
86
|
};
|
|
@@ -88,7 +88,7 @@ function makeService(baseURL, baseHeaders) {
|
|
|
88
88
|
return new Proxy({}, {
|
|
89
89
|
get(_target, prop) {
|
|
90
90
|
if (isHTTPMethod(prop))
|
|
91
|
-
return
|
|
91
|
+
return service(prop);
|
|
92
92
|
throw new Error(`Invalid HTTP method: ${prop.toString()}`);
|
|
93
93
|
}
|
|
94
94
|
});
|