Parse raw body on cloudflare-worker service(Non NODE) - javascript

I've created an api server'ish environment on cloudflare using cloudflare-worker and there is no node server running(cloudflare-worker is pretty much serverless event handler service). It does provide the configurations to handle any subdomain calls much like how api works. I've used a package called cf-worker-router to do so.
My cloud service looks like this:
import { ApiError, ApiRedirect, DomainRouter, FetchRouter } from 'cf-worker-router';
const router = new FetchRouter();
// add the cloudflare event listener
addEventListener('fetch', (event) => {
router.onFetch(event);
});
router.route('/users', 'POST', async (event) => {
// automatically converts anything not of Response type to ApiResponse
return await event.request.text();
});
And what I did was create a POST request to the url and supplied some body to the request. I was able to get the request text successfully but now I can't figure out how to parse the text I received.
When using the request as multipart/form-data request and the received body text is as follows:
"----------------------------093590450792211419875705\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nJon Doe\r\n----------------------------093590450792211419875705--\r\n"
I tried sending application/x-www-form-urlencoded and I the response text as such:
"name=Jon%20Doe"
And Similar for application/json request:
"{\n\t\"name\": \"Jon Doe\"\n}"
Since cloudflare is not using nodejs server, body-parser can't be applied here. This service is pretty much an open api so it needs to take care of all sorts of request content types. Is there any way to identify and decode the strignified contents from any of these content types to a valid object in javascript?

To handle form data uploads, you can use the request.formData() method which will return a promise of a FormData object.
For example:
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const formData = await request.formData();
const name = formData.get('name');
return new Response(`Hello ${name}`);
}

Related

sveltekit fetch function with ajax requests

In my sveltekit app I make AJAX calls to my api endpoints. For example:
+page.svelte
<script>
async function get_card() {
const url = '/api/card/?category=' + $page.params.slug;
const response = await fetch(url, {
method: 'GET',
})
const card = await response.json();
return card;
}
</script>
In the browser javascript console I get this warning:
Loading /api/card/?category=Neurology using `window.fetch`.
For best results, use the `fetch` that is passed to your `load`
function: https://kit.svelte.dev/docs/load#making-fetch-requests
But as far as I can tell, that fetch function is only accessible to me on the server, and I do not see a way to use it in a script that may run on the client (such as +page.svelte). I tried passing the function as part of the data object from load:
+layout.server.js
export const load = async ({ fetch, locals }) => {
return {
email: locals.user.email,
group: locals.user.group,
fetch: fetch
}
}
But, not surprisingly, that does not work since the function is not serializable.
Am I Doing It Wrong™, or should I just ignore the warning?
fetch is originally a browser API and SvelteKit defines it on the server as well, if it does not exist. The warning is there to tell you that you are creating another round trip to the server (one for the page and one for the data) when you possibly could have loaded the data on the server so it could be transmitted as part of the page (during server-side rendering).
If the code of your function is not executed right away, then this is a false positive (recent issue on this). I.e. if the data should be requested at a significantly later point, there is no way to bundle the request with the page.
(You are definitely not meant to pass on the fetch of load, you are supposed to use it to get the data.)

Getting Console Messages on Webpage NodeJS

I'm wondering if there's any way to listen for console messages and act on console messages when they're received. Mainly, is there any way to do this without an external module, and using the http module?
The goal is to trigger a NodeJS function or code snippet on an event like click in the HTML. If there's also a way to do this, then that's great. But once again, I'd like to do this without an external module, and just use those that are built-in to NodeJS.
Use onclick() function in JavaScript to trigger a function call when clicking on a element. Then use fetch to make a api call to the nodejs server.
I know #Haris Wilson already got the answer, but I'd just like to provide a code example.
Instead of trying to catch a console message and then execute a function if we find it, we can use fetch() to make a request to whatever URL we need, and this can allow us to make other requests.
In this case, we can use the url module and the http module to parse the url and serve the API and website, respectively.
const url = require('url')
const http = require('http')
const requestListener = async function (req, res) {
// Basic server setup
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.end(/** Content here */)
// API
if (url.parse(req.url, true).pathname === '/APIcall') {
let arguments = url.parse(req.url, true).query
// Preform necassary actions here
}
}
We can now use onClick to call a function inside our webpage JavaScript, and use fetch([API URL]) to give our NodeJS data to preform an action. We can use URL params to do this, such as https://localhost:8080/APIcall?data=someData&moreParam=more-data, where ?data=someData&moreParam=more-data are the URL params.

How do I stream JSON objects from ExpressJS?

I'm trying to stream JSON objects from an ExpressJS / Node backend API to a frontend site.
I do not want to use Sockets.IO for various reasons. As I understand it, the native streaming libraries should support streaming objects, it appears that just Express is complicating this.
My frontend code seams straight forward. I use Fetch to get my target URL, get a read stream from the response object, and set that read stream to objectMode: true.
Frontend Example:
async function () {
let url = "myurl";
let response = await fetch( url, {
method: 'GET',
mode: 'cors',
wtihCredentials: 'include'
}
const reader = response.body.getReader({objectMode: true });
// Where things are a bit ambiguous
let x = true;
while (x) {
const {done, value} = reader.read()
if (done) { break; }
// do something with value ( I push it to an array )
}
}
Backend Bode Example ( fails because of I cannot change the stream to objectMode )
router.get('/', (request, response) => {
response.writeHead(200, { 'Content-Type' : 'application/json' });
MongoDB.connection.db.collection('myCollection').find({}).forEach( (i) => {
response.write(i);
}).then( () => {
response.end()
})
})
Now my problem is that there does not appear to be anyway to change the ExpressJS write stream to objectMode: true. To my dismay, the ExpressJS documentation doesn't even acknoledge the existence of the write() function on the response object: https://expressjs.com/en/api.html#res
How do I change this over to objectMode: true ?
conversely, I tried to work with the writeStream as a string. The problem that I run into is that when the send buffer fills up, it does it by characters, not by the object. These means that at some point invalid JSON is passed to requester.
A suggested solution that I run into often is that I could read all of the chunks on the client and assemble valid JSON. This defeats the purpose of streaming, so Im trying to find a better way.
For what I believe is the same problem, I cannot figure out how to talk directly to the write stream object from the express code so I am unable to use the native writeStream operation writable.length in order to manually check to see if there is space for the entire JSON object as a string. This is preventing me from using stringified JSON with new line terminators.
https://nodejs.org/api/stream.html#stream_writable_writablelength
https://nodejs.org/api/stream.html#stream_writable_writableobjectmode
Could someone set me straight? I am working with 100k + records in my Mongo database, I really need partical page loading to work so that the users can start picking through the data.

Sending a file to the client from Node.js with Express

I have a unique situation in terms of difficulty.
I need to send HTML to the server, have the server convert the HTML to a PDF, send that PDF back to the client, and then download the PDF using client-side code.
I have to do it this way because I'm using client-side routing, so the only way I can access my endpoint that should perform this action is via a GET Request with Ajax or Fetch from client-side JavaScript. I am aware of res.sendFile(), but that attempts to render the file in the browser - I don't want that - rather, I want to be able to use client-side code to download the file.
Is it possible, then, to send a PDF file from temporary storage on the server down to the client, allowing client-side code to do whatever it wants to the file thereafter - in my case, downloading it?
I don't believe I have to provide any code because this is more of a theoretical question.
My issue stemmed from the fact that I could not just use res.sendFile() or res.download() from Express because the route was not being accessed by the browser URL bar, rather, my application uses client-side routing, and thus I had to make an HTTP GET Request via Fetch or XMLHttpRequest.
The second issue is that I needed to build the PDF file on the server based on an HTML string sent from the client - so again, I need to make a GET Request sending along a request body.
My solution, then, using Fetch, was to make the Get Request from the client:
fetch('/route' , {
method: 'GET',
body: 'My HTML String'
});
On the server, I have my code that converts the HTML string to a PDF, using the HTML-PDF Node module, and then, I convert that file to a Base64 String, setting the MIME Type and appending data:application/pdf;base64,.
app.get('/route', (req, res) => {
// Use req.body to build and save PDF to temp storage (os.tempdir())
// ...
fs.readFile('./myPDF.pdf', (err, data) => {
if (err) res.status(500).send(err);
res.contentType('application/pdf')
.send(`data:application/pdf;base64,${new Buffer.from(data).toString('base64')}`);
});
});
Back on the client, I have my aforementioned Fetch Request, meaning I just need to tack on the promise to get the response:
fetch('/route', {
method: 'POST',
body: 'My HTML String' // Would define object and stringify.
})
.then(res => res.text())
.then(base64String => {
// Now I just need to download the base64String as a PDF.
});
To make the download, I dynamically create an anchor tag, set its href attribute to the Base64 String in the response from the server, give it a title, and then programmatically click it:
const anchorTag = document.createElement('a');
anchorTag.href = base64String;
anchorTag.download = "My PDF File.pdf";
anchorTag.click();
So, all together and on the client:
fetch('/route', {
method: 'POST',
body: 'My HTML String' // Would define object and stringify.
})
.then(res => res.text())
.then(base64String => {
const anchorTag = document.createElement('a');
anchorTag.href = base64String;
anchorTag.download = "My PDF File.pdf";
anchorTag.click();
});
The solution for using an anchor tag to trigger the download came from another StackOverflow answer. It's also important to note that Base64 Encoding is not very efficient. Better solutions exist, but for my purposes, Base64 will work fine.
It is also imperative to note that Base64 Encoding is precisely that - an Encoding Scheme, not, I repeat, not an Encryption Scheme. So if your PDF files contain privileged information, you would likely want to add token authentication to the endpoint and encrypt the file.

Node gRPC: sending metadata from server to client without error

From the client side, it is easy to add metadata for the server:
const meta = new grpc.Metadata();
meta.add('xyz', 'okay');
stub.service.Rpc(request, meta, (err, response) => {
});
The above can be accessed on the server like this:
call.metadata.get('xyz');
Now, if we need to send metadata from the server to the client, we do this:
const err = { code, details };
const meta = new grpc.Metadata();
meta.add('...', '...');
callback(err, null, meta);
Note that we are passing error, and the actual response is null.
How do I pass a null error and a non-null response, along with metadata?
If I do the following, it does not seem to work as there is no way to access the metadata on the client without the error.
callback(null, r, meta);
// `r` is some response message
Does gRPC spec explicitly disallow sending metadata from server to client when there is no error?
Also, while we're at it, I'd like someone explain how do we send trailing vs initial metadata from server to client in Node.
Relevant links:
https://github.com/grpc/grpc-node
Can I send a custom Error message from server to client GRPC?
How to add metadata to nodejs grpc call
https://github.com/grpc/grpc/issues/9053
https://medium.com/compli-engineering/grpc-nodejs-using-jwt-authentication-b048fef6ecb2
ServerUnaryCall.sendMetadata(responseMetadata)
server:
const method = (call, cb) => {
// code
call.sendMetadata(metadata)
// code
}
client:
const call = client.method(params, cb)
call.on('metadata', (metadata) => {
// code
})
Looks like you can use such code:
client.someFunction().on('metadata', (meta) => { /* any code */ })
At least on v0.9.x you can see: https://github.com/grpc/grpc-node/blob/v1.9.x/packages/grpc-native-core/src/client.js#L562

Categories

Resources