
スワックは次のとおりです。
この README は進行中の作業です。 Twitterでも質問していただけます。
$ npm i thwackまたは
$ yarn add thwack Axiosは当時リリースされたときは素晴らしかったです。 XMLHttpRequestを囲む Promise ベースのラッパーが提供されましたが、これは使いにくかったです。しかし、それは遠い昔のことであり、時代は変わりました。ブラウザはより賢くなっています。おそらく、データ取得ソリューションが追いつく時期が来たのではないでしょうか?
Thwack は、最新のブラウザを念頭に置いてゼロから構築されました。このため、Axios のような荷物はありません。 Axios は、gzip 圧縮された状態で約 5,000 の重さになります。一方、Thwack は 1.5k までの細身です。
これらは同じ API をサポートしていますが、主にoptionsに関していくつかの違いがありますが、ほとんどの場合、多くのアプリケーションで互換的に使用できるはずです。
Thwack は、Axios のようにすべての問題を解決しようとするのではなく、ユーザーが本当に必要とするものの 98% に対するソリューションを提供します。これが、Thwack のフットプリントを羽のように軽くする理由です。
それをスクラッチします。 Thwack は、はるかに小さい設置面積で、Axios と同じレベルのパワーを提供します。また、Thwack の Promise ベースのイベント システムは使いやすくなっています。
次のメソッドは、すべての Thwack インスタンスで使用できます。
thwack(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.request(options: ThwackOptions): Promise<ThwackResponse>
thwack.get(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.delete(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.head(url: string [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.post(url: string, data:any [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.put(url: string, data:any [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.patch(url: string, data:any [,options: ThwackOptions]): Promise<ThwackResponse>;
thwack.create(options: ThwackOptions): ThwackInstance;
createメソッドは、指定されたoptionsを使用して、現在の Thwack インスタンスの新しい子インスタンスを作成 (da!) します。
thwack.getUri(options: ThwackOptions): string;
Thwacks URL 解決は RFC-3986 に準拠しています。 Axios はそうではありません。 @thwack/resolveによって強化されています。
Thwack は、 request 、 response 、 data 、およびerrorのイベント タイプをサポートします。
Thwack のイベント システムの詳細については、以下の Thwack イベントを参照してください。
thwack.addEventListener(type: string, callback: (event:ThwackEvent) => Promise<any> ): void;
thwack.removeEventListener(type: string, callback: (event:ThwackEvent) => Promise<any> ): void;
Thwack には、同時リクエストを行うための次のヘルパー関数があります。これらは主に Axios との互換性のためのものです。使用例については、以下の「使い方」セクションを参照してください。
thwack.all(Promise<ThwackResponse>[])
thwack.spread(callback<results>)
options引数には次のプロパティがあります。
urlこれは完全修飾 URL または相対 URL です。
baseURL上記のurlから完全修飾 URL を構築するために使用されるベース URL を定義します。絶対 URL またはundefinedある必要があります。ブラウザで実行している場合、またはノードまたは React Native でundefined場合、デフォルトは現在の Web ページのorigin + pathnameになります。
たとえば、次のようにしたとします。
thwack ( 'foo' , {
baseURL : 'http://example.com' ,
} ) ;取得された URL は次のようになります。
http://example.com/foo
method次の HTTP メソッドのいずれかを含む文字列: get 、 post 、 put 、 patch 、 delete 、またはhead 。
data methodがpost 、 put 、またはpatchの場合、これはリクエスト本文の構築に使用されるデータです。
headersここに、オプションの HTTP リクエスト ヘッダーを配置できます。ここで指定したヘッダーはすべて、インスタンスのヘッダー値とマージされます。
たとえば、Thwack インスタンスを次のように設定するとします。
const api = thwack . create ( {
headers : {
'x-app-name' : 'My Awesome App' ,
} ,
} ) ;その後、インスタンスを使用するときに、次のような呼び出しを行います。
const { data } = await api . get ( 'foo' , {
headers : {
'some-other-header' : 'My Awesome App' ,
} ,
} ) ;送信されるヘッダーは次のとおりです。
x-app-name: My Awesome App
some-other-header': 'My Awesome App'
defaultsこれにより、このインスタンス、および実際にはすべての子インスタンスのデフォルト オプションを読み取り/設定できるようになります。
例:
thwack . defaults . baseURL = 'https://example.com/api' ;たとえば、 defaults createに渡されるオブジェクトと同じです。たとえば、次の場合は「https://example.com/api」が出力されます。
const instance = thwack . create ( {
baseURL : 'https://example.com/api' ,
} ) ;
console . log ( instance . defaults . baseURL ) ;また、インスタンスのdefaults設定しても (またはインスタンスにoptionsを渡しても)、親には影響しないことにも注意してください。したがって、次の例では、 thwack.defaults.baseURL引き続き「https://api1.example.net/」になります。
thwack . defaults . baseURL = 'https://api1.example.net/' ;
const instance = thwack . create ( ) ;
instance . defaults . baseURL = 'https://example.com/api' ;
console . log ( thwack . defaults . baseURL ) ;paramsこれは、フェッチ URL の構築に使用されるキーと値のペアを含むオプションのオブジェクトです。 baseURLまたはurlに:keyセグメントがある場合、それらは一致するキーの値に置き換えられます。たとえば、次のようにしたとします。
thwack ( 'orders/:id' , {
params : { id : 123 } ,
baseURL : 'http://example.com' ,
} ) ;取得された URL は次のようになります。
http://example.com/orders/123
:nameを指定しない場合、または:nameよりも多くのparamがある場合、残りのキー/値が検索パラメータとして設定されます (つまり、 ?key=value )。
maxDepth Thwack がエラーをスローする前に callbck で実行できる再帰リクエストの最大レベル。これは、適切な保護手段が設定されていない状態で別のrequestが発行された場合に、イベント コールバックによって再帰ループが発生するのを防ぐために使用されます。デフォルト = 3。
responseTypeデフォルトでは、Thwack は応答ヘッダーcontent-typeの値に基づいて応答データをデコードする方法を自動的に決定します。ただし、サーバーが間違った値で応答した場合は、 responseType設定することでパーサーをオーバーライドできます。有効な値は、 arraybuffer 、 document (つまり、 formdata )、 json 、 text 、 stream 、およびblobです。デフォルトは自動です。
Thwack によって返される内容は、次の表によって決まります。 「フェッチメソッド」列はdataで解決されるものです。 responseTypeを指定しない場合、Thwack はcontent-typeとresponseParserMapテーブルに基づいてフェッチ メソッドを自動的に決定します (以下を参照)。
| コンテンツタイプ | responseType | fetchメソッド |
|---|---|---|
application/json | json | response.json() |
multipart/form-data | formdata | response.formData() |
text/event-stream | stream | 処理せずにresponse.body dataとして返す |
blob | response.blob() | |
arraybuffer | response.arrayBuffer() | |
*/* | text | response.text() |
注: #27741 のため、
stream現在 React Native でサポートされていません
responseParserMapどの応答パーサーを使用するかを決定するもう 1 つの便利な方法は、 responseParserMapを使用することです。これにより、応答ヘッダーから得られるcontent-typeとパーサー タイプの間のマッピングを設定できます。
Thwack はデフォルトとして次のマップを使用します。これにより、 jsonとformdataデコードが可能になります。一致するものがない場合、応答パーサーはデフォルトでtextを使用します。特別な*/*キーを設定することでデフォルトを指定できます。
{
"application/json" : " json " ,
"multipart/form-data" : " formdata " ,
"*/*" : " text "
} ; responseParserMapで指定した値はすべて、デフォルトのマップにマージされます。つまり、デフォルトをオーバーライドしたり、新しい値を追加したりできます。
たとえば、画像を BLOB にダウンロードするとします。 baseURL API エンドポイントと、あらゆる種類の画像を BLOB としてダウンロードするresponseParserMapに設定できますが、 jsonダウンロードも許可します ( content-type: application/jsonのデフォルトであるため)。
import thwack from 'thwack' ;
thwack . defaults . responseParserMap = { 'image/*' : 'blob' } ; image/*コンテンツ タイプ ( image/jpeg 、 image/pngなど) でダウンロードした URL は、 blobパーサーで解析されます。
const getBlobUrl = async ( url ) => {
const blob = ( await thwack . get ( url ) ) . data ;
const objectURL = URL . createObjectURL ( blob ) ;
return objectURL ;
} ;CodeSandbox で実行されているこの例を参照してください。
このテクニックは画像以外にも使用できることに注意してください。
ご覧のとおり、 responseParserMapの使用は、さまざまな Thwack 呼び出しに対してresponseType設定する必要性を排除する優れた方法です。
validateStatusこのオプションの関数は、Thwack が Promise または Throw を返すためにどのステータス コードを使用するかを決定するために使用されます。応答statusが渡されます。この関数が true を返した場合、promise は解決され、そうでない場合、promise は拒否されます。
デフォルトの関数は、2xx (つまり 200 ~ 299) にないステータスに対してスローします。
paramsSerializerこれは、Thwack がparamsシリアル化するために呼び出すオプションの関数です。たとえば、オブジェクト{a:1, b:2, foo: 'bar'}の場合、文字列a=1&b=2&foo=barにシリアル化する必要があります。
ほとんどの人にとって、デフォルトのシリアライザーは問題なく動作するはずです。これは主にエッジケースと Axios の互換性のためです。
デフォルトのシリアライザーはパラメーターをアルファベット順に並べていることに注意してください。これに従うことをお勧めします。ただし、これが状況で機能しない場合は、独自のシリアライザーをロールすることができます。
resolverこれは、デフォルトのリゾルバー動作をオーバーライドするために提供できる関数です。 resolver 2 つの引数を取ります。URL と、未定義urlある必要があるbaseURL 、または絶対 URL です。レゾルバーを交換する理由はほとんどありませんが、これは必要な場合の避難口です。
status受信した 3 桁の HTTP ステータス コードを表すnumber 。
ok true に設定されたbooleanは、2xx 範囲のstatusコード (つまり、成功) です。この値はvalidateStatusの影響を受けません。
statusText statusコードのテキストを表すstring 。プログラム ロジックではstatusコード (またはok ) を使用する必要があります。
headers返された HTTP ヘッダーを含むキー/値オブジェクト。重複するヘッダーはセミコロンで区切られて 1 つのヘッダーに連結されます。
dataこれは、ストリーミングおよび変換後に返された HTTP 応答の本文を保持します。唯一の例外は、 streamのresponseTypeを使用した場合です。この場合、 data body要素に直接設定されます。
ThwackResponseErrorがスローされた場合、 data応答本文のプレーン テキスト表現になります。
optionsリクエストを処理した完全なoptionsオブジェクト。このoptions 、 defaultsと同様に、親インスタンスと完全にマージされます。
response fetchによって返される完全な HTTP Responseオブジェクト、または合成イベント コールバックからのresponse 。
Thwack リクエストからの応答の結果が 2xx 以外のstatusコード (例: 404 Not Found) になった場合、 ThwackResponseErrorがスローされます。
注: 他のタイプのエラー (不正なイベント リスナー コールバックなど) がスローされる可能性があるため、捕捉されたエラーを問い合わせて、それが
ThwackResponseErrorタイプであるかどうかを確認することをお勧めします。
try {
const { data } = await thwack . get ( someUrl )
} catch ( ex ) {
if ( ex instanceof thwack . ThwackResponseError )
const { status , message } = ex ;
console . log ( `Thwack status ${ status } : ${ message } ` ) ;
} else {
throw ex ; // If not, rethrow the error
}
} ThwackResponseError通常の JavaScript Errorのすべてのプロパティに加えて、成功ステータスと同じプロパティを持つthwackResponseプロパティが含まれます。
Thwack で作成されたインスタンスは、親インスタンスに基づいています。親のデフォルト オプションはインスタンスを通じて継承されます。これは、子に影響を与える可能性のある親でのオプションbaseURLなど) を設定する場合に便利です。
逆に、親はaddEventListener使用して子供を監視できます (この例については、以下の「すべての API 呼び出しをログに記録する方法」を参照してください)。

Thwack イベント システムをインスタンスと組み合わせることで、Thwack は非常に強力になります。これを使用すると、さまざまなイベントを聞くことができます。
全イベントの流れはこちらです。ご覧のとおり、コールバックがrequest()をすでに実行しているかどうかを確認せずにやみくもに発行すると、コードが無限ループに陥る可能性があるため、注意が必要です。

requestイベントはアプリケーションのいずれかの部分がデータ取得メソッドの 1 つを呼び出すたびに、 requestイベントが発生します。すべてのリスナーは、 event.optionsに呼び出しのoptionsを持つThwackRequestEventオブジェクトを取得します。これらのイベント リスナーは、(イベントをログに記録する) ような単純なことも、(モック データ) でリクエストを阻止して応答を返すような複雑なことも行うことができます。
// callback will be called for every request made in Thwack
thwack . addEventListener ( 'request' , callback ) ;コールバックは
asyncすることができ、Thwack を延期できるため、たとえば、続行する前に別の URL でデータをフェッチすることができることに注意してください。
responseイベントこのイベントは、HTTP ヘッダーを受信した後、本文がストリーミングされて解析される前に発生します。リスナーは、応答に設定されたthwackResponseキーを持つThwackResponseEventオブジェクトを受け取ります。
dataイベントこのイベントは、本体がストリーミングされて解析された後に発生します。これは、フェッチが 2xx ステータス コードを返した場合にのみ発生します。リスナーは、応答に設定されたthwackResponseキーを持つThwackDataEventオブジェクトを受け取ります。
errorイベントこのイベントは、本体がストリーミングされて解析された後に発生します。フェッチが 2xx 以外のステータス コードを返した場合に発生します。リスナーは、応答に設定されたthwackResponseキーを持つThwackErrorEventオブジェクトを受け取ります。
Thwack は NodeJS 上で動作しますが、 window.fetch用のポリフィルが必要です。幸いなことに、使用できるnode-fetchと呼ばれる素晴らしいポリフィルがあります。
NodeJS バージョン 10 を使用している場合は、 Array#flatおよびObject#fromEntriesのポリフィルも必要になります。 NodeJS バージョン 11 以降にはこれらのメソッドがあり、ポリフィルは必要ありません。
これらのポリフィルを自分で提供することも、代わりに次の便利なインポートのいずれかを使用することもできます。 NodeJS 11 以降を実行している場合は、次を使用します。
import thwack from 'thwack/node' ; // NodeJS version 12+NodeJS 10 で実行している場合は、次を使用します。
import thwack from 'thwack/node10' ; // NodeJS version 10これらのポリフィルを自分で提供したい場合、Thwack を使用するには、 thwack/coreからインポートし、 fetch fetchのデフォルトとして設定する必要があります。
import thwack from 'thwack/code' ;
thwack . defaults . fetch = global . fetch ;これはアプリの起動コード (通常はindex.jsで行う必要があります。
注:
blobのresponseTypeは NodeJS ではサポートされていません。
Thwack は React Native と互換性があり、追加のポリフィルは必要ありません。 React Native で作成されたサンプル アプリについては、以下を参照してください。
注: #27741 のため、React Native は
streamをサポートしていません
thwack.all()とthwack.spread()使用して、同時リクエストを行うことができます。データは 1 つの配列としてコールバックに渡されます。
ここでは 2 人の GitHub ユーザーの情報を表示します。
function displayGitHubUsers ( ) {
return thwack
. all ( [
thwack . get ( 'https://api.github.com/users/donavon' ) ,
thwack . get ( 'https://api.github.com/users/revelcw' ) ,
] )
. then (
thwack . spread ( ( ... results ) => {
const output = results
. map (
( { data } ) => ` ${ data . login } has ${ data . public_repos } public repos`
)
. join ( 'n' ) ;
console . log ( output ) ;
} )
) ;
}これらは単なるヘルパー関数であることに注意してください。 async / awaitを使用している場合は、 Promise.all使用して Thwack ヘルパーなしでこれを書くことができます。
async function displayGitHubUsers ( ) {
const results = await Promise . all ( [
thwack . get ( 'https://api.github.com/users/donavon' ) ,
thwack . get ( 'https://api.github.com/users/revelcw' ) ,
] ) ;
const output = results
. map ( ( { data } ) => ` ${ data . login } has ${ data . public_repos } public repos` )
. join ( 'n' ) ;
console . log ( output ) ;
}これは CodeSandbox でライブで実行されているのを確認できます。
(デモは axios/fetch に関するこの blob 投稿からインスピレーションを受けています)
AbortControllerを使用して、そのsignal thwackオプションに渡してリクエストをキャンセルします。
ブラウザでは、組み込みの AbortController を使用できます。
import thwack from 'thwack' ;
const controller = new AbortController ( ) ;
const { signal } = controller ;
thwack ( url , { signal } ) . then ( handleResponse ) . catch ( handleError ) ;
controller . abort ( ) ;NodeJS では、abort-controller のようなものを使用できます。
import thwack from 'thwack' ;
import AbortController from 'abort-controller' ;
const controller = new AbortController ( ) ;
const { signal } = controller ;
thwack ( url , { signal } ) . then ( handleResponse ) . catch ( handleError ) ;
controller . abort ( ) ;リクエストのキャンセル時に何らかのアクションを実行したい場合は、 signalでabortイベントをリッスンすることもできます。
signal . addEventListener ( 'abort' , handleAbort ) ;addEventListener('request', callback)を追加し、各リクエストをコンソールに記録します。
import thwack from 'thwack' ;
thwack . addEventListener ( 'request' , ( event ) => {
console . log ( 'hitting URL' , thwack . getUri ( event . options ) ) ;
} ) ;React を使用している場合は、同じことを実現するアプリで「使用」できるフックを次に示します。
import { useEffect } from 'react' ;
import thwack from 'thwack' ;
const logUrl = ( event ) => {
const { options } = event ;
const fullyQualifiedUrl = thwack . getUri ( options ) ;
console . log ( `hitting ${ fullyQualifiedUrl } ` ) ;
} ;
const useThwackLogger = ( ) => {
useEffect ( ( ) => {
thwack . addEventListener ( 'request' , logUrl ) ;
return ( ) => thwack . removeEventListener ( 'request' , logUrl ) ;
} , [ ] ) ;
} ;
export default useThwackLogger ;これを使用する方法のコードスニペットを次に示します。
const App = ( ) = {
useThwackLogger ( )
return (
< div >
...
</ div >
)
}いくつかのユーザー データをリクエストしたアプリがあるとします。アプリが特定の URL (たとえばusers ) にヒットし、特定のユーザー ID (たとえば123 ) をクエリしている場合、リクエストがサーバーにヒットするのを防ぎ、代わりに結果を模擬したいと考えます。
ThwackResponseのstatusのデフォルトは 200 なので、OK 以外の応答を模擬する必要がない限り、 data返すだけで済みます。
thwack . addEventListener ( 'request' , async ( event ) => {
const { options } = event ;
if ( options . url === 'users' && options . params . id === 123 ) {
// tells Thwack to use the returned value instead of handling the event itself
event . preventDefault ( ) ;
// stop other listeners (if any) from further processing
event . stopPropagation ( ) ;
// because we called `preventDefault` above, the caller's request
// will be resolved to this `ThwackResponse` (defaults to status of 200 and ok)
return new thwack . ThwackResponse (
{
data : {
name : 'Fake Username' ,
email : '[email protected]' ,
} ,
} ,
options
) ;
}
} ) ;多くの場合、DTO (データ転送オブジェクト) をクライアントが利用しやすいものに変換することが望まれます。以下の例では、複雑な DTO をfirstName 、 lastName 、 avatar 、およびemailに変換します。 API 呼び出しから返されるが、アプリケーションには必要のないその他のデータ要素は無視されます。
このサンプル アプリでは、DTO 変換、ログ記録、および偽のデータを返す例を確認できます。

CodeSandbox でソースコードを表示できます。
この例では、画像を BLOB URL としてロードする React Hook があります。 URL から BLOB URL へのマッピングをセッション ストレージにキャッシュします。ロードされると、ページを更新すると、Blob URL から画像が即座にロードされます。
const useBlobUrl = ( imageUrl ) => {
const [ objectURL , setObjectURL ] = useState ( '' ) ;
useEffect ( ( ) => {
let url = sessionStorage . getItem ( imageUrl ) ;
async function fetchData ( ) {
if ( ! url ) {
const { data } = await thwack . get ( imageUrl , {
responseType : 'blob' ,
} ) ;
url = URL . createObjectURL ( data ) ;
sessionStorage . setItem ( imageUrl , url ) ;
}
setObjectURL ( url ) ;
}
fetchData ( ) ;
} , [ imageUrl ] ) ;
return objectURL ;
} ;CodeSandbox のこの例を参照してください
現在、 https://api.example.comに REST エンドポイントがあります。新しい REST エンドポイントを別の URL に公開し、ネットワーク トラフィックの 2% をこれらの新しいサーバーにゆっくりルーティングし始めたいとします。
注: 通常、これはバックエンドのロード バランサーによって処理されます。ここではデモンストレーションのみを目的として示しています。
これは、リクエスト イベント リスナーのoptions.url次のように置き換えることで実現できます。
thwack . addEventListener ( 'request' , ( event ) => {
if ( Math . random ( ) >= 0.02 ) {
return ;
}
// the code will be executed for approximately 2% of the requests
const { options } = event ;
const oldUrl = thwack . getUri ( options ) ;
const url = new URL ( '' , oldUrl ) ;
url . origin = 'https://api2.example.com' ; // point the origin at the new servers
const newUrl = url . href ; // Get the fully qualified URL
event . options = { ... event . options , url : newUrl } ; // replace `options`]
} ) ;use-thwackと合わせて、React Native 用のデータ取得アプリを作成するのはこれ以上に簡単です。
Expo で実行されているアプリ全体を表示します。

Thwack は Axios から大きな影響を受けています。マットさん、ありがとう!
MITのもとでライセンスを取得
これらの素晴らしい人々に感謝します (絵文字キー):
ドナボン・ウェスト ? | ジェレミー・タイス | ユライマ・エステベス | ジェレミー・バーガー | ブルック・スカーレット・ヤロフ | カール・ホーキー | こうじ |
トム・バイラー | イアン・サザーランド | ブレイク・ヨーダー | ライアン・ヒンチー | ミロ・ドジッチ | サンチセビッチ |
このプロジェクトは、全員参加者の仕様に従っています。あらゆる種類の貢献を歓迎します!