Lambda function – GET doesn't return anything - aws-lambda

I am completely new to The Serverless Framework and AWS lambda.
When making a GET request to http://localhost:3000/user/1e89a3f0-d170-11e9-94bd-91e9ae84f3e9 I would expect a response being send back to the browser with a valid JSON object matching the Key. Like the only getting logged out to the console. And not empty document.
Am I returning incorrectly? I am having difficulties debugging this, I don’t now if the problem is with my lambda function, or what it is.
Thank you.
console.log statement
{
email: 'i#am.com',
password: '$argon2i$v=19$m=4096,t=3,p=1$IIICgcMqbUA7wFpEMqb/GA$ENScjko+Y8pruQsTiE6qN81QAJfAPX/T116RQZqe347Y1p0rez4KhKaEulMeabKKiu8',
id: '1e89a3f0-d170-11e9-94bd-91e9ae84f3e9'
}
Here is the get handler in question.
users/get.js
const AWS = require("aws-sdk");
const dynamoDb = new AWS.DynamoDB.DocumentClient({
region: "localhost",
endpoint: "http://localhost:8000"
});
module.exports.get = async event => {
const params = {
TableName: process.env.DYNAMODB_TABLE,
Key: {
id: event.pathParameters.id
}
};
dynamoDb.get(params, (error, result) => {
if (error) {
console.error(error);
return;
}
console.log(result.Item); // logs successfully to the console.
return {
// doesn't return a response.
statusCode: 200,
body: JSON.stringify(result.Item)
};
});
};
serverless.yml
# EXCERPT
functions:
get:
handler: users/get.get
events:
- http:
method: get
path: user/{id}
cors: true
resources:
Resources:
UsersDynamoDbTable:
Type: "AWS::DynamoDB::Table"
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: ${self:provider.environment.DYNAMODB_TABLE}
custom:
dynamodb:
stages:
- dev
start:
port: 8000
inMemory: true
sharedDb: true
noStart: true

You should either use the callback argument to return a response:
module.exports.get = (event, context, callback) => {
const params = {
TableName: process.env.DYNAMODB_TABLE,
Key: {
id: event.pathParameters.id,
},
};
dynamoDb.get(params, (error, result) => {
if (error) {
console.error(error);
callback({
statusCode: 500,
body: 'Unable to get item',
});
}
console.log(result.Item);
callback(null, {
statusCode: 200,
body: JSON.stringify(result.Item),
});
});
};
Or use promises:
module.exports.get = async event => {
try {
const params = {
TableName: process.env.DYNAMODB_TABLE,
Key: {
id: event.pathParameters.id,
},
};
const result = await dynamoDb.get(params).promise();
console.log(result.Item);
return {
statusCode: 200,
body: JSON.stringify(result.Item),
};
} catch (error) {
console.error(error);
return {
statusCode: 500,
body: 'Unable to get item',
};
}
};

Related

How to enable graphql subscription in loopback 4 with openapi-to-graphql

as per the title, I am having problem trying to enable graphql subscription in my loopback 4 application.
Here is my code that I've done so far.
index.ts
export async function main(options: ApplicationConfig = {}) {
const app = new BackendLb4Application(options)
await app.boot()
await app.start()
const url = app.restServer.url;
const oas: Oas3 = <Oas3><unknown>await app.restServer.getApiSpec()
const {schema} = await createGraphQLSchema(oas, {
operationIdFieldNames: true,
baseUrl: url,
createSubscriptionsFromCallbacks: true,
})
const handler = graphqlHTTP( (request:any, response:any, graphQLParams: any) => ({
schema,
pretty: true,
graphiql: true
}))
app.mountExpressRouter(graphqlPath, handler);
const pubsub = new PubSub()
const ws = createServer(app);
ws.listen(PORT, () => {
new SubscriptionServer(
{
execute,
subscribe,
schema,
onConnect: (params: any, socket: any, ctx: any) => {
console.log(params, 'here on onconnect')
// Add pubsub to context to be used by GraphQL subscribe field
return { pubsub }
}
},
{
server: ws,
path: '/subscriptions'
}
)
})
return app
}
Here is my schema
type Subscription {
"""
Equivalent to PATCH onNotificationUpdate
"""
postRequestQueryCallbackUrlApiNotification(secondInputInput: SecondInputInput): String
"""
Equivalent to PATCH onNotificationUpdate
"""
postRequestQueryCallbackUrlOnNotificationUpdate(firstInputInput: FirstInputInput): String
}
Here is an example of my controller
#patch('/notification-update', {
operationId: 'notificationUpdate',
description: '**GraphQL notificationUpdate**',
callbacks:[ {
onNotificationUpdate: {
//'{$request.query.callbackUrl}/onNotificationUpdate': {
post: {
requestBody: {
operationId: 'notificationUpdateCallback',
description: 'rasjad',
content: {
'application/json': {
schema: {
title: "firstInput",
type: 'object',
properties: {
userData: {
type: "string"
}
}
}
}
}
},
responses: {
'200': {
description: 'response to subscription',
}
}
}
},
// }
}],
responses: {
'200': {
description: 'Notification PATCH success count',
content: {'application/json': {schema: CountSchema}},
},
},
})
async updateAll(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Notification, {partial: true}),
},
},
})
notification: Notification,
#param.where(Notification) where?: Where<Notification>,
): Promise<Count> {
return this.notificationRepository.update(notification, where);
}
Ive defined the callbacks object in my controller which will then create a subscription in my schema. Tested it out on graphiql but did not work.
I am not sure where to go from here. Do I need a custom resolver or something? Not sure.
Appreciate it if anyone could help on this.
Just in case someone else is looking to do the same thing.
I switched out graphqlHTTP with Apollo Server to create my graphql server.
So my final index.ts looks like this.
export async function main(options: ApplicationConfig = {}) {
const lb4Application = new BackendLb4Application(options)
await lb4Application.boot()
await lb4Application.migrateSchema()
await lb4Application.start()
const url = lb4Application.restServer.url;
const graphqlPath = '/graphql'
// Get the OpenApiSpec
const oas: Oas3 = <Oas3><unknown>await lb4Application.restServer.getApiSpec()
// Create GraphQl Schema from OpenApiSpec
const {schema} = await createGraphQLSchema(oas, {
strict: false,
viewer: true,
baseUrl: url,
headers: {
'X-Origin': 'GraphQL'
},
createSubscriptionsFromCallbacks: true,
customResolvers: {
"lb4-title": {
"your-path":{
patch: (obj, args, context, info) => {
const num = Math.floor(Math.random() * 10);
pubsub.publish("something", { yourMethodName: {count: num} }).catch((err: any) => {
console.log(err)
})
return {count: 1}
}
}
}
},
customSubscriptionResolvers: {
"lb4-title" : {
"yourMethodName": {
post: {
subscribe: () => pubsub.asyncIterator("something"),
resolve: (obj: any, args: any, context, info) => {
console.log(obj, 'obj')
}
}
}
}
}
})
const app = express();
const server = new ApolloServer({
schema,
plugins: [{
async serverWillStart() {
return {
async drainServer() {
subscriptionServers.close();
}
};
}
}],
})
const subscriptionServers = SubscriptionServer.create(
{
// This is the `schema` we just created.
schema,
// These are imported from `graphql`.
execute,
subscribe,
},
{
server: lb4Application.restServer.httpServer?.server,
path: server.graphqlPath,
//path: server.graphqlPath,
}
);
await server.start();
server.applyMiddleware({ app, path: "/" });
lb4Application.mountExpressRouter('/graphql', app);
return lb4Application
}
Also you will need to define the callbacks object in your controller like so.
#patch('/something-update', {
operationId: 'somethingUpdate',
description: '**GraphQL somethingUpdate**',
callbacks:[
{
yourMethodName: {
post: {
responses: {
'200': {
description: 'response to subscription',
content: {'application/json': {schema: CountSchema}},
}
}
}
},
}
],
responses: {
'200': {
description: 'Something PATCH success count',
content: {'application/json': {schema: CountSchema}},
},
},
})
async updateAll(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Something, {partial: true}),
},
},
})
something: Something,
#param.where(Something) where?: Where<Something>,
): Promise<Count> {
return this.somethingRepository.updateAll(something, where);
}
And that is it. You can test it out from the GraphQL Playground and play around with the subscriptions.
For the time being, I am fine with defining customResolvers and customSubscriptionResolvers but I'm pretty sure I can automate this two objects from the controllers.
Cheers!

GraphQL Subscriptions Error: "The \"properties\" argument must be of type Array. Received type object"

I am trying to implement a simple API with GraphQL. My queries and my mutations are in place and working, but now I'm trying to include subscriptions as well.
I already added the subscription in the schema, I included the event publish in the addUser mutation and defined the subscribe function for the subscription type.
Now, when I am trying to run a subscription query in the graphiql in-browser IDE, I get this error:
"The \"properties\" argument must be of type Array. Received type object"
Attached is the schema object. Did I configured something wrong or am I missing something? Thanks!
P.S I also need to mention that I am using mongoose to store the data on an a mongo instance, hence the entities.
import {
GraphQLFloat,
GraphQLID,
GraphQLInt,
GraphQLList,
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString
} from 'graphql';
// models
import UserType from '../types/user/UserType';
import AccountType from '../types/account/AccountType';
import TransactionType from '../types/transaction/TransactionType';
// entities
import User from '../entities/user/user';
import Account from '../entities/account/account';
import Transaction from '../entities/transaction/transaction';
// subscriptions
import { PubSub } from 'graphql-subscriptions';
// subscriptions
const pubsub = new PubSub();
const USER_CREATED = 'user_created';
// the acceptable starting point of our graph
const RootQueryType = new GraphQLObjectType({
name: 'RootQueryType',
fields: () => ({
// query individual entities in the database
user: {
type: UserType,
description: 'The current user identified by an id',
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return User.findById(args.id);
}
},
account: {
type: AccountType,
description: 'Details about the account in question identified by an id',
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return Account.findById(args.id);
}
},
transaction: {
type: TransactionType,
description: 'Details about the transaction in question identified by an id',
args: {
id: {
type: GraphQLID
}
},
resolve(parent, args) {
return Transaction.findById(args.id);
}
},
// query all entities in the database
users: {
type: new GraphQLList(UserType),
resolve: (parent, args) => {
return User.find({});
}
},
accounts: {
type: new GraphQLList(AccountType),
resolve: (parent, args) => {
return Account.find({});
}
},
transactions: {
type: new GraphQLList(TransactionType),
resolve(parent, args) {
return Transaction.find({});
}
}
})
});
const MutationType = new GraphQLObjectType({
name: 'Mutation',
fields: () => ({
addUser: {
type: UserType,
args: {
name: {
type: new GraphQLNonNull(GraphQLString)
},
age: {
type: new GraphQLNonNull(GraphQLInt)
},
email: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(parent, args) {
let user = new User({
name: args.name,
age: args.age,
email: args.email
});
pubsub.publish(USER_CREATED, {
newUser: user
});
return user.save();
}
},
addAccount: {
type: AccountType,
args: {
currency: {
type: new GraphQLNonNull(GraphQLString)
},
balance: {
type: new GraphQLNonNull(GraphQLFloat)
},
holderId: {
type: new GraphQLNonNull(GraphQLString)
}
},
resolve(parent, args) {
let account = new Account({
currency: args.currency,
balance: args.balance,
holderId: args.holderId
});
return account.save().then(() => console.log('user created'));
}
},
addTransaction: {
type: TransactionType,
args: {
sourceAccountId: {
type: new GraphQLNonNull(GraphQLString)
},
targetAccountId: {
type: new GraphQLNonNull(GraphQLString)
},
amount: {
type: new GraphQLNonNull(GraphQLFloat)
}
},
resolve(parent, args) {
let transaction = new Transaction({
sourceAccountId: args.sourceAccountId,
tagetAccountId: args.tagetAccountId,
timestamp: new Date(),
amount: args.amount
});
Account.findById(args.sourceAccountId, (err, account) => {
if (!err) {
account.balance -= args.amount;
return account.save();
}
});
Account.findById(args.targetAccountId, (err, account) => {
if (!err) {
account.balance += args.amount;
return account.save();
}
});
return transaction.save();
}
}
})
});
const SubscriptionType = new GraphQLObjectType({
name: 'Subscription',
fields: () => ({
newUser: {
type: UserType,
description: 'This subscription is going to provide information every time a new user creation event fires',
resolve: (payload, args, context, info) => {
console.table(payload, args, context, info); // debugging
return payload;
},
subscribe: () => pubsub.asyncIterator(USER_CREATED)
}
})
});
const schema = new GraphQLSchema({
query: RootQueryType,
mutation: MutationType,
subscription: SubscriptionType
});
export default schema;
I expect that when I run the subscription query, it will run listening for events being published and when from another tab I will run a mutation to add a new user, the first tab will catch the event and return details of the user in the payload.

How to access the context in GraphQL from your resolvers

I just want to send the request to all my resolvers through the context field, but when I access it from one of my resolvers, it returns null.
app.use('/graphql', graphqlHTTP(async (request, response, graphQLParams) => ({
schema: schema,
context:{token_1:null,test:request},
graphiql:true
})));
These are part of my schema. Firstly I Login to set the token ,but when I want to access the context.token_1 from the other resolver (BuyItems), it returns null.
BuyItems :{
type: UserType,
args: {
name: {type: GraphQLString},
points: {type: GraphQLInt}
},
resolve(parent,args,context){
console.log(context.token_1)
return UserModel.findOneAndUpdate({name:args.name},{points:args.points})
}
},
Login: {
type: AuthType,
args: {
email: {type:GraphQLString},
password: {type:GraphQLString}
},
async resolve(parent,args,context){
const user = await UserModel.findOne({ email: args.email });
if (!user) {
throw new Error('User does not exist on login!');
}
const isEqual = await bcrypt.compare(args.password, user.password);
if (!isEqual) {
throw new Error('Password is incorrect!');
}
const token = jwt.sign(
{ userId: user.id, email: user.email },
'somesupersecretkey',
{ expiresIn: '1h' }
);
context.token_1 = token;
return {tokenExpiration: 1, userId: user.id, token:token}
}
}

seneca - communication between two microservices

I'm new in Seneca. I have been trying to make two microservices to communicate each other but I keep failing and get this errors:
Error: Response Error: 404 Not Found
at module.exports.internals.Utils.internals.Utils.handle_response (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\seneca-transport\lib\transport-utils.js:71:11)
at c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\seneca-transport\lib\http.js:154:25
at read (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\wreck\lib\index.js:590:24)
at finish (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\wreck\lib\index.js:398:20)
at wrapped (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\hoek\lib\index.js:879:20)
at module.exports.internals.Recorder.onReaderFinish (c:\Users\Actiview\Desktop\microservices\orderManager\node_modules\wreck\lib\index.js:449:16)
at Object.onceWrapper (events.js:313:30)
at emitNone (events.js:111:20)
at module.exports.internals.Recorder.emit (events.js:208:7)
at finishMaybe (_stream_writable.js:614:14)
=== SENECA FATAL ERROR === MESSAGE: ::: seneca: Action failed: Response Error: 404 Not Found. CODE: ::: act_execute INSTANCE :::
Seneca/pcbyi7v5c76v/1534346071465/6536/3.7.0/- DETAILS ::: {
message: 'Response Error: 404 Not Found',
pattern: '',
fn: { [Function: transport_client] id: 'host:127.0.0.2,pg:,port:8080' },
callback:
{ [Function: bound action_reply]
seneca:
Seneca {
'private$':
{ act:
{ parent:
{ start: 1534346071559,
end: 1534346071561, and more...
this is my code:
orderIndex.ts
{
const orderPlugin = require('./orderManagerPlugin');
const express = require('express');
const SenecaWeb = require('seneca-web');
const seneca = require("seneca")();
let bodyParser = require('body-parser');
var Routes = [{
prefix: '/orders',
pin: 'area:order,action:*',
map: {
fetch: { GET: true },
create: { GET: false, POST: true },
delete: { GET: false, DELETE: true },
}
}]
var config = {
routes: Routes,
adapter: require('seneca-web-adapter-express'),
context: express().use(bodyParser.urlencoded({ 'extended': 'true' })).use(bodyParser.json()),
options: {parseBody: false}
}
seneca.use(SenecaWeb,config);
seneca.use( orderPlugin );
seneca.ready(function (err) {
const app = seneca.export('web/context')();
app.listen({ host: "127.0.0.4", port: 8081 });
});
}
orderPlugin.ts
{
var plugin = function orderPlugin(options) {
var seneca = this;
var senecaEmailer;
seneca.add({ area: "order", action: "fetch" }, function (args,
done) {
var orders = this.make("orders");
orders.list$({ id: args.id }, done);
});
seneca.add({ area: "order", action: "delete" }, function (args,
done) {
var orders = this.make("orders");
orders.remove$({ id: args.id }, function (err) {
done(err, null);
});
});
seneca.add({ area: "order", action: "create" }, function (args,
done) {
console.log('create order');
senecaEmailer.act( 'role:web', {area: 'email', action:'send'} , done);
});
this.add( { init: "orderPlugin" }, function (args, done) {
senecaEmailer = require("seneca")().client({ host: "127.0.0.2", port: 8080 });
done();
});
}
module.exports = plugin;
}
emailIndex.ts
{
const mailPlugin = require('./emailingPlugin');
const express = require('express');
const SenecaWeb = require('seneca-web');
const seneca = require("seneca")();
let bodyParser = require('body-parser');
var Routes = [{
prefix: '/emails',
pin: 'area:email, action:*',
map: {
send: { GET: true },
}
}]
var config = {
routes: Routes,
adapter: require('seneca-web-adapter-express'),
context: express().use(bodyParser.urlencoded({ 'extended': 'true' })).use(bodyParser.json()),
options: {parseBody: false}
}
seneca.use(SenecaWeb,config);
seneca.use( mailPlugin );
seneca.ready(function (err) {
const app = seneca.export('web/context')();
app.listen({ host: "127.0.0.2", port: 8080 } );
});
}
emailPlugin.ts
{
import {EmailService} from './emailService';
var plugin = function emailPlugin(options) {
var seneca = this;
let mailer :EmailService ;
seneca.add({area: "email", action: "send"}, function(args, done) {
mailer.sendMail('guzon56#gmail.com', done);
});
this.add( { init: "emailPlugin" }, function (args, done) {
console.log('before init');
mailer = require('./emailService')();
console.log('after init');
done();
});
};
module.exports = plugin;
}
please help me.
Tnx.
Seneca is explained by Richard Rodger in this post. The chapter "Service Discovery" talks about meshing the microservices in a network.
For my applications I use the seneca-mesh plugin. This plugin README says:
To join the network, all a service has to do is contact one other
service already in the network. The network then shares information
about which services respond to which patterns. There is no need to
configure the location of individual services anywhere.
Reading Richard's post and the plugin documentation could be a good starting point for your project. Hope it helps!

Running GraphQL over REST API returns null data [duplicate]

This question already has answers here:
Why does a GraphQL query return null?
(6 answers)
Closed 3 years ago.
I have gone through this blog followed the instructions in the blog post http://graphql.org/blog/rest-api-graphql-wrapper/
to create a graphQL endpoint over my own REST API. If I log the calls in the console I can see the correct response getting generated, but the data is always NULL in GraphiQL IDE. What could be the reason?
Here is my code:
import {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
} from 'graphql'
import fetch from 'node-fetch'
const BASE_URL = 'http://localhost/my.test.web/api/v1/customer/91/reservation'
const ReservationType = new GraphQLObjectType({
name: 'Reservation',
description: 'This is reservation details',
fields: () => ({
id: {type: GraphQLString},
confirmationNumber: {
type: GraphQLString,
resolve: (reservation) => reservation.confirmationNumber
},
status: {
type: GraphQLString,
resolve: (reservation) => reservation.status
}
})
});
const QueryType = new GraphQLObjectType(
{
name: "query",
description: "This is query by Id",
fields: () => ({
reservation: {
type: ReservationType,
args: {
id: {type: GraphQLString}
},
resolve: (root, args) => {
var url = BASE_URL+ '/' + args.id;
console.log(url);
var options = {
headers: {
'Accept': 'application/json',
'Accept-Language':'en-US'
}
};
fetch(url,options)
.then(function(res) {
return res.json();
}).then(function(json) {
console.log(json);
return json;
});
}
}
}
)
});
export default new GraphQLSchema(
{
query: QueryType,
}
)
When I run this using graphiQL and express, I can see that the log is correctly generated by this part of the code -
.then(function(json) {
console.log(json);
return json;
}
But in the GraphiQL UI the data is null
GraphiQL IDE query screenshot
Finally I was able to find the cause - It is the syntax and not the JSON returned. Notice the "," at the end of each block and also removed the wrapper around the resolve:
The QueryType should be defined as follows and it works like a charm
const QueryType = new GraphQLObjectType({
name: "query",
description: "This is person query by Id",
fields: () => ({
person: {
type: PersonType,
args: {
id: { type: GraphQLString },
},
resolve: (root, args) =>
fetch(BASE_URL +'/people/' +args.id)
.then(function(res) {
return res.json()
})
.then(function(json) {
console.log(json)
return json
}),
},
}),
});

Resources