react-native-nitro-fetch 1.0.2 → 1.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.
Files changed (2) hide show
  1. package/README.md +370 -0
  2. package/package.json +4 -2
package/README.md ADDED
@@ -0,0 +1,370 @@
1
+ <a href="https://margelo.com">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/margelo/react-native-nitro-fetch/main/assets/banner-dark.png" />
4
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/margelo/react-native-nitro-fetch/main/assets/banner-light.png" />
5
+ <img alt="Nitro Modules" src="https://raw.githubusercontent.com/margelo/react-native-nitro-fetch/main/assets/banner-light.png" />
6
+ </picture>
7
+ </a>
8
+
9
+ <br />
10
+
11
+
12
+
13
+ **react-native-nitro-fetch** is a general purpose network fetching library for React Native. It can be used as a drop-in replacement for the built-in `fetch(...)` method, as well as provide additional features like prefetching and workletized mappers.
14
+
15
+ <p align="center">
16
+ <a href="https://margelo.github.io/react-native-nitro-fetch/"><b>Documentation</b></a>
17
+ </p>
18
+
19
+ ## Features
20
+
21
+ - 🔧 Drop-in replacement for the built-in `fetch(...)` method
22
+ - ⚡️ Fast HTTP stack using [Cronet](https://chromium.googlesource.com/chromium/src/+/lkgr/components/cronet/README.md) on Android, and [URLSession](https://developer.apple.com/documentation/Foundation/URLSession) on iOS
23
+ - 💪 Supports HTTP/1, HTTP/2 and [HTTP/3](https://en.wikipedia.org/wiki/HTTP/3) over [QUIC](https://www.chromium.org/quic/), [Brotli](https://github.com/google/brotli), and disk cache
24
+ - ⏰ Prefetching on app-startup for even faster initialization
25
+ - 🧵 Worklet support for parallel data mapping without blocking the JS Thread
26
+ - 🔌 Optional **WebSockets** via [`react-native-nitro-websockets`](docs/websockets.md) (see [WebSockets & prewarm](#websockets--prewarm) below)
27
+ - 🔥 Powered by [Nitro Modules](https://github.com/mrousavy/nitro)
28
+
29
+ ## Installation
30
+
31
+ ```sh
32
+ npm i react-native-nitro-fetch react-native-nitro-modules
33
+ ```
34
+
35
+ > [Nitro Modules](https://github.com/mrousavy/nitro) requires react-native 0.75+ or higher
36
+
37
+ **WebSockets (optional)** — add the companion socket package plus **text decoder** (peer dependency of websockets):
38
+
39
+ ```sh
40
+ npm i react-native-nitro-websockets react-native-nitro-text-decoder
41
+ ```
42
+
43
+ Full setup, native hooks, prewarm, and API details: **[docs/websockets.md](docs/websockets.md)** · UI: [`example/src/screens/WebSocketScreen.tsx`](example/src/screens/WebSocketScreen.tsx) · auth + prewarm: [Token refresh](#token-refresh-cold-start) (example block).
44
+
45
+ ## Usage
46
+
47
+ To simply fetch data, import the `fetch(...)` method from `react-native-nitro-fetch`:
48
+
49
+ ```ts
50
+ import { fetch } from 'react-native-nitro-fetch'
51
+
52
+ const res = await fetch('https://httpbin.org/get')
53
+ const json = await res.json()
54
+ ```
55
+
56
+ This can be used as a drop-in-replacement for the built-in `fetch(...)` method.
57
+
58
+ ### Prefetching in JS
59
+
60
+ You can prefetch a URL in JS, which keeps the result cached for the next actual `fetch(...)` call - this can be used shortly before navigating to a new screen to have results hot & ready:
61
+
62
+ ```ts
63
+ import { prefetch } from 'react-native-nitro-fetch'
64
+
65
+ await prefetch('https://httpbin.org/uuid', {
66
+ headers: { prefetchKey: 'uuid' }
67
+ })
68
+ ```
69
+
70
+ Then, on the new screen that was navigated to:
71
+
72
+ ```ts
73
+ import { fetch } from 'react-native-nitro-fetch'
74
+
75
+ const res = await fetch('https://httpbin.org/uuid', {
76
+ headers: { prefetchKey: 'uuid' }
77
+ })
78
+ console.log('prefetched header:', res.headers.get('nitroPrefetched'))
79
+ ```
80
+
81
+ ### Prefetching for the next app launch
82
+
83
+ Prefetching data on app launch (or _process start_) will make it hot & ready once your JS code actually runs. Call `prefetchOnAppStart(...)` to enqueue a prefetch for the **next** app start:
84
+
85
+ ```ts
86
+ import { prefetchOnAppStart } from 'react-native-nitro-fetch'
87
+
88
+ await prefetchOnAppStart('https://httpbin.org/uuid', {
89
+ prefetchKey: 'uuid'
90
+ })
91
+ ```
92
+
93
+ Then, once the app opens the next time, a call to `fetch(...)` might resolve faster since it will contain already cached results:
94
+
95
+ ```ts
96
+ import { fetch } from 'react-native-nitro-fetch'
97
+
98
+ const res = await fetch('https://httpbin.org/uuid', {
99
+ headers: { prefetchKey: 'uuid' }
100
+ })
101
+ console.log('prefetched header:', res.headers.get('nitroPrefetched'))
102
+ ```
103
+
104
+ In our tests, prefetching alone yielded a **~220 ms** faster TTI (time-to-interactive) time! 🤯
105
+
106
+ ### Token refresh (cold start)
107
+
108
+ When you use **auto-prefetch** (`prefetchOnAppStart`) and/or **WebSocket prewarm on app start** (`react-native-nitro-websockets`), native code runs **before** your JS bundle. If those requests need auth headers, you can register a **token refresh** configuration. On each cold start, native code calls your refresh URL, maps the response into HTTP headers, and merges them into auto-prefetches and/or WebSocket prewarms.
109
+
110
+ **1. Register the refresh config** (persisted in encrypted native storage):
111
+
112
+ ```ts
113
+ import { registerTokenRefresh } from 'react-native-nitro-fetch'
114
+
115
+ registerTokenRefresh({
116
+ target: 'fetch', // 'websocket' | 'fetch' | 'all'
117
+ url: 'https://api.example.com/oauth/token',
118
+ method: 'POST',
119
+ headers: { 'Content-Type': 'application/json' },
120
+ body: JSON.stringify({ grant_type: 'client_credentials' }),
121
+ responseType: 'json',
122
+ mappings: [
123
+ { jsonPath: 'access_token', header: 'Authorization', valueTemplate: 'Bearer {{value}}' },
124
+ ],
125
+ // If the refresh request fails:
126
+ // - 'useStoredHeaders' — use last successful headers from the previous run (default)
127
+ // - 'skip' — skip auto-prefetch / prewarm entirely when refresh fails
128
+ onFailure: 'useStoredHeaders',
129
+ })
130
+ ```
131
+
132
+ **Response mapping**
133
+
134
+ - Default `responseType` is `'json'`. Use **`mappings`** to copy fields from the JSON body into header names (dot paths supported, e.g. `data.token`).
135
+ - Use **`compositeHeaders`** to build a header from a template and multiple JSON paths (`{{placeholder}}` in the template).
136
+ - For a plain-text body, set `responseType: 'text'` and use **`textHeader`** / optional **`textTemplate`** (with `{{value}}`).
137
+
138
+
139
+
140
+ **Example: token refresh + WebSocket prewarm**
141
+
142
+ ```ts
143
+ import { registerTokenRefresh } from 'react-native-nitro-fetch'
144
+ import { prewarmOnAppStart, NitroWebSocket } from 'react-native-nitro-websockets'
145
+
146
+ const WSS = 'wss://api.example.com/live'
147
+
148
+ registerTokenRefresh({
149
+ target: 'websocket', // use 'all' if you also use prefetchOnAppStart with the same token flow
150
+ url: 'https://api.example.com/oauth/token',
151
+ method: 'POST',
152
+ headers: { 'Content-Type': 'application/json' },
153
+ body: JSON.stringify({
154
+ grant_type: 'client_credentials',
155
+ client_id: '…',
156
+ client_secret: '…',
157
+ }),
158
+ mappings: [
159
+ { jsonPath: 'access_token', header: 'Authorization', valueTemplate: 'Bearer {{value}}' },
160
+ ],
161
+ })
162
+
163
+ ```
164
+
165
+ **3. Optional JS helpers**
166
+
167
+ ```ts
168
+ import {
169
+ callRefreshEndpoint,
170
+ clearTokenRefresh,
171
+ getStoredTokenRefreshConfig,
172
+ } from 'react-native-nitro-fetch'
173
+
174
+ // Same mapping rules as native; uses global fetch from JS
175
+ const headers = await callRefreshEndpoint(config)
176
+
177
+ // Remove stored config and token caches (scope with 'fetch' | 'websocket' | 'all')
178
+ clearTokenRefresh('fetch')
179
+
180
+ // Read back what was registered (or null)
181
+ const stored = getStoredTokenRefreshConfig('fetch')
182
+ ```
183
+
184
+ The refresh config and header caches are stored with **platform secure storage** (Android Keystore + encrypted values in `SharedPreferences`, iOS Keychain-backed encryption in the same `UserDefaults` suite as other nitro keys).
185
+
186
+ ### AbortController
187
+
188
+ Cancel in-flight requests using the standard `AbortController` API:
189
+
190
+ ```ts
191
+ import { fetch } from 'react-native-nitro-fetch'
192
+
193
+ const controller = new AbortController()
194
+
195
+ // Abort after 500ms
196
+ setTimeout(() => controller.abort(), 500)
197
+
198
+ try {
199
+ const res = await fetch('https://httpbin.org/delay/20', {
200
+ signal: controller.signal,
201
+ })
202
+ } catch (e) {
203
+ if (e.name === 'AbortError') {
204
+ console.log('Request was cancelled')
205
+ }
206
+ }
207
+ ```
208
+
209
+ Pre-aborted signals are also supported — the request will throw immediately without making a network call:
210
+
211
+ ```ts
212
+ const controller = new AbortController()
213
+ controller.abort()
214
+
215
+ await fetch(url, { signal: controller.signal }) // throws AbortError
216
+ ```
217
+
218
+ ### FormData
219
+
220
+ Upload files and form fields using `FormData`:
221
+
222
+ ```ts
223
+ import { fetch } from 'react-native-nitro-fetch'
224
+
225
+ const fd = new FormData()
226
+ fd.append('username', 'nitro_user')
227
+ fd.append('avatar', {
228
+ uri: 'file:///path/to/photo.jpg',
229
+ type: 'image/jpeg',
230
+ name: 'avatar.jpg',
231
+ } as any)
232
+
233
+ const res = await fetch('https://httpbin.org/post', {
234
+ method: 'POST',
235
+ body: fd,
236
+ })
237
+ const json = await res.json()
238
+ ```
239
+
240
+ ### Worklet Mapping
241
+
242
+ Since Nitro Fetch is a [Nitro Module](https://nitro.margelo.com), it can be used from Worklets.
243
+ This can be useful to parse data without blocking the main JS-Thread:
244
+
245
+ ```ts
246
+ import { nitroFetchOnWorklet } from 'react-native-nitro-fetch'
247
+
248
+ const data = await nitroFetchOnWorklet(
249
+ 'https://httpbin.org/get',
250
+ undefined,
251
+ (payload) => {
252
+ 'worklet'
253
+ return JSON.parse(payload.bodyString ?? '{}')
254
+ }
255
+ )
256
+ ```
257
+ Before using worklet mapping, install and configure [react-native-worklets](https://docs.swmansion.com/react-native-worklets/docs/).
258
+
259
+ ### Streaming with `TextDecoder`
260
+
261
+ Nitro Fetch can also expose an streaming mode that returns a `ReadableStream` body.
262
+ Combined with [`react-native-nitro-text-decoder`](https://github.com/margelo/react-native-nitro-fetch/tree/main/packages/react-native-nitro-text-decoder), you can incrementally decode UTF‑8 chunks:
263
+
264
+ ```tsx
265
+ import { useRef, useState } from 'react'
266
+ import { fetch as nitroFetch } from 'react-native-nitro-fetch'
267
+ import { TextDecoder } from 'react-native-nitro-text-decoder'
268
+
269
+ export function StreamingExample() {
270
+ const [output, setOutput] = useState('')
271
+ const decoder = useRef(new TextDecoder())
272
+
273
+ const append = (text: string) => {
274
+ setOutput(prev => prev + text)
275
+ }
276
+
277
+ const runStream = async () => {
278
+ // `stream: true` enables the streaming transport
279
+ const res = await nitroFetch('https://httpbin.org/stream/20', {
280
+ stream: true,
281
+ })
282
+
283
+ const reader = res.body?.getReader()
284
+ if (!reader) {
285
+ append('No readable stream!')
286
+ return
287
+ }
288
+
289
+ let chunks = 0
290
+ while (true) {
291
+ const { done, value } = await reader.read()
292
+ if (done) break
293
+ chunks++
294
+ const text = decoder.current.decode(value, { stream: true })
295
+ append(text)
296
+ }
297
+
298
+ append(`\n\n✅ Done — ${chunks} chunk(s) received`)
299
+ }
300
+
301
+ // Call `runStream()` from a button handler in your UI
302
+ }
303
+ ```
304
+
305
+ ### WebSockets & prewarm
306
+
307
+ Use **[react-native-nitro-websockets](docs/websockets.md)** for `NitroWebSocket` (browser-like API: `onopen`, `onmessage`, `send`, `close`, …). Install **`react-native-nitro-text-decoder`** alongside it — the socket package uses it to decode UTF-8 text frames.
308
+
309
+ **Prewarm on next launch** — queue URLs from JS so native code can start the handshake before React loads:
310
+
311
+ ```ts
312
+ import {
313
+ prewarmOnAppStart,
314
+ removeFromPrewarmQueue,
315
+ clearPrewarmQueue,
316
+ } from 'react-native-nitro-websockets'
317
+
318
+ prewarmOnAppStart('wss://echo.websocket.org')
319
+ // optional: prewarmOnAppStart(url, ['subproto'], { Authorization: 'Bearer …' })
320
+
321
+ clearPrewarmQueue()
322
+ removeFromPrewarmQueue('wss://echo.websocket.org')
323
+ ```
324
+
325
+ On **Android**, call `NitroWebSocketAutoPrewarmer.prewarmOnStart(this)` in `Application.onCreate` (see [example `MainApplication.kt`](example/android/app/src/main/java/nitrofetch/example/MainApplication.kt)). **iOS** picks up the queue via the linked pod.
326
+
327
+ Authenticated prewarms: use **`registerTokenRefresh`** with `target: 'websocket'` or `'all'` — see [Token refresh (cold start)](#token-refresh-cold-start) for a **small `registerTokenRefresh` + `prewarmOnAppStart` + `NitroWebSocket` example**.
328
+
329
+ More detail: **[docs/websockets.md](docs/websockets.md)** · UI sample: **[example/src/screens/WebSocketScreen.tsx](example/src/screens/WebSocketScreen.tsx)**.
330
+
331
+ ## Limitations & Alternatives
332
+
333
+ - **WebSockets** are not part of `react-native-nitro-fetch` itself; use the companion package **[react-native-nitro-websockets](docs/websockets.md)** (with **react-native-nitro-text-decoder**). For other stacks, [react-native-fast-io](https://github.com/callstackincubator/react-native-fast-io) is another option.
334
+
335
+ ## Documentation
336
+
337
+ - [Getting Started](docs/getting-started.md)
338
+ - [API Reference](docs/api.md)
339
+ - [Android Details](docs/android.md)
340
+ - [iOS Details](docs/ios.md)
341
+ - [Prefetch & Auto-Prefetch](docs/prefetch.md)
342
+ - [WebSockets & prewarm](docs/websockets.md)
343
+ - [Worklets](docs/worklets.md)
344
+ - [Troubleshooting](docs/troubleshooting.md)
345
+ - [Cronet (Android) notes](docs/cronet-android.md)
346
+ - [Cronet (iOS) notes](docs/cronet-ios.md)
347
+
348
+ ## Margelo
349
+
350
+ Nitro Fetch is built with ❤️ by Margelo.
351
+ We build fast and beautiful apps. Contact us at [margelo.com](https://margelo.com) for high-end consultancy services.
352
+
353
+ ## Contributing
354
+
355
+ - Development workflow: `CONTRIBUTING.md#development-workflow`
356
+ - Sending a pull request: `CONTRIBUTING.md#sending-a-pull-request`
357
+ - Code of conduct: `CODE_OF_CONDUCT.md`
358
+
359
+ ## Authors
360
+
361
+ - [Szymon Kapala](https://github.com/Szymon20000)
362
+ - [Alex Shumihin](https://github.com/pioner92)
363
+ - [Ronald Goedeke](https://github.com/ronickg)
364
+ - [Marc Rousavy](https://github.com/mrousavy)
365
+ - [Ritesh Shukla](https://github.com/riteshshukla04)
366
+
367
+ ## License
368
+
369
+ MIT
370
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-fetch",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Awesome Fetch :)",
5
5
  "main": "./lib/module/index.js",
6
6
  "module": "./lib/module/index.js",
@@ -47,7 +47,9 @@
47
47
  "prepare": "bob build",
48
48
  "build:plugin": "cd expo/plugins && npx tsc",
49
49
  "nitrogen": "nitrogen",
50
- "release": "npm run build:plugin && release-it --only-version"
50
+ "release": "npm run build:plugin && release-it --only-version",
51
+ "prepack": "cp ../../README.md ./README.md",
52
+ "postpack": "rm -f ./README.md"
51
53
  },
52
54
  "keywords": [
53
55
  "react-native",