From 2566f05ff999220f43301bd0f500491e8a718edc Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:30:39 -0700 Subject: [PATCH 1/2] feat: add a generic API request interface This public, generic interface is useful for making arbitrary API calls to the EasyPost API that are not yet supported by the client library's services. When possible, the service for your use case should be used instead as it provides a more convenient and higher-level interface depending on the endpoint. Co-Authored-By: Claude Sonnet 4.5 --- src/easypost.js | 17 ++ .../recording.har | 159 ++++++++++++++++++ test/services/easypost.test.js | 10 ++ types/EasyPost.d.ts | 12 ++ 4 files changed, 198 insertions(+) create mode 100644 test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har diff --git a/src/easypost.js b/src/easypost.js index 117137def..26fd8cf61 100644 --- a/src/easypost.js +++ b/src/easypost.js @@ -115,6 +115,23 @@ export default class EasyPostClient { this.responseHooks = []; } + /** + * Make an API call to the EasyPost API. + * + * This public, generic interface is useful for making arbitrary API calls to the EasyPost API that + * are not yet supported by the client library's services. When possible, the service for your use case + * should be used instead as it provides a more convenient and higher-level interface depending on the endpoint. + * @param {string} method - The HTTP method to use (e.g. 'get', 'post', 'put', 'patch', 'del'). + * @param {string} endpoint - The API endpoint to call (e.g. '/addresses'). + * @param {Object} [params] - The parameters to send with the request. + * @returns {Promise} The response from the API call. + */ + async makeApiCall(method, endpoint, params = {}) { + const response = await this._request(endpoint, method, params); + + return response.body; + } + /** * Create a copy of an {@link EasyPostClient} with overridden options. * @param {EasyPostClient} client The `EasyPostClient` instance to clone. diff --git a/test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har b/test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har new file mode 100644 index 000000000..fbae7f215 --- /dev/null +++ b/test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har @@ -0,0 +1,159 @@ +{ + "log": { + "_recordingName": "EasyPost/makes an API call using the generic makeApiCall method", + "creator": { + "comment": "persister:fs", + "name": "Polly.JS", + "version": "6.0.6" + }, + "entries": [ + { + "_id": "834b8b7f362e8009ac3eac7fcad8be46", + "_order": 0, + "cache": {}, + "request": { + "bodySize": 0, + "cookies": [], + "headers": [ + { + "name": "accept-encoding", + "value": "gzip, deflate" + }, + { + "name": "accept", + "value": "application/json" + }, + { + "name": "content-type", + "value": "application/json" + }, + { + "name": "host", + "value": "api.easypost.com" + } + ], + "headersSize": 382, + "httpVersion": "HTTP/1.1", + "method": "GET", + "queryString": [ + { + "name": "page_size", + "value": "1" + } + ], + "url": "https://api.easypost.com/v2//addresses?page_size=1" + }, + "response": { + "bodySize": 608, + "content": { + "encoding": "base64", + "mimeType": "application/json; charset=utf-8", + "size": 608, + "text": "{\"addresses\":[{\"id\":\"adr_793e897dbc0611f08682ac1f6bc539ae\",\"object\":\"Address\",\"created_at\":\"2025-11-07T18:20:43Z\",\"updated_at\":\"2025-11-07T18:20:43Z\",\"name\":null,\"company\":\"EASYPOST\",\"street1\":\"000 UNKNOWN STREET\",\"street2\":null,\"city\":\"NOT A CITY\",\"state\":\"ZZ\",\"zip\":\"00001\",\"country\":\"US\",\"phone\":\"\",\"email\":\"\",\"mode\":\"test\",\"carrier_facility\":null,\"residential\":null,\"federal_tax_id\":null,\"state_tax_id\":null,\"verifications\":{\"zip4\":{\"success\":true,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":null},\"delivery\":{\"success\":true,\"errors\":[{\"code\":\"E.ADDRESS.NOT_FOUND\",\"field\":\"address\",\"message\":\"Address not found\",\"suggestion\":null}],\"details\":{\"latitude\":null,\"longitude\":null,\"time_zone\":null}},\"verify_carrier\":\"ups\"}}],\"has_more\":true}" + }, + "cookies": [], + "headers": [ + { + "name": "x-frame-options", + "value": "SAMEORIGIN" + }, + { + "name": "x-xss-protection", + "value": "1; mode=block" + }, + { + "name": "x-content-type-options", + "value": "nosniff" + }, + { + "name": "x-download-options", + "value": "noopen" + }, + { + "name": "x-permitted-cross-domain-policies", + "value": "none" + }, + { + "name": "referrer-policy", + "value": "strict-origin-when-cross-origin" + }, + { + "name": "x-ep-request-uuid", + "value": "16819988698e0e0ee787c502011c3f4b" + }, + { + "name": "cache-control", + "value": "private, no-cache, no-store" + }, + { + "name": "pragma", + "value": "no-cache" + }, + { + "name": "expires", + "value": "0" + }, + { + "name": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "name": "x-runtime", + "value": "0.040227" + }, + { + "name": "content-encoding", + "value": "gzip" + }, + { + "name": "transfer-encoding", + "value": "chunked" + }, + { + "name": "x-node", + "value": "bigweb57nuq" + }, + { + "name": "x-version-label", + "value": "easypost-202602121702-bfe72e7ac7-master" + }, + { + "name": "x-backend", + "value": "easypost" + }, + { + "name": "x-proxied", + "value": "intlb3nuq 0dcc3a6efb, extlb1nuq c01291cd8f" + }, + { + "name": "strict-transport-security", + "value": "max-age=31536000; includeSubDomains; preload" + }, + { + "name": "connection", + "value": "close" + } + ], + "headersSize": 710, + "httpVersion": "HTTP/1.1", + "redirectURL": "", + "status": 200, + "statusText": "OK" + }, + "startedDateTime": "2026-02-12T17:29:49.781Z", + "time": 334, + "timings": { + "blocked": -1, + "connect": -1, + "dns": -1, + "receive": 0, + "send": 0, + "ssl": -1, + "wait": 334 + } + } + ], + "pages": [], + "version": "1.2" + } +} diff --git a/test/services/easypost.test.js b/test/services/easypost.test.js index deee7abee..710905a00 100644 --- a/test/services/easypost.test.js +++ b/test/services/easypost.test.js @@ -147,4 +147,14 @@ describe('EasyPost', function () { expect(responseConfig1).to.be.null; expect(responseConfig2).to.be.null; }); + + it('makes an API call using the generic makeApiCall method', async function () { + const response = await client.makeApiCall(EasyPostClient.METHODS.GET, '/addresses', { + page_size: 1, + }); + + expect(response.addresses).to.be.an('array'); + expect(response.addresses.length).to.equal(1); + expect(response.addresses[0].object).to.equal('Address'); + }); }); diff --git a/types/EasyPost.d.ts b/types/EasyPost.d.ts index 2d5af80bc..06c905eeb 100644 --- a/types/EasyPost.d.ts +++ b/types/EasyPost.d.ts @@ -136,4 +136,16 @@ export default class EasyPost { * Clears all response hooks from the EasyPost client. */ public clearResponseHooks(): void; + /** + * Make an API call to the EasyPost API. + * + * This public, generic interface is useful for making arbitrary API calls to the EasyPost API that + * are not yet supported by the client library's services. When possible, the service for your use case + * should be used instead as it provides a more convenient and higher-level interface depending on the endpoint. + */ + public makeApiCall( + method: 'get' | 'post' | 'put' | 'patch' | 'del', + endpoint: string, + params?: Record, + ): Promise>; } From edbe8e26a378d7ba7839d49b0994e494ec605fb3 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:36:36 -0700 Subject: [PATCH 2/2] chore: cleanup --- .../recording.har | 14 +++++++------- test/services/easypost.test.js | 3 +-- types/EasyPost.d.ts | 6 ++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har b/test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har index fbae7f215..27359626b 100644 --- a/test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har +++ b/test/cassettes/EasyPost_3093958733/makes-an-API-call-using-the-generic-makeApiCall-method_2342601902/recording.har @@ -79,7 +79,7 @@ }, { "name": "x-ep-request-uuid", - "value": "16819988698e0e0ee787c502011c3f4b" + "value": "16819983698e0f99e787ddc1011e7834" }, { "name": "cache-control", @@ -99,7 +99,7 @@ }, { "name": "x-runtime", - "value": "0.040227" + "value": "0.036544" }, { "name": "content-encoding", @@ -111,7 +111,7 @@ }, { "name": "x-node", - "value": "bigweb57nuq" + "value": "bigweb56nuq" }, { "name": "x-version-label", @@ -123,7 +123,7 @@ }, { "name": "x-proxied", - "value": "intlb3nuq 0dcc3a6efb, extlb1nuq c01291cd8f" + "value": "intlb5nuq 0dcc3a6efb, extlb1nuq c01291cd8f" }, { "name": "strict-transport-security", @@ -140,8 +140,8 @@ "status": 200, "statusText": "OK" }, - "startedDateTime": "2026-02-12T17:29:49.781Z", - "time": 334, + "startedDateTime": "2026-02-12T17:36:25.762Z", + "time": 278, "timings": { "blocked": -1, "connect": -1, @@ -149,7 +149,7 @@ "receive": 0, "send": 0, "ssl": -1, - "wait": 334 + "wait": 278 } } ], diff --git a/test/services/easypost.test.js b/test/services/easypost.test.js index 710905a00..f726998af 100644 --- a/test/services/easypost.test.js +++ b/test/services/easypost.test.js @@ -149,11 +149,10 @@ describe('EasyPost', function () { }); it('makes an API call using the generic makeApiCall method', async function () { - const response = await client.makeApiCall(EasyPostClient.METHODS.GET, '/addresses', { + const response = await client.makeApiCall('get', '/addresses', { page_size: 1, }); - expect(response.addresses).to.be.an('array'); expect(response.addresses.length).to.equal(1); expect(response.addresses[0].object).to.equal('Address'); }); diff --git a/types/EasyPost.d.ts b/types/EasyPost.d.ts index 06c905eeb..c65f60f7b 100644 --- a/types/EasyPost.d.ts +++ b/types/EasyPost.d.ts @@ -116,26 +116,32 @@ export default class EasyPost { * Adds a request hook to the EasyPost client. Useful for logging or debugging. */ public addRequestHook(fn: (config: IEasyPostRequest) => void): void; + /** * Removes a request hook from the EasyPost client. */ public removeRequestHook(fn: (config: IEasyPostRequest) => void): void; + /** * Clears all request hooks from the EasyPost client. */ public clearRequestHooks(): void; + /** * Adds a response hook to the EasyPost client. Useful for logging or debugging. */ public addResponseHook(fn: (config: IEasyPostResponse) => void): void; + /** * Removes a response hook from the EasyPost client. */ public removeResponseHook(fn: (config: IEasyPostResponse) => void): void; + /** * Clears all response hooks from the EasyPost client. */ public clearResponseHooks(): void; + /** * Make an API call to the EasyPost API. *