ApolloServer (NodeJS): Return string response - node.js

I have an ApolloServer with default settings such as:
class AuditRemoteGraphQLDataSource extends RemoteGraphQLDataSource {
...
}
const gateway = new ApolloGateway({
serviceList,
buildService: ({url}) => new AuditRemoteGraphQLDataSource({url})
});
const server = new ApolloServer({
debug: false,
gateway,
context: async ({req}) => {
.........
}
Upon making a request (query/mutation), we expect:
{
"data": {
"something": "somevalue"
}
}
This works fine. The challenge I am having is that for a specific GET request, I want to return a string as is. I tried to extend:
async didReceiveResponse(response, req, context) {
const body = await super.didReceiveResponse(response, req, context);
body.data = "someString";
return body;
}
But it did not help.

Related

How to make context request in dialogoflow via nodejs sdk?

I would like to know how to create a context dialog, but I don't think anything about how to make this request... can anyone help me?
i'm using a lib sdk dialogflow
I searched how to do this, but I don't find anything, I don't know how to implement this..
me code
class DialogFlowController {
public dialogID: string
public sessionID: string
public sessionClient: SessionsClient
constructor () {
this.dialogID = 'chatbot-asxj'
this.sessionID = v4()
this.sessionClient = new dialogflow.SessionsClient({
credentials: {
type: config.gooogleType,
client_id: config.googleClientId,
private_key: config.googlePrivateKey,
client_email: config.googleCLientEmail,
token_url: config.googleTokenUrl
}
})
}
public async interactor (req: Request, res: Response): Promise<Response> {
console.log(teste)
const messagem = req.body.goMessage
try {
// Create a new session
const sessionPath = this.sessionClient.projectAgentSessionPath(this.dialogID, this.sessionID)
const request = {
session: sessionPath,
queryInput: {
text: {
// The query to send to the dialogflow agent
text: messagem,
// The language used by the client (en-US)
languageCode: 'en-US'
}
}
}
const response = await this.sessionClient.detectIntent(request)
Socket.io.emit('returnMessage', response[0].queryResult?.fulfillmentText)
return res.status(200).json()
} catch (err) {
return res.status(500).json(err)
}
}
}

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.

Context is empty in GraphQL middleware

I'm sending from frontend authorization token in headers and then I want to check validity of this token in some endpoints using middleware and context, but context is always empty.
I'm using type-graphql.
Frontend code (I check request in 'Network' tab and I can see my additional header):
private async mutate<T>(
mutation: DocumentNode,
data: unknown,
token?: string
) {
const response = await apolloClient.mutate<T>({
mutation: mutation,
context: {
headers: {
'auth-token': token || '',
},
},
variables: {
data: data,
},
});
return response.data;
}
Resolver code:
#Mutation(() => Token)
#UseMiddleware(authMiddleware)
async login(#Ctx() ctx: unknown, #Arg('data') data: LoginInput) {
console.log(ctx);
...
}
Middleware code:
export const authMiddleware: MiddlewareFn = ({ context }, next) => {
console.log(context);
try {
return next();
} catch (error) {
return next();
}
};
console.log is always equal to {}
I found the cause.
In declaration of ApollorServer the context was missing.
const server = new ApolloServer({
schema,
context: ({ req }) => {
const context = {
req,
};
return context;
},
cors: {
origin: '*',
credentials: true,
},
});

User is not available in request with NestJS passport strategy (other than documentation exemples)

I'm trying to implement a passport strategy (passport-headerapikey), I was able to make it work and I can secure my routes.
But the request is empty and cannot access the logged in user ?
import { HeaderAPIKeyStrategy } from "passport-headerapikey";
import { PassportStrategy } from "#nestjs/passport";
import { Injectable, NotFoundException } from "#nestjs/common";
import { CompanyService } from "../../companies/companies.service";
#Injectable()
export class ApiKeyStrategy extends PassportStrategy(HeaderAPIKeyStrategy, "api-key") {
constructor(private readonly companyService: CompanyService) {
super(
{
header: "Authorization",
prefix: "Api-Key "
},
true,
async (apiKey, done) => {
return this.validate(apiKey, done);
}
);
}
public async validate(apiKey: string, done: (error: Error, data) => {}) {
const company = await this.companyService.findByApiKey(apiKey);
if (company === null) {
throw new NotFoundException("Company not found");
}
return company;
}
}
#UseGuards(AuthGuard("api-key"))
export class CompaniesController {
constructor(private companyService: CompanyService) {}
#Get()
#ApiOperation({ title: "Get company information" })
public getCompany(#Request() req) {
// here request is empty, so i cannot access the user..
console.log("request", req);
return [];
}
}
Thanks for your help !
To access the logged user, you can inject the object in the request. To do that, in your ApiKeyStrategy constructor, change the third parameter to something like this:
async (apiKey, verified, req) => {
const user = await this.findUser(apiKey);
// inject the user in the request
req.user = user || null;
return verified(null, user || false);
}
Now, you can access the logged user:
getCompany(#Request() req) {
console.log(req.user);
}
I hope that could help you.
As show in the documentation you should do some works to get the current user : here the documetation
First of all in the app.module make sure that the context is set :
context: ({ req }) => ({ req })
Then you can add this in the controller/resolver, this example use the Gql (GraphQL):
export const CurrentUser = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req.user;
},
);
if this one doesnt work for you try this one instead :
export const CurrentUser = createParamDecorator(
(data: unknown, context: ExecutionContext) => {
const ctx = GqlExecutionContext.create(context);
const request = ctx.getContext();
request.body = ctx.getArgs();
return request.user;
},
);
Modify your validate method like so:
public async validate(apiKey: string, done: (error: Error, data) => {}) {
const company = await this.companyService.findByApiKey(apiKey);
if (company === null) {
return done(new NotFoundException("Company not found"), null);
}
return done(null, company);
}

Sending file using restify with multipart/form-data causes a timeout problem

I have a problem because I am trying to implement file upload using multipart / form-data on my NodeJS server. When I call upload, the file I upload appears in the temporary server folder, but my request does not continue and my client is waiting for a response (in this case the uploadFile method is never running).
upload.router.ts
import {Router} from '../common/router';
import * as restify from 'restify';
class UploadRouter extends Router {
uploadFile = (req, resp, next) => {
console.log(req);
resp.json('test');
};
applyRoutes(application: restify.Server) {
this.basePath = '/upload';
application.post(`${this.basePath}`, this.uploadFile);
}
}
export const uploadRouter = new UploadRouter();
server.ts
export class Server {
application: restify.Server;
initRoutes(routers: Router[]): Promise<any> {
return new Promise((resolve, reject) => {
try {
const options: restify.ServerOptions = {
name: environment.project.name,
version: environment.project.version
};
if (environment.security.enableHTTPS) {
options.certificate = fs.readFileSync(environment.security.certificate);
options.key = fs.readFileSync(environment.security.key);
}
this.application = restify.createServer(options);
this.connector = blockchainConnector(environment.blockchain.connector);
const corsOptions: corsMiddleware.Options = {
preflightMaxAge: 10,
origins: ['*'],
allowHeaders: ['*'],
exposeHeaders: []
};
const cors: corsMiddleware.CorsMiddleware = corsMiddleware(corsOptions);
this.application.pre(cors.preflight);
this.application.use(cors.actual);
this.application.use(restify.plugins.queryParser());
this.application.use(restify.plugins.bodyParser());
this.application.use(restify.plugins.acceptParser(this.application.acceptable));
this.application.use(restify.plugins.fullResponse());
this.application.use(restify.plugins.multipartBodyParser({
multiples: true,
mapParams: true,
mapFiles: true,
keepExtensions: true,
uploadDir: environment.directory.tempDir
}));
this.application.use(mergePatchBodyParser);
this.application.use(tokenParser);
// routes
for (let router of routers) {
router.applyRoutes(this.application, this.connector);
indexRouter.addRouter(router);
}
indexRouter.applyRoutes(this.application);
this.application.listen(environment.server.port, () => {
resolve(this.application);
});
this.application.on('restifyError', handleError);
} catch (error) {
reject(error);
}
})
}
bootstrap(routers: Router[] = []): Promise<Server> {
return this.initRoutes(routers).then(() => this);
}
shutdown() {
this.application.close();
}
}
I realize that this is 8 months later, but it looks like you forgot to call next() in uploadFile

Resources