Trying to bring clarity to this issue. I need to get any attachment file from another back end and return in the nestjs controller.
Service code:
getFile(token: string, orderId: number, fileName: string): Observable<any> {
let url = `${this.configService.get<string>(
'BACKEND_ORDER_BASE_URL',
)}/file/download`;
if (orderId) {
url += `?serviceDistrictId=${orderId}`;
}
if (fileName) {
url += `?fileName=${orderId}`;
}
return this.httpService.get(url, {headers:{Authorization: token
}});
}
Controller code:
#Get('download')
#UseGuards(JwtAuthGuard)
getFile(
#Query('fileName') fileName: string,
#Query('orderId') orderId: number,
#AuthHeader() bearerToken: string,
#Res() res: Response): Observable<any>
{
return this.fileService.getFile(bearerToken, orderId, fileName)
.pipe(
map(response => response.data.pipe(res))
);
}
I get an error when running it: ERROR [ExceptionsHandler] response.data.pipe is not a function TypeError: response.data.pipe is not a function.
Can anybody help with the code solution that gets any file from another back end and returns it in nestjs controller?
Much Thanks
Related
I need dynamically assign a new route but it for some reason refuses to work.
When I send a request in the Postman it just keeps waiting for a response
The whole picture of what I am doing is the following:
I've got a controller with a decorator on one of its methods
#Controller()
export class Test {
#RESTful({
endpoint: '/product/test',
method: 'post',
})
async testMe() {
return {
type: 'hi'
}
}
}
export function RESTful({ endpoint, method, version }: { endpoint: string, version?: string, method: HTTPMethodTypes }) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): void {
const originalMethod = descriptor.value
Reflect.defineMetadata(propertyKey, {
endpoint,
method,
propertyKey,
version
}, target)
return originalMethod
}
}
export function Controller() {
return function (constructor: any) {
const methods = Object.getOwnPropertyNames(constructor.prototype)
Container.set(constructor)
for (let action of methods) {
const route: RESTfulRoute = Reflect.getMetadata(action, constructor.prototype)
if (route) {
const version: string = route.version ? `/${route.version}` : '/v1'
Container.get(Express).injectRoute((instance: Application) => {
instance[route.method](`/api${version}${route.endpoint}`, async () => {
return await Reflect.getOwnPropertyDescriptor(constructor, route.propertyKey)
// return await constructor.prototype[route.propertyKey](req, res)
})
})
}
}
}
}
Is it possible to dynamically set the route in the way?
I mainly use GraphQL but sometimes I need RESTful API too. So, I want to solve this by that decorator
In order for the response to finish, there must be a res.end() or res.json(...) or similar. But I cannot see that anywhere in your code.
I need to download all xlsx files from myFolder I have two methods getExcelSheets to get all excel sheets list
then using this information to download the file using #microsoft.graph.downloadUrl by calling getFileContentById method but I failed to get and convert it to xlsx format
import { Client } from "#microsoft/microsoft-graph-client";
import axios from "axios";
import * as MicrosoftGraph from "#microsoft/microsoft-graph-types";
export async function getExcelSheets(
template_name: string,
msgraph_client: Client,
): Promise<MicrosoftGraph.DriveItem[]> {
const result = await msgraph_client
.api(
`drives/{driver_id}/root:/myFolder/${template_name}:/children`
)
.get();
return result.value as MicrosoftGraph.DriveItem[];
}
export async function getFileContentById(download_url: string): Promise<any> {
const response = await axios.get(download_url);
return response;
}
any hint on how I get the file then convert it to xlsx I was using this method to convert the buffer to xlsx
xlsx.read(file, { type: "buffer" })
The Axios call you are making in getFileContentById will receive a stream that you can write to a file or convert to xlsx like below,
export async function getFileContentById(download_url: string): Promise<any> {
const writer = fs.createWriteStream('file.xlsx');
return axios({
method: 'get',
url: download_url,
responseType: 'stream',
}).then(response => {
// Save the file and read later
response.data.pipe(writer);
// OR Convert the binary to xlsx usibng the library
const sheet = XLSX.read(response.data, { type: "buffer" });
console.log(sheet)
/*
{
SheetNames: [ 'Sheet1' ],
Sheets: { Sheet1: { A1: [Object], '!ref': 'A1' } }
}
*/
});
}
Currently I'm writing code for request login to server and receive it's session data and send to global context.
Basic principle is 1)Request and get Promise 2)Validate fetch result itself and response status. 3)Provide value to external component. And I'm working on 1) and 2).
But I got an error about data typing Type 'Promise<any>' is missing the following properties from type 'SessionInfo': userEmail, userName, sessionToken, duets(2739) at code that returns result data to external components. Despite of strict data typing(Maybe I think), I'm not sure why linter says Promise not Promise>. I think TS fails to assert it's type.
When I run very similar code with Javascript(without typing), it works in past. I'm not sure why this happens and I don't know what's wrong. Can you check my code?
Codes are below, there's 4 files -- interface definition file related to User, interface definition for handling response json, Actual request fetch, Response validation and evaluation.
When I checked linting at return res.data at actionHandler.ts, linter succeed to predict it's type. res.data is ResponseSet<SessionInfo>.data?: userTypes.SessionInfo as linter said.
In userTypes.ts
export interface SessionInfo {
userEmail: string,
userName: string,
sessionToken: string,
due: number,
}
In commonTypes.ts
export interface ResponseSet<T> { // Response wrapper when it returns with code 200
statusCode: ResponseStatusCode, // ResponseStatusCode is custom typed status code not for request it self.
data?: T,
description?: string,
};
In userReq.ts
const login = async (email: string, password: string): Promise<commonTypes.ResponseSet<userTypes.SessionInfo>> => {
try {
const request: Request = new Request(
composeUri(`user/login`, { email, password }),
{
method: 'GET',
headers: { 'Content-type': 'application/json' },
mode: 'cors',
}
);
const response: Response = await fetch(request);
if (response.status != 200) throw response.status;
return await response.json();
} catch {
return {
statusCode: 1,
};
}
}
In actionHandler.ts
export const doLogin = (email: string, password: string): userTypes.SessionInfo => {
const result: userTypes.SessionInfo = userReq.login(email, password)
.then(res => {
if (res.statusCode != 0) throw new Error(res.description || 'UnknownError');
return res.data;
})
.catch(err => {
return null;
});
return result;
}
Where I got an error is const result:.... I got Type 'Promise<any>' is missing the following properties from type 'SessionInfo': userEmail, userName, sessionToken, due ts(2739). I'm not sure why it is recognized as 'Promise` despite of strict type definition of my code.
the issue is that result isn't SessionInfo. It is a Promise of it.
const result: Promisse<userTypes.SessionInfo | null>;
doLogin is async due to used promise, you should follow async await inside and it can't return userTypes.SessionInfo, result will be a promise.
export const doLogin = async (email: string, password: string): Promise<userTypes.SessionInfo | null> => {
try {
const result: commonTypes.ResponseSet<userTypes.SessionInfo> = await userReq.login(email, password);
if (res.statusCode != 0) throw new Error(res.description || 'UnknownError');
} catch (e) {
return null;
}
return res.data;
}
// somewhere in the code (async env)
await doLogin(email, password);
I Found The Tutorial about
Designing a clean REST API with Node.js (Express + Mongo)
project in github.
but the problem is i didn't get the concept of routing in one part.
the misundrestanding part is how is it possible to pass httpRequest data to handle method within contact-endpoint module?
because handle method is in here export default function makeContactsEndpointHandler({ contactList }) {
return async function handle(httpRequest) {
this is the index of project:
import handleContactsRequest from "./contacts";
import adaptRequest from "./helpers/adapt-request";
app.all("/contacts", contactsController);
app.get("/contacts/:id", contactsController);
function contactsController(req, res) {
const httpRequest = adaptRequest(req);
handleContactsRequest(httpRequest)
.then(({ headers, statusCode, data }) =>
res.set(headers).status(statusCode).send(data)
)
.catch((e) => res.status(500).end());
}
this is the adaptRequest:
export default function adaptRequest (req = {}) {
return Object.freeze({
path: req.path,
method: req.method,
pathParams: req.params,
queryParams: req.query,
body: req.body
})
}
this is the handleContactsRequest module:
import makeDb from "../db";
import makeContactList from "./contact-list";
import makeContactsEndpointHandler from "./contacts-endpoint";
const database = makeDb();
const contactList = makeContactList({ database });
const contactsEndpointHandler = makeContactsEndpointHandler({ contactList });
export default contactsEndpointHandler;
this is part of contact-endpoint module:
export default function makeContactsEndpointHandler({ contactList }) {
return async function handle(httpRequest) {
switch (httpRequest.method) {
case "POST":
return postContact(httpRequest);
case "GET":
return getContacts(httpRequest);
default:
return makeHttpError({
statusCode: 405,
errorMessage: `${httpRequest.method} method not allowed.`,
});
}
}
makeContactsEndpointHandler is a function that returns a function (async handle(xxx)).
In handleContactsRequest, we export the result of the call: makeContactsEndpointHandler({ contactList }). Which is therefore the function async handle(xxx) itself.
So, in index, when we call handleContactsRequest with the constant httpRequest as argument, we're actually calling that handle(xxx) function. (I wrote xxx as parameter name to highlight the difference between the two httpRequest declarations.)
I want to see if I can create a stack based on both, CDN and also angular 2 universal. So when the user navigate has the CDN to get the assets, and if the user access the first time will have the complete html rendered by Universal.
I was thinking in:
Client <===> Akamai <===> Varnish <===> Origin Server (node.js with universal)
This sounds good? have you ever tried it?
Also i'm considering adding nginx and ELB for the complete stack.
The question is:
- Can this stack work as expected?
Yes it can be done! The big issue is how do you invalidate an arbitrary number of http requests made in Angular that determine the rendered page. Using some sort of header schema to invalidate might be helpful.
Assuming your are using official ng-express engine, a service like this could let you define the response from the Angular runtime:
import { RESPONSE } from '#nguniversal/express-engine/tokens'
import { Inject, Injectable, Optional } from '#angular/core'
import { Response } from 'express'
export interface IServerResponseService {
getHeader(key: string): string
setHeader(key: string, value: string): this
setHeaders(dictionary: { [key: string]: string }): this
appendHeader(key: string, value: string, delimiter?: string): this
setStatus(code: number, message?: string): this
setNotFound(message?: string): this
setError(message?: string): this
}
#Injectable()
export class ServerResponseService implements IServerResponseService {
private response: Response
constructor(#Optional() #Inject(RESPONSE) res: any) {
this.response = res
}
getHeader(key: string): string {
return this.response.getHeader(key)
}
setHeader(key: string, value: string): this {
if (this.response)
this.response.header(key, value)
return this
}
appendHeader(key: string, value: string, delimiter = ','): this {
if (this.response) {
const current = this.getHeader(key)
if (!current) return this.setHeader(key, value)
const newValue = [...current.split(delimiter), value]
.filter((el, i, a) => i === a.indexOf(el))
.join(delimiter)
this.response.header(key, newValue)
}
return this
}
setHeaders(dictionary: { [key: string]: string }): this {
if (this.response)
Object.keys(dictionary).forEach(key => this.setHeader(key, dictionary[key]))
return this
}
setStatus(code: number, message?: string): this {
if (this.response) {
this.response.statusCode = code
if (message)
this.response.statusMessage = message
}
return this
}
setNotFound(message = 'not found'): this {
if (this.response) {
this.response.statusCode = 404
this.response.statusMessage = message
}
return this
}
setError(message = 'internal server error'): this {
if (this.response) {
this.response.statusCode = 500
this.response.statusMessage = message
}
return this
}
}