In serverless i have the following directory structure for my functions:
serverless.yml
functions -
stories -
create.js
get.js
my serverless.yml then looks like this:
functions:
stories:
create:
handler: functions/stories/create.main
events:
- http:
path: stories/create
method: post
cors: true
authorizer: aws_iam
get:
handler: functions/stories/get.main
events:
- http:
path: stories/{id}
method: get
cors: true
authorizer: aws_iam
however when i run a test to check the create: serverless invoke local --function create --path mocks/create-event.json i get the following error:
Serverless Error ---------------------------------------
Function "create" doesn't exist in this Service
I managed to get one function working that looks like this:
functions:
stories:
handler: functions/stories/create.main
events:
- http:
path: stories/create
method: post
cors: true
authorizer: aws_iam
Since i added the get function, i decided i needed to add the create and get parts after stories, but no matter how i change the handler the functions never seem to exist.
I've tried changing the path to functions/stories/create/create.main with no difference, is there anything obvious i'm missing to allow multiple handlers within the same location?
I was looking at the following example, which uses one folder of "todos" which contains multiple functions, but i can't see any obvious difference between it and mine, except i've added an extra folder.
Your template is invalid. You can't just put your function under an arbitrary node to tell the framework that it applies to some object of your app. Your stories: node should be a comment.
Try something like this:
functions:
# Stories related functions
createStory:
handler: functions/stories/create.main
events:
- http:
path: stories # You can post directly to stories to be more RESTful
method: post
cors: true
authorizer: aws_iam
getStory:
handler: functions/stories/get.main
events:
- http:
path: stories/{id}
method: get
cors: true
authorizer: aws_iam
# More examples to understand the routing
getAllStories:
handler: functions/stories/getAll.main # Returns a list of all stories
events:
- http:
path: stories
method: get
cors: true
authorizer: aws_iam
deleteStory:
handler: functions/stories/delete.main # Deletes a story
events:
- http:
path: stories/{id}
method: delete
cors: true
authorizer: aws_iam
Took so much time on this! Just figured out that when I typed the command below, I mentioned the function name in the handle.js file which is wrong! I should call the handler name itself that exists in the serverless.yml file instead
For example
This was wrong:
sls invoke local --function testFunction
This is right:
sls invoke local --function test
Related
I am having an issue while creating AWS::Appsync:Resolver Cloudformation in Serverless Framework using Javascript resolver.
My Javascript Resolver code in root dir /src/resolvers/jsResolver.js which I have attached to AWS::AppSync::Resolver cloudformation in code block. I have also installed npm plugin for appsync utils in my package.json
import { util } from '#aws-appsync/utils';
export function request(ctx) {
const {source, args} = ctx
return {
operation: 'Invoke',
payload: { field: ctx, arguments: args, source },
};
}
export function response(ctx) {
util.error("Failed to fetch relatedPosts", "LambdaFailure", ctx.prev.result)
return ctx.result;
}
My AWS::AppSync::Resolver Cloudformation is below in YML file also I have used Code as its Mandatory if I have declared it as APPSYNC_JS Runtime
AppSyncJsResolver:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GettAtt Graphql.APiId
Kind: PIPELINE
Code: ./src/resolvers/jsResolver.js <—- Here my code is breaking up with error contains one or more error
TypeName: Query
FieldName: getInfo
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
PipelineConfig:
Functions:
- !GetAtt AppSyncFunction.FunctionId
I have tried above code as per AWS Cloudformation documentation for Appsync available where they have mentioned that in AWS::AppSync::Resolver for creating Javascript Resolver using Cloudformation as below one of the properties. which I have included in my AWS::AppSync::Resolver
Code
The resolver code that contains the request and response functions. When code is
used, the runtime is required. The runtime value must be APPSYNC_JS.
Required: No
Type: String
Required: No
Type: String
So I've tried this and cant find enough solutions regarding Javascript Resolvers all are available with VTL template specific.
With above code my CloudFormation build failed with the following error: An error occurred: AppSyncJSResolver- The code contains one or more errors. (Service: AWSAppSync; Status Code: 400; Error Code: BadRequestException; Request ID: 0245d64d-...; Proxy: null)
AppSyncJSResolver- which is my AWS::AppSync::Resolver in above code. and I have code property in that which giving me an error. I have verified with multiple sources and I am not finding any errors with my Javascript Resolver file /src/resolvers/jsResolver.js which I have declared in AppSyncJSResolver AWS::AppSync::Resolver in code property, I am not sure why I’m getting this error, any help would be great.
To answering my own question, The way I resolve it via two ways.
1. We can write whole Resolver code in YML Cloudformation in Code property like below. Make sure your resolver code should be inside of your Code property and use "|" special character (Multi-line code) after Code property.
AppSyncJsResolver:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GettAtt Graphql.APiId
Kind: PIPELINE
Code: |
import { util } from '#aws-appsync/utils';
export function request(ctx) {
const {source, args} = ctx
return {
operation: 'Invoke',
payload: { field: ctx, arguments: args, source },
};
}
export function response(ctx) {
util.error("Failed to fetch relatedPosts", "LambdaFailure",ctx.prev.result)
return ctx.result;
}
TypeName: Query
FieldName: getInfo
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
PipelineConfig:
Functions:
- !GetAtt AppSyncFunction.FunctionId
2. If you want to keep your business logic out of YML file and keep it separate then you can use CodeS3Location property in your javascript resolver like below.
first create bucket in S3 and store your javascript resolver file with your resolver code in bucket. make sure you give enough IAM permission to your appsync to access your S3 bucket.
After above step you can rewrite your YML Cloudformation like below
AppSyncJsResolver:
Type: AWS::AppSync::Resolver
Properties:
ApiId: !GettAtt Graphql.APiId
Kind: PIPELINE
CodeS3Location:s3://my-bucket-name/my-filename.js
TypeName: Query
FieldName: getInfo
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0
PipelineConfig:
Functions:
- !GetAtt AppSyncFunction.FunctionId
Hope this help others and will contribute more about Javascript Resolver so it will be easier for other to find more complex solutions and get as much as resources about Javascript Resolver. Thanks to #Graham Hesketh for your suggestions.
Im trying so hard to run this "job project" from Github, so i put my API keys on .env created file locate on root of api directory
APP_ID=00000000
API_KEY=000000000000000000000000
...and put this code on my config.js :
require('dotenv').config({ path: 'api/.env' })
module.exports = {
APP_ID: process.env.APP_ID,
API_KEY: process.env.API_KEY,
BASE_URL: 'https://api.adzuna.com/v1/api/jobs',
BASE_PARAMS: 'search/1?&results_per_page=20&content-
type=application/json',
};
But it looks like the request failing with this error net::ERR_CONNECTION_REFUSED:
I think, it dosent happens to take the api keys values, because accessKeyId is always empty
So, does there any other way to hide the api keys for it to work ?
Thanks
I am trying to configure a proxy for my API requests using http-proxy-middleware, which the create react app docs suggest. I set up my proxy like this, in the setupProxy.js file:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
createProxyMiddleware("/post", {
target: 'https://postman-echo.com',
changeOrigin: true,
logLevel: 'debug'
})
);
};
then, I do a simple POST to an endpoint:
const response = await fetch("/post", {
method: 'POST',
headers: {
'content-type': 'application/json;charset=UTF-8'
},
body: JSON.stringify({ foo1: "bar1", foo2: "bar2" })
});
console.log(await response.json());
According to the http-proxy-middleware docs, I should expect a proxy that does something like this:
[HPM] POST /post -> https://postman-echo.com/post
But instead, the debugger shows this:
[HPM] POST /post -> https://postman-echo.com
The path, /post, does not get appended to the proxy request. The target should actually be https://postman-echo.com/post. My client gets a 404 error because https://postman-echo.com on its own does not match anything on the backend.
If it did reroute correctly, I should expect the same results as a CURL request
curl -X POST -F 'foo1=bar1' -F 'foo2=bar2' https://postman-echo.com/post
{"args":{},"data":{},"files":{},"form":{"foo1":"bar1","foo2":"bar2"},"headers":{"x-forwarded-proto":"https","x-forwarded-port":"443","host":"postman-echo.com","x-amzn-trace-id":"Root=1-61200c54-7b5809be3e78040f09edcd42","content-length":"240","user-agent":"curl/7.64.1","accept":"*/*","content-type":"multipart/form-data; boundary=------------------------bb54b419e41f4a4a"},"json":null,"url":"https://postman-echo.com/post"}%
But I 404 because the path is not added. Why is the path being left out?
I created a simple app that recreates my issue. This looks similar to this issue but they are not the same (I am using the same syntax as the answer suggests).
I got it working. The problem was that I was testing with an endpoint that 404'd. I got confused because the debugger doesn't append /post to the end of the log like the docs say it should.
I want to import a helper function into a Node.js Lambda function in Serverless (AWS). I've tried using Layer functions, as well as a wrapper module for Serverless - but nothing has worked so far.
This is the function I want to use with other Lambda functions - helperFunc.js:
class HelperClass { ... } //a helper class that I want to use in other functions
module.exports = HelperClass
This is one of my Lambda functions. It also serves an API Gateway endpoint - users.js:
"use strict";
const helperClass = require('./helperFunc') //I don't know how to do this
module.exports.get = (event, context, callback) => {
const params = { ... } //DynamoDB Params
// a bunch of code that uses the helper class wrapper
callback(null, {
headers: {
"Access-Control-Allow-Origin" : "*", // Required for CORS support to work
"Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
}, body: JSON.stringify(res), statusCode: 200})
}
};
This is what my serverless.yml currently looks like:
...
getUsers:
handler: src/users.get
events:
- http:
path: users
method: get
authorizer: authFunc
And this what my project directory looks like:
./
serverless.yml
./src
helperFunc.js
users.js
./auth
UPDATE 1: I was finally able to achieve this functionality using Lambda Layers. However, it still feels as if it's not the best way to do this mainly due to the complex directory set-up.
Here's the updated project directory:
./
serverless.yml
./layers/helperFunc/nodejs/node_modules/helperFunc
index.js
./src
users.js
./auth
And here is the updated serverless.yml file:
layers:
helperFunc:
path: layers/helperFunc
...
functions:
getUsers:
handler: src/users.get
layers:
- {Ref: HelperFuncLambdaLayer} # referencing the layer in cf
events:
- http:
path: users
method: get
authorizer: authFunc
The solution is to use serverless-webpack-plugin.
I want every hapi route path to start with a prefix (/api/1) without adding it to each route. Is this possible?
The following route should be available with path /api/1/pets and not /pets
const Hapi = require('hapi');
const server = new Hapi.Server();
server.route({
method: 'GET',
path: '/pets'
})
Seems you can't do it globally for the whole application. But there's a possibility to add prefixes for all the routes defined inside a plugin:
server.register(require('a-plugin'), {
routes: {
prefix: '/api/1'
}
});
Hope this helps.
Just in case, if you're gonna try to add base path via events for new routes, it's not gonna work.
The best bet would be to use a constant in the paths -
server.route({
method: 'GET',
path: constants.route.prefix + '/pets')
});
and have the constant defined in a static constants.js file
I don't see such option in Hapi docs. Still, I can suggest you a small workaround. Make some function:
function createRoutePath(routePath) {
return `/api/1${routePath}`;
}
And then use it this way:
server.route({
method: 'GET',
path: createRoutePath('/pets')
});
UPDATE:
As another workaround, leave all as is, and setup proxy web server. For example nginx.