I am building a simple NEST api, just to learn stuff.
In it there is a service which has a method called ‘getTestFileAsJSON’. I am trying to build this api for a blog. There will be articles, which are files in the .txt format.
I am trying to write a method which will read said text files and return JSON as apis should, best on best practices. From what I have read depending on the size of the files ( esp. if they are huge) streams are a good idea.
I was searching around SO for resolutions or more information and finally after several threads decided on writing my method like so:
async getTestFileAsJSON() {
const text = { value: '' };
const testFileAsReadableStream = createReadStream(
join(process.cwd(), '/files/test.txt'),
);
const streamToString = async (stream) => {
const chunks = [];
for await (const chunk of stream) {
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks).toString('utf-8');
};
const myText = await streamToString(testFileAsReadableStream);
console.log(myText);
text.value = myText;
return JSON.stringify(text);
}
This gets the job done and I get my response. This method is async. But in my controller I do not await it. Does this mean that even thought I have used async/await nothing truly async is happening within the code and so this is why everything is working even though I am not awaiting it in the controller?:
@Get('json-file')
getStreamFile() {
return this.blogService.getTestFileAsJSON();
}
>Solution :
The method you’ve posted does indeed use asynchronous operations, and it is correct to use async/await in the code where you are interacting with asynchronous operations like reading a file stream. However, there’s an important distinction to understand between the synchronous and asynchronous aspects of your code.
In your getTestFileAsJSON() method, the usage of await within the streamToString() function ensures that the myText variable is populated asynchronously after reading the file stream. This means that the asynchronous part is properly handled in this method, allowing it to work correctly even if you don’t await it in the controller method.
The controller method getStreamFile() returns a promise that will resolve to the result of this.blogService.getTestFileAsJSON(). This means the actual processing and handling of the file stream is asynchronous, and your API will handle other requests while waiting for the file reading to complete. So, even if you don’t await the result in the controller, the code is still asynchronous and the request handling will not be blocked.
However, you should note that in your current code, you’re returning a JSON string in your service method, and not a JavaScript object. It would be a better practice to return a JSON object directly from the service method and let the Nest framework handle the serialization to JSON when sending the response.
Here’s a modified version of your code:
async getTestFileAsJSON() {
const testFileAsReadableStream = createReadStream(
join(process.cwd(), '/files/test.txt'),
);
const streamToString = async (stream) => {
const chunks = [];
for await (const chunk of stream) {
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks).toString('utf-8');
};
const myText = await streamToString(testFileAsReadableStream);
return { value: myText }; // Return an object, not a JSON string
}
@Get('json-file')
async getStreamFile() {
return this.blogService.getTestFileAsJSON();
}
In this modified version, the getTestFileAsJSON() method returns an object, which will be automatically serialized to JSON when it’s sent as the response. This is more aligned with the standard behavior in Nest.js and REST APIs in general.