Created
January 18, 2025 04:55
-
-
Save VldMrgnn/954b79e6d334d2ae8b11339d3a2b5998 to your computer and use it in GitHub Desktop.
Starfx Middleware for fetching data in main thread from the backend through the worker.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Showcase Example: Fetching Data via a Worker | |
* | |
* In this example, data is stored and fetched from a worker. | |
* It mimics the classic fetch behavior but instead passes the request through a worker. | |
* At request time, you can enhance the logic (e.g., add additional hashes) | |
* and at response time, perform parsing or other heavy-lifting operations. | |
*/ | |
import { nanoid } from 'nanoid'; | |
import { ApiCtx, Next, Ok, Result, safe } from 'starfx'; | |
import { isOk } from '../helpers'; | |
// this is part of @effection-contrib/websocket package but not exported as standalone | |
import { withResolvers } from '../helpers/with-resolvers'; | |
//my cache validation worker | |
import { getVacacheWorker } from '../workers/worker-factory-vacache'; | |
export type WApiCtx = ApiCtx & { workerPayload?: any }; | |
export /** | |
* This middleware sends a fetch-like request to the worker. | |
* The worker will fetch the data and return it to the main thread. | |
* @export | |
* @param {WApiCtx} ctx | |
* @param {Next} next | |
* @returns {{}} | |
*/ function* workerFetch(ctx: WApiCtx, next: Next) { | |
const { url, ...options } = ctx.req(); | |
const worker = getVacacheWorker(); | |
if (!worker) { | |
throw new Error("Worker not started"); | |
} | |
const resolver = withResolvers<Result<Response>>(); | |
const id = `${Date.now()}-${Math.random()}-${nanoid()}`; | |
const handleMessage = (event: MessageEvent) => { | |
if (event.data?.id === id) { | |
worker.removeEventListener("message", handleMessage); | |
if (event.data.error) { | |
resolver.reject(new Error(event.data.error)); | |
return; | |
} | |
const { response } = event.data; | |
// Handle the stream response | |
if (response.body instanceof ReadableStream) { | |
const reader = response.body.getReader(); | |
const chunks: any[] = []; | |
const processChunks = () => | |
reader.read().then(({ done, value }) => { | |
if (done) { | |
// Construct response only if the status allows a body | |
const options: ResponseInit = { status: response.status }; | |
if (response.headers) { | |
options.headers = new Headers(response.headers); | |
} | |
const body = [204, 205, 304].includes(response.status) | |
? null // No body for these status codes | |
: JSON.stringify(chunks); | |
resolver.resolve(Ok(new Response(body, options))); | |
} else { | |
chunks.push(value); | |
return processChunks(); | |
} | |
}); | |
processChunks(); | |
} else { | |
// Handle non-stream responses | |
const options: ResponseInit = { status: response.status }; | |
if (response.headers) { | |
options.headers = new Headers(response.headers); | |
} | |
const body = [204, 205, 304].includes(response.status) | |
? null // No body for these status codes | |
: JSON.stringify(response.body); | |
resolver.resolve(Ok(new Response(body, options))); | |
} | |
} | |
}; | |
worker.addEventListener("message", handleMessage); | |
worker.postMessage({ id, url, options }); | |
const res = yield* resolver.operation; | |
ctx.response = isOk(res) | |
? res.value | |
: new Response(JSON.stringify({ error: res.error }), { status: 500 }); | |
ctx.json = yield* safe(() => ctx.response.json()); | |
yield* next(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A few notes on usage of the workersfetch middleware
The flow now goes like this:
Main Thread Setup
Configure your API with the required middleware, including the custom
workerFetch
.Thunk Example on Main Thread
Define a thunk to trigger the GET request through the worker and process the returned response.
Worker Thunk for Data Processing
The worker receives the request, processes data, and sends the result back. We set up a thunk middleware in worker too.
Sending the Response from the Worker
Return the processed result to the main thread—either as a complete response or a stream.
Helper Functions for Streaming
Create and send a readable stream from an array of data.