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