Reputation: 7591
I followed the directions at Apollo Server for deploying as AWS lambda. https://www.apollographql.com/docs/apollo-server/deployment/lambda/ used the serverless framework and it is working fine in region east-2.
I extended the example to use a PostGres DB for queries (I used the npm sequalize package). The same code is used fine when I run as an ApolloServer and a local postresql DB. I made it so that it can also switch to an apollo-server-lambda. I have an if statement that changes the connection to the DB depending on if it is a lambda or not.
The issue I have is that queries that do not hit the DB work fine. But graphql queries to the DB return:
{
"error": {
"message": "Internal server error"
}
}
OK, So now how do I debug a nodejs lambda function?
The lambda management console does have a test operation. I redefiend the hello world test to use this as the test:
{
"operationName": null,
"variables": {},
"query": "{users {id firstName lastName addressNumber streetName city email createdAt updatedAt }}"
}
But that does not seem to be the right way to invoke the lambda function. Because the log returns:
{
"body": "Apollo Server supports only GET/POST requests.",
"statusCode": 405,
"headers": {
"Allow": "GET, POST"
}
}
const { ApolloServer } = require('apollo-server')
const { ApolloServer: ApolloServerLambda } = require('apollo-server-lambda')
const { typeDefs, resolvers, connect } = require('./schema.js')
// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
async function setup(server) {
let { url } = await server.listen()
console.log(`🚀 Server ready at ${url}`)
await connect("local")
}
async function awsSetup() {
await connect("aws")
}
if (process.env.USERNAME == 'ysg4206') {
const server = new ApolloServer({ typeDefs, resolvers })
setup(server)
} else {
const server = new ApolloServerLambda({ typeDefs, resolvers })
//awsSetup()
exports.graphqlHandler = server.createHandler({
playground: true,
introspection: true,
cors: {
origin: '*',
credentials: true,
},
context: ({ event, context }) => { return (
{
headers: event.headers,
functionName: context.functionName,
event,
context
})
}
})
}
const { gql } = require('apollo-server')
const { DB } = require('./db')
const { GraphQLDateTime } = require('graphql-iso-date')
exports.typeDefs = gql`
scalar DateTime
type User {
id: Int
"English First Name"
firstName: String
lastName: String
addressNumber: Int
streetName: String
city: String
email: String
createdAt: DateTime
updatedAt: DateTime
}
input UserType {
"Hebrew First Name"
firstName: String
lastName: String
addressNumber: Int
streetName: String
city: String
email: String
}
type Query {
users: [User]
findUser(firstName: String): User
hello(reply: String): String
}
type Mutation {
addUser(user: UserType): User!
}
type Subscription {
newUser: User!
}
`
exports.resolvers = {
Query: {
// users: async () => {
// let users = await DB.findAll()
// return users
// },
users: () => DB.findAll(),
findUser: async (_, { firstName }) => {
let who = await DB.findFirst(firstName)
return who
},
hello: (_, { reply }, context) => {
console.log(`hello with reply ${reply}`)
console.log(`context : ${JSON.stringify(reply, null, 4)}`)
return reply
}
},
Mutation: {
addUser: async (_, args) => {
let who = await DB.addUser(args.user)
return who
}
}
}
exports.connect = async function connect(where) {
await DB.dbSetup(where)
await DB.populate()
let users = await DB.findAll()
console.log(users)
}
Upvotes: 3
Views: 2775
Reputation: 1082
For anyone deploying their serverless app and receiving {"message": "Internal server error"}
when going to their AWS endpoint
I was running into this issue for a whole day. I made a couple of changes but I think what did it for me was including context when initializing my ApolloServer:
const server = new ApolloServer({
...serverConfig, // typeDefs and resolvers
context: ({ event, context }) => ({
headers: event.headers,
functionName: context.functionName,
event,
context,
}),
playground: {
endpoint: '/dev/graphql',
},
});
See this link: https://www.apollographql.com/docs/apollo-server/deployment/lambda/#getting-request-info
When debugging I recommend two things
serverless offline
, go to the local endpoint and see if you get any errors (you will need to install the serverless offline plugin and include it in your serverless.yml
)Also some related additional information if you're using a yarn monorepo and typescript -
Make sure to compile and transpile the typescript code. See this article. My code:
yarn add webpack serverless-webpack
yarn add -D webpack-node-externals
webpack.config.js
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');
module.exports = {
entry: slsw.lib.entries,
target: 'node',
mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
optimization: {
minimize: false,
},
performance: {
hints: false,
},
devtool: 'nosources-source-map',
externals: [nodeExternals()],
module: {
rules: [
{
test: /\.ts$/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
targets: {
node: true,
},
},
],
'@babel/typescript',
],
},
include: [__dirname],
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
libraryTarget: 'commonjs2',
path: path.join(__dirname, '.webpack'),
filename: '[name].js',
},
};
serverless.yml
# serverless.yml
service: apollo-lambda
plugins:
- serverless-webpack
- serverless-offline
custom:
webpack:
webpackConfig: ./webpack.config.js
includeModules: true
provider:
name: aws
runtime: nodejs12.x
functions:
graphql:
# this is formatted as <FILENAME>.<HANDLER>
handler: dist/server.graphqlHandler
environment:
SLS_DEBUG: true
events:
- http:
path: graphql
method: post
cors: true
integration: lambda-proxy
- http:
path: graphql
method: get
cors: true
integration: lambda-proxy
tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
"outDir": "./dist",
"strict": true,
"lib": ["es5"],
"esModuleInterop": true,
"types": ["react", "jest"]
}
}
I also added a script called deploy
which deletes the dist
folder, recreates the dist
folder after compiling the typescript (see outDir
in tsconfig.json
), and then runs serverless deploy
:
package.json
{
...
"main": "dist/server.js",
"scripts": {
...
"deploy": "rimraf dist && npx tsc && serverless deploy",
},
...
}
Note: you'll need to install rimraf
globally for the script to work (npm install -g rimraf
)
Upvotes: 2
Reputation: 3777
Debugging lambda functions is hard! You can try digging through logs if they are configured through CloudWatch, but that won't always give you a workable stack trace, and it's challenging to find the exact invocation you're looking for.
Have you tried deploying with the Serverless Framework Dashboard? That will help give you a full stack trace along with your logs. You can get started by simply running the serverless
command in your application root directory.
More information is available here
Full disclosure - I work for Serverless Inc. on the Serverless Framework.
Upvotes: 1