Update mixfetch example
This commit is contained in:
@@ -153,115 +153,116 @@ export const MixFetch = () => {
|
||||
error: `Error: ${errorMsg}`,
|
||||
};
|
||||
const statusColor: Record<typeof status, string> = {
|
||||
idle: "gray",
|
||||
idle: "#9e9e9e",
|
||||
starting: "orange",
|
||||
ready: "green",
|
||||
error: "red",
|
||||
ready: "#85E89D",
|
||||
error: "#ff6b6b",
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: "1rem" }}>
|
||||
{/* --- Start MixFetch Section --- */}
|
||||
<Paper sx={{ p: 2, mb: 2 }} variant="outlined">
|
||||
<Stack direction="row" alignItems="center" spacing={2}>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={status === "starting" || status === "ready"}
|
||||
onClick={handleStart}
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<Paper sx={{ p: 3 }}>
|
||||
<Stack spacing={3}>
|
||||
{/* --- Start MixFetch Section --- */}
|
||||
<Stack direction="row" alignItems="center" spacing={2}>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={status === "starting" || status === "ready"}
|
||||
onClick={handleStart}
|
||||
>
|
||||
Start MixFetch
|
||||
</Button>
|
||||
{status === "starting" && <CircularProgress size={20} />}
|
||||
<Typography
|
||||
fontFamily="monospace"
|
||||
fontSize="small"
|
||||
sx={{ color: statusColor[status] }}
|
||||
>
|
||||
{statusText[status]}
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
{/* --- Fetch Controls (disabled until ready) --- */}
|
||||
<Box
|
||||
sx={{
|
||||
opacity: isReady ? 1 : 0.5,
|
||||
pointerEvents: isReady ? "auto" : "none",
|
||||
}}
|
||||
>
|
||||
Start MixFetch
|
||||
</Button>
|
||||
{status === "starting" && <CircularProgress size={20} />}
|
||||
<Typography
|
||||
fontFamily="monospace"
|
||||
fontSize="small"
|
||||
sx={{ color: statusColor[status] }}
|
||||
>
|
||||
{statusText[status]}
|
||||
</Typography>
|
||||
{/* Single fetch */}
|
||||
<Stack direction="row" spacing={2}>
|
||||
<TextField
|
||||
disabled={busy}
|
||||
fullWidth
|
||||
label="URL"
|
||||
type="text"
|
||||
variant="outlined"
|
||||
defaultValue={defaultUrl}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
size="small"
|
||||
/>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={busy}
|
||||
onClick={handleFetch}
|
||||
>
|
||||
Fetch
|
||||
</Button>
|
||||
</Stack>
|
||||
{busy && (
|
||||
<Box mt={2}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)}
|
||||
{html && (
|
||||
<>
|
||||
<Box mt={2}>
|
||||
<strong>Response</strong>
|
||||
</Box>
|
||||
<Paper sx={{ p: 2, mt: 1 }} elevation={4}>
|
||||
<Typography fontFamily="monospace" fontSize="small">
|
||||
{html}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Concurrent fetch demo */}
|
||||
<Box mt={3}>
|
||||
<strong>Concurrent Requests</strong>
|
||||
<Box mt={1}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={concurrentBusy}
|
||||
onClick={handleConcurrentFetch}
|
||||
>
|
||||
Send 5 Concurrent Requests (posts/1-5)
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{concurrentBusy && (
|
||||
<Box mt={2}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)}
|
||||
{concurrentResults.length > 0 && (
|
||||
<Paper sx={{ p: 2, mt: 2 }} elevation={4}>
|
||||
{concurrentResults.map((result, i) => (
|
||||
<Typography key={i} fontFamily="monospace" fontSize="small">
|
||||
{result}
|
||||
</Typography>
|
||||
))}
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* --- Fetch Controls (disabled until ready) --- */}
|
||||
<Box
|
||||
sx={{
|
||||
opacity: isReady ? 1 : 0.5,
|
||||
pointerEvents: isReady ? "auto" : "none",
|
||||
}}
|
||||
>
|
||||
{/* Single fetch */}
|
||||
<Stack direction="row">
|
||||
<TextField
|
||||
disabled={busy}
|
||||
fullWidth
|
||||
label="URL"
|
||||
type="text"
|
||||
variant="outlined"
|
||||
defaultValue={defaultUrl}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={busy}
|
||||
sx={{ marginLeft: "1rem" }}
|
||||
onClick={handleFetch}
|
||||
>
|
||||
Fetch
|
||||
</Button>
|
||||
</Stack>
|
||||
{busy && (
|
||||
<Box mt={2}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)}
|
||||
{html && (
|
||||
<>
|
||||
<Box mt={2}>
|
||||
<strong>Response</strong>
|
||||
</Box>
|
||||
<Paper sx={{ p: 2, mt: 1 }} elevation={4}>
|
||||
<Typography fontFamily="monospace" fontSize="small">
|
||||
{html}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Concurrent fetch demo */}
|
||||
<Box mt={3}>
|
||||
<strong>Concurrent Requests</strong>
|
||||
<Box mt={1}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
disabled={concurrentBusy}
|
||||
onClick={handleConcurrentFetch}
|
||||
>
|
||||
Send 5 Concurrent Requests (posts/1-5)
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{concurrentBusy && (
|
||||
<Box mt={2}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
)}
|
||||
{concurrentResults.length > 0 && (
|
||||
<Paper sx={{ p: 2, mt: 2 }} elevation={4}>
|
||||
{concurrentResults.map((result, i) => (
|
||||
<Typography key={i} fontFamily="monospace" fontSize="small">
|
||||
{result}
|
||||
</Typography>
|
||||
))}
|
||||
</Paper>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* --- Log Panel --- */}
|
||||
{logs.length > 0 && (
|
||||
<Paper
|
||||
ref={logContainerRef}
|
||||
sx={{ p: 2, mt: 3, maxHeight: 200, overflow: "auto" }}
|
||||
variant="outlined"
|
||||
sx={{ p: 2, mt: 2, maxHeight: 200, overflow: "auto" }}
|
||||
>
|
||||
<strong>Log</strong>
|
||||
{logs.map((entry, i) => (
|
||||
@@ -276,6 +277,6 @@ export const MixFetch = () => {
|
||||
))}
|
||||
</Paper>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "mixFetch Example: Private HTTP Requests"
|
||||
description: "Replace browser fetch with mixFetch to route HTTP requests through the Nym mixnet. Covers setup, CA certificates, WSS gateways, and usage examples."
|
||||
description: "Replace browser fetch with mixFetch to route HTTP requests through the Nym mixnet. Covers setup, CA certificates, TLS configuration, and usage examples."
|
||||
schemaType: "TechArticle"
|
||||
section: "Developers"
|
||||
lastUpdated: "2026-03-15"
|
||||
@@ -14,13 +14,9 @@ An easy way to secure parts or all of your web app is to replace calls to [`fetc
|
||||
|
||||
Things to be aware of:
|
||||
|
||||
- CA certificates in `mixFetch` are periodically updated. If you get a certificate error, the root certificate you need might not be valid yet. [Send a PR](https://github.com/nymtech/nym/pulls) if you need changes to the certificates.
|
||||
- If you are using `mixFetch` in a web app with HTTPS, you will need to use a gateway that has Secure Websockets (WSS) to avoid a [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) error.
|
||||
- `mixFetch` supports concurrent requests (up to 10) to the same or different URLs.
|
||||
|
||||
<Callout type="info">
|
||||
Right now Gateways are not required to run a Secure Websocket (WSS) listener, so only a subset of nodes running in Gateway mode have configured their nodes to do so. You need to select a Gateway that has WSS from [Harbourmaster](https://harbourmaster.nymtech.net/).
|
||||
</Callout>
|
||||
- **CA certificates** are bundled into the WASM binary at build time. They're updated with each SDK release — if you hit a certificate error, update to the latest `@nymproject/mix-fetch-full-fat` version.
|
||||
- **HTTPS and WSS.** When serving your app over HTTPS, the mixnet connection must also use Secure WebSockets to avoid a [mixed content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) error. Set `forceTls: true` in your `SetupMixFetchOps` config (see below) and the SDK will automatically select a WSS-capable gateway.
|
||||
- `mixFetch` supports **concurrent requests** (up to 10) to the same or different URLs.
|
||||
|
||||
## Environment Setup
|
||||
|
||||
@@ -41,30 +37,34 @@ npm run dev
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @nymproject/mix-fetch-full-fat
|
||||
npm install @nymproject/mix-fetch-full-fat @mui/material @emotion/react @emotion/styled
|
||||
```
|
||||
|
||||
The MUI packages are used by the example UI below. If you only need `mixFetch` itself, install just `@nymproject/mix-fetch-full-fat`.
|
||||
|
||||
## Configuration
|
||||
|
||||
```ts
|
||||
import type { SetupMixFetchOps } from '@nymproject/mix-fetch-full-fat';
|
||||
|
||||
const mixFetchOptions: SetupMixFetchOps = {
|
||||
clientId: "docs-mixfetch-demo",
|
||||
clientId: "my-app",
|
||||
preferredGateway: "q2A2cbooyC16YJzvdYaSMH9X3cSiieZNtfBr8cE8Fi1",
|
||||
mixFetchOverride: {
|
||||
requestTimeoutMs: 60_000,
|
||||
},
|
||||
forceTls: true, // force WSS
|
||||
forceTls: true, // use Secure WebSockets (required when serving over HTTPS)
|
||||
};
|
||||
```
|
||||
|
||||
`preferredGateway` is optional — if omitted, the SDK auto-selects a gateway. You can pin a specific one via [Harbourmaster](https://harbourmaster.nymtech.net/).
|
||||
|
||||
## Full Example
|
||||
|
||||
This example shows explicit initialization via `createMixFetch`, single URL fetch, and concurrent requests. Results appear both in the UI and in a visible log panel.
|
||||
|
||||
<Callout type="info">
|
||||
For this example we use the `full-fat` version of the ESM SDK. If you use the unbundled ESM variant, make sure your [bundler configuration](../bundling/bundling) copies the WASM and web worker files to the output bundle.
|
||||
For this example we use the `full-fat` version of the ESM SDK. If you use the unbundled ESM variant, make sure your [bundler configuration](/developers/typescript/bundling/bundling) copies the WASM and web worker files to the output bundle.
|
||||
</Callout>
|
||||
|
||||
```tsx
|
||||
|
||||
Reference in New Issue
Block a user