How I can use sendFile in fastify? - fastify

Server.ts
import fastify from "fastify";
import cookie from 'fastify-cookie';
import apiRoute from './routes/api';
import jwtPlugin from "./plugins/jwtPlugin";
import closePlugin from "./plugins/closePlugin";
import path from "path";
const PORT = parseInt(process.env.PORT!, 10)
export default class Server {
app = fastify({ logger: true })
constructor() {
this.setup()
}
setup() {
this.app.get('/', (request, reply) => {
reply.send({ hello: 'world' })
})
this.app.register(apiRoute, { prefix: '/api' })
this.app.register(cookie)
this.app.register(require('fastify-url-data'))
this.app.register(jwtPlugin)
this.app.register(closePlugin)
this.app.setErrorHandler((error, request, reply) => {
reply.send({
statusCode: error.statusCode,
name: error.name,
message: error.message,
validation: error.validation,
stack: error.stack,
})
})
this.app.register(require('fastify-rate-limit'), {
max: 100,
timeWindow: '1 minute'
})
this.app.register(require('fastify-static'), {
root: path.join(__dirname, 'public')
})
}
version/index.ts
const versionRoute: FastifyPluginCallback = (fastify, opts, done) => {
//Todo 1. get version of app
//Define request body to fastify
fastify.post(
//Route
'/version_info',
async (request, reply) => {
try {
const result: VersionBody[] = await Version.getVersionInfo("testServer")
reply.send(result[0])
} catch (error) {
reply.status(500)
reply.send({
code: 500,
error: "Version Error",
message: error
})
}
}
)
//Todo 2. get update file
//Define request body to fastify
fastify.get('/update_file', function (req, reply) {
reply.sendFile(...)
})
done()
}
export default versionRoute
Hi, I have a question.
I want to send the file, when request to specific url.
So, I install fastify-specific and register.
But, it show error message like,
'FastifyReply<Server, IncomingMessage, ServerResponse, RouteGenericInterface, unknown>' type does not have property 'sendFile'
How I can register reply.sendFile in fastify?
or Is there any way to send file in fastify?
If you know about it, please help me.

Related

GraphQL Subscriptions is null using Express-GraphQL and graphql-subscriptions

I am using TypeScript and have Server and Client application. Below is the server code.
Server Code
import express, { Express } from "express";
import { graphqlHTTP } from "express-graphql";
import { buildSchema } from "type-graphql";
import { TaskResolver } from "./resolvers/task.resolver";
import { pgDatasource } from "./configs/db.config";
import { SeatBandingResolver } from "./resolvers/seatBanding.resolver";
import { GuestChatResolver } from "./resolvers/guestChat.resolver";
import { RateResolver } from "./resolvers/rate.resolver";
import { YearResolver } from "./resolvers/year.resolver";
import { ImplementationRateResolver } from "./resolvers/implementationRate.resolver";
import { UserResolver } from "./resolvers/user.resolver";
import { ReportResolver } from "./resolvers/report.resolver";
// Subscriptions
const ws = require("ws");
const { useServer } = require("graphql-ws/lib/use/ws");
const { execute, subscribe } = require("graphql");
const main = async () => {
const app: Express = express();
try {
//connect to db
await pgDatasource.initialize();
} catch (err) {
throw err;
}
//build gql schema
let schema = await buildSchema({
resolvers: [
SeatBandingResolver,
GuestChatResolver,
RateResolver,
YearResolver,
ImplementationRateResolver,
UserResolver,
],
validate: false,
// pubSub: new PubSub()
});
let schemaDoc = await buildSchema({
resolvers: [ReportResolver],
validate: false,
});
//ql schema for report
const docServer = graphqlHTTP((req, res) => {
return {
schema: schemaDoc,
graphiql: true,
context: {
req: req,
header: req.headers,
},
};
});
//setting a graphql server instance
const graphqServer = graphqlHTTP((req, res, graphQLParams) => {
return {
schema,
context: {
req: req,
header: req.headers,
},
graphiql: true,
};
});
app.use(cors());
//graphql endpoint : change it to backend
app.use("/graphql", graphqServer);
//for report : change name to google api
app.use("/doc", docServer);
//test route
app.get("/", (req, res) => {
res.json({
message: "Hello world",
});
});
let server = app.listen(3001, () => {
console.log("server started");
const wsServer = new ws.WebSocketServer({
host: "localhost",
// server,
path: "/graphql",
port: 3001,
});
useServer(
{
schema,
execute,
subscribe,
onConnect: (ctx) => {
console.log("Connect");
},
onSubscribe: (ctx, msg) => {
console.log("Subscribe");
},
onNext: (ctx, msg, args, result) => {
console.debug("Next");
},
onError: (ctx, msg, errors) => {
console.error("Error");
},
onComplete: (ctx, msg) => {
console.log("Complete");
},
},
wsServer
);
});
};
//starting a server
main()
.then(async (_) => {
// await addColumn()
})
.catch((err) => {
console.log(err);
});
Subscription Code at Client Side
import { Year } from "../entities/year.entity";
import { NewYear } from "../inputs/addYear.input";
import {
Arg,
Ctx,
Field,
Int,
Mutation,
ObjectType,
Query,
Resolver,
Root,
Subscription,
UseMiddleware,
} from "type-graphql";
import { Request } from "express";
import { Response } from "../helpers/response.helper";
import { Pagination } from "../inputs/pagination.input";
import { isAuth } from "../helpers/auth.helper";
import { PubSub, PubSubEngine } from "graphql-subscriptions";
const pubSub = new PubSub();
#ObjectType()
class MessagePayload {
#Field()
message: string;
}
#Resolver(() => Year)
export class YearResolver {
#Mutation(() => String)
async sendMessage(#Arg("message") message: string): Promise<string> {
console.log("in send subscription");
pubSub.publish("MESSAGE_NOTIFICATION", { message });
return message;
}
//calling the subscription
#Subscription(() => MessagePayload || null, {
topics: "MESSAGE_NOTIFICATION",
})
async receiveMessage(
#Root() root: MessagePayload
): Promise<MessagePayload | null> {
console.log("in publisher");
console.log(root, "in recieve message");
pubSub.asyncIterator("MESSAGE_NOTIFICATION");
return { message: "hello from the subscription" };
}
}
The issue I am facing here is Subscription is not working properly and the data is always null.
Can anyone help me to identify what I am missing here?
Thanks.
I'm not sure for 100% because your code descriptions are kinda confusing, but it looks like you should return pubSub.asyncIterator('MESSAGE_NOTIFICATION') in method receiveMessage. This method is called to start streaming messages to client at selected channel (MESSAGE_NOTIFICATION), not sending them. To send messages use pubsub. Of course you should change typing too.
You can find a similiar implementation here.

Can't send FormData to NodeJS server in MERN Stack App with TypeScript

I'm stuck with that request already. I'm trying to send FormData to NodeJS server but all I got in backend when I console.log the req.body is empty object. I checked the FormData keys/values and it's all good.
Here is my POST request in frontend:
const createProduct = (e: any) => {
e.preventDefault();
const data = new FormData()
data.append("name", name)
data.append("description", description)
data.append("price", price)
for (const colorAndImage of colorsAndImages) {
data.append('images', colorAndImage.images[1]);
data.append('colors', colorAndImage.colors);
}
data.append("type", type)
for (var pair of data.entries()) {
console.log(pair[0]+ ', ' + pair[1]); // the keys/values are correct
}
fetch('http://localhost:4000/products/create', {
method: 'POST',
body: data
})
.then(response => {
if (response.status === 201) {
setName('')
setDescription('')
setPrice('')
setType('')
} else if (response.status === 500) {
console.log('error');
}
})
.catch(error => console.log(error));
}
And my controller in backend:
productController.post('/create', async (req: Request, res: Response) => {
console.log(req.body)
try {
const data = {
name: req.body.name,
description: req.body.description,
price: req.body.price,
colors: req.body.colors,
images: req.body.images,
type: req.body.type,
likes: req.body.likes
}
let product = await create(data)
res.status(201).json(product)
} catch (error) {
console.log(error);
//res.status(500).json({error: error})
}
})
Even that I obviously send some data, the req.body is an empty object and I got that error:
Error: Product validation failed: name: Path 'name' is required.,
description: Path 'description' is required., price: Path 'price' is
required., type: Path 'type' is required.
at ValidationError.inspect
UPDATE
My express config:
import express, { Application } from 'express';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import auth from '../middlewares/auth';
const corsConfig: cors.CorsOptions = {
credentials: true,
origin: ['http://localhost:3000', 'http://localhost:2000']
}
export default function (app: Application) {
app.use(cors(corsConfig))
app.use(cookieParser());
app.use(express.urlencoded({ extended: false }));
app.use(express.json())
app.use(auth())
}
And root server:
import express, { Application } from "express";
import routes from './routes'
import config from './config/config'
import mongooseConfig from './config/mongoose'
import expressConfig from './config/express'
const app: Application = express()
expressConfig(app);
mongooseConfig();
app.use(express.json())
app.use(routes)
app.listen(config.PORT, () => console.log(`Server is listening on port ${config.PORT}`))
Routes file:
import { Router } from "express";
import authController from "./controllers/authController";
import productController from "./controllers/productController";
const routes = Router()
routes.use('/auth', authController)
routes.use('/products', productController)
export default routes;
Maybe you can just submit it as JSON instead of Form data, this works always :smile:
const createProduct = (e: any) => {
e.preventDefault();
const data = {
"name": name,
"description": description,
"price": price,
"colorsAndImages": colorsAndImages,
"type": type,
};
// Please check mappings as I just transferred what you had :smile:
fetch('http://localhost:4000/products/create', {
method: 'POST',
body: JSON.stringify(data),
})
.then(response => {
if (response.status === 201) {
setName('')
setDescription('')
setPrice('')
setType('')
} else if (response.status === 500) {
console.log('error');
}
})
.catch(error => console.log(error));
}

Introducing a express middleware in NodeJS TypeScript OOP

I'm trying to introducing cls-rtracer and when instantiating it, it's coming as undefined.
The code is written in TypeScript using Object Oriented Paradigm instead of Functional.
Examples posted in NPM for the library are functional.
I've commented the place where it is coming as undefined.
Introducing any other middleware in this express app seems straightforward except cls-rtracer
This is the code:
import express, {NextFunction, Response, Request} from 'express';
import * as bodyParser from 'body-parser';
import helmet from 'helmet';
import morgan from 'morgan';
import cors from 'cors';
import swaggerJSDoc from 'swagger-jsdoc';
import swaggerui from 'swagger-ui-express';
import {Controller} from './api/v1/controllers/Controller';
import {logger} from './api/v1/utils/Logger';
import {errorHandler} from './api/v1/utils/ErrorHandler';
import {BaseError} from './api/v1/utils/BaseError';
import {HttpStatusCode} from './api/v1/constants/HttpStatusCode';
import rTracer from 'cls-rtracer';
export const stream = {
write: (text: string) => {
logger.info(text.replace(/\n$/, ''));
},
};
const swaggerOptions = {
swaggerDefinition: {
info: {
title: 'Backend',
descriptions: 'Service',
contact: {
name: '',
},
servers: ['http://localhost:8080//api/v1/domain'],
version: '1.0.1',
},
},
apis: ['./api/v1/routes/*.ts'],
};
const swaggerDocs = swaggerJSDoc(swaggerOptions);
export class App {
public app: express.Application;
public port: number;
constructor(controllers: Controller[], port: number) {
this.app = express();
this.port = port;
this.initializeMiddlewares();
this.initializeHealth();
this.initializeControllers(controllers);
this.initializeErrorHandler();
this.initializeSwagger();
}
private initializeMiddlewares() {
this.app.use(rTracer.expressMiddleware());
const requestId = rTracer.id();
console.log(requestId); // giving undefined
this.app.use(bodyParser.json());
this.app.use(cors());
this.app.use(
morgan(
[
'ip: :remote-addr',
':method', ':url', 'HTTP/:http-version', 'status: :status',
':res[content-length]', 'referrer: :referrer',
'userAgent: :user-agent', 'responseTime: :response-time ms',
].join(' | '),
{stream: stream},
),
);
this.app.use(express.json());
this.app.use(helmet());
}
private initializeControllers(controllers: Controller[]) {
controllers.forEach((controller) => {
this.app.use('/', controller.router);
});
}
private initializeErrorHandler() {
this.app.use(async (req: Request, res: Response, next: NextFunction) => {
const error = new BaseError('Not Found', HttpStatusCode.NOT_FOUND, true, 'Not Found');
next(error);
});
this.app.use(async (error: Error, req: Request, res: Response, next: NextFunction) => {
if (!errorHandler.isTrustedError(error)) {
// #ts-ignore: Unreachable code error
res.status(error.status).json(error);
}
await errorHandler.handleError(error);
// #ts-ignore: Unreachable code error
res.status(error.httpCode || HttpStatusCode.INTERNAL_SERVER).json({error: error});
});
}
private initializeSwagger() {
this.app.use(
'/api-docs',
swaggerui.serve,
swaggerui.setup(swaggerDocs, {explorer: true}),
);
}
private initializeHealth() { }
public listen() {
this.app.listen(this.port, () => {
logger.info(`listening on the port ${this.port}`);
});
}
}
export default App;
Why does it work flawlessly in the code below:
import express from 'express';
import {CONFIG} from './config';
import MasterRouter from './routers/MasterRouter';
import ErrorHandler from './utils/ErrorHandler';
import rTracer from 'cls-rtracer';
import morgan from 'morgan';
class Server {
public app = express();
public router = MasterRouter;
constructor(
) {
this.correlationalIdMiddleware();
this.loggingMiddleware();
this.routingMiddleware();
this.errorHandlingMiddleWare();
}
correlationalIdMiddleware() {
this.app.use(rTracer.expressMiddleware());
}
loggingMiddleware() {
this.app.use(morgan((tokens, req, res) => {
const requestId = rTracer.id();
return [
`> requestId: ${requestId} -`,
tokens.method(req, res),
tokens.url(req, res),
tokens.status(req, res),
tokens.res(req, res, 'content-length'), '-',
tokens['response-time'](req, res), 'ms',
].join(' ');
}));
}
routingMiddleware() {
this.app.use('/api', this.router);
}
errorHandlingMiddleWare() {
this.app.use((req, res, next) => {
const error = new ErrorHandler(404, 'Not Found');
next(error);
});
this.app.use((error: ErrorHandler, req: any, res: any, next: any) => {
const errorObject = {
status: 'error',
statusCode: error.statusCode,
message: error.message,
};
const requestId = rTracer.id();
console.log(`> requestId: ${requestId} - ${JSON.stringify(errorObject)}`);
res.status(error.statusCode || 500).json(errorObject);
});
}
}
const server = new Server;
server.app.listen(CONFIG.PORT, () => {
console.log(`> Server listening on ${CONFIG.PORT}`);
});

TestCafe Triggering Test By POST Request In Express

I had a question that doesn't seem to be answered anywhere.
I am running tests from within my Express.js api. I set up a page that has a button and a field to enter a keyword intended to be used during a testcafe test. My endpoint I set up is /testcafe. But after sending a post request to /testcafe, there is a long delay while test runs and so my question is what is the best next step besides hanging?
Also, can my post request body, which contains the keyword, be directly used in a test like this? Keep in mind it's this pattern:
frontend -> POST request -> Express server -> /testcafe endpoint - test
My problem is after it reaches test, I currently have it attempting to call fetch from within the request logger. Is this right?
import { ClientFunction, Selector } from 'testcafe';
import { RequestLogger, RequestHook } from 'testcafe';
import zlib from 'zlib';
import fetch from 'isomorphic-unfetch';
const url = 'https://www.mysitetesturl.com/page';
class MyRequestHook extends RequestHook {
constructor (requestFilterRules, responseEventConfigureOpts) {
super(requestFilterRules, responseEventConfigureOpts);
}
onRequest (e) {
console.log('in onRequest!')
console.log('========================')
console.log('Request Body')
let buf = e._requestContext.reqBody
console.log(buf.toLocaleString())
}
onResponse (e) {
let buf = Buffer(e.body)
let unzippedBody = Buffer(zlib.gunzipSync(buf))
let payload = unzippedBody.toLocaleString()
fetch('http://myapiipaddress/api/testcafe',
method: 'PUT',
body: JSON.stringify(payload)
)
.then((err, doc) => {
if(err) {
console.log(err)
} else {
console.log(doc)
}
})
}
}
const myRequestHook = new MyRequestHook({
url: url,
method:'get'},
{
includeHeaders: true,
includeBody: true
}
);
fetch('http://myapiipaddress/api/testcafe',
method: 'GET'
)
.then((err, doc) => {
if(err) {
console.log(err)
} else {
fixture`myfixture`
.page(doc.url)
.requestHooks(myRequestHook);
test(`mytest`, async t => {
const inputField = Selector('input');
await t
await t
.wait(5000)
.typeText(inputField, doc.text)
.wait(5000)
}
);
}
})
According to your scheme, you need to organize your code in a different way:
const createTestCafe = require('testcafe');
....
// Choose the necessary body parser for express application
// https://github.com/expressjs/body-parser
app.use(bodyParser.urlencoded({ extended: true }));
...
app.post('/', function (req, res) {
createTestCafe('localhost', 1337, 1338, void 0, true)
.then(testcafe => {
const runner = testcafe.createRunner();
return runner
.src('/tests')
.browsers('chrome')
.run();
})
.then(failedCount => {
testcafe.close();
res.end();
});
})

Logging Fastify response body

How can I log the response body in Fastify? The body doesn't seem to be exposed as part of the response object:
const fastify = require('fastify')({
logger: {
serializers: {
res: function (res) {
// No body in req afaik
return { }
}
}
}
})
Try this:
const fastify = require('fastify')({
logger: {
serializers: {
res: function (res) {
return {
statusCode: res.statusCode,
payload: res.payload,
}
},
}
}
})
fastify.addHook('onSend', function (_request, reply, payload, next) {
Object.assign(reply.res, { payload });
next();
})
If some of your payloads are objects and you want to get them in serialize before they are - well, serialized - you can add preSerialization hook as well:
fastify
.addHook('preSerialization', (_request, reply, payload, next) => {
Object.assign(reply.res, { payload });
next();
})
.addHook('onSend', (_request, reply, payload, next) => {
if (!reply.res.payload) Object.assign(reply.res, { payload });
next();
});
here you are a working example. I think that this kind of usage need to be used only for debugging because you are slowing down if you have many req/sec.
I have also added a JSON Schema validation as demo:
const fastify = require('fastify')({ logger: true })
fastify.register(async function (fastify, opts) {
fastify.addHook('onSend', function (request, reply, payload, next) {
console.log(payload);
next()
})
fastify.get('/', {
schema: {
response: {
'2xx': { properties: { this: { type: 'string' } } }
}
}
}, async function () {
return { this: 'is', a: 'json' }
})
})
fastify.listen(3000)
You will get:
curl http://localhost:3000/
{"this":"is"}

Resources