[SOLVED] Can't connect to Atlas cluster from my lambda using mongoose

Issue

I am trying to connect to a cluster using the last example from mongoose site

Here are my files using node14 and typescript

  • src/index.ts
import { APIGatewayProxyHandler } from "aws-lambda"

export { list as productsList } from "./products"

export const list: APIGatewayProxyHandler = (event, context, callback) => {
  callback(null, {
    statusCode: 200,
    body: `Hello from ${process.env.AWS_SAM_BRANCH}`,
  })
}
  • src/utils.ts
import mongoose from "mongoose"
import { APIGatewayProxyResult, Callback } from "aws-lambda"

let mongoConnection: Promise<typeof mongoose> | null = null

export const connectMongoose = async () => {
  if (mongoConnection == null) {
    const mongoURI = `mongodb+srv://USER:[email protected]/myFirstDB?retryWrites=true&w=majority`
    mongoConnection = mongoose
      .connect(mongoURI, { serverSelectionTimeoutMS: 3000 })
      .then((mongooseReply) => {
        console.log({ mongooseReply })
        return mongoose
      })
      .catch((mongooseError) => {
        console.log({ mongooseError })
        return mongoose
      })
    await mongoConnection
  }
  return mongoConnection
}

export const errorHandler = (error: unknown, callback: Callback<APIGatewayProxyResult>) => {
  console.error("catchedError", error)
  if (error instanceof Error) {
    callback(null, {
      statusCode: 400,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ error: error.message }),
    })
  } else {
    callback(null, {
      statusCode: 500,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ error: "Internal server error" }),
    })
  }
}
  • src/products/index.ts
import { APIGatewayProxyHandler } from "aws-lambda"

import Model from "./model"
import { connectMongoose, errorHandler } from "../utils"

export const list: APIGatewayProxyHandler = (event, context, callback) => {
  try {
    connectMongoose()
    Model.find({}, (error: unknown, reply: unknown) => {
      if (error) throw error
      callback(null, {
        statusCode: 200,
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(reply),
      })
    })
  } catch (error) {
    errorHandler(error, callback)
  }
}
  • src/products/model.ts
import mongoose from "mongoose"

const model = mongoose.model(
  "Product",
  new mongoose.Schema(
    {
      title: {
        type: String,
        required: true,
        maxLength: 256,
      },
      description: {
        type: String,
        required: true,
        maxLength: 2048,
      },
      count: {
        type: Number,
        required: true,
        min: 0,
        max: 1000 * 1000,
      },
    },
    {
      timestamps: true,
      versionKey: false,
    }
  )
)

export default model

Here is the code in a repo is includes commands used to deploy with AWS SAM

There are 2 routes in my app

This works and returns Hello from test with status 200

This doesn’t work and returns {"message":"Internal Server Error"} with status 500

Here are the CloudWatch logs exported as CSV

timestamp,message
1647203544609,"START RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Version: $LATEST
"
1647203545742,"2022-03-13T20:32:25.685Z 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce    INFO    {
  mongooseReply: <ref *1> Mongoose {
    connections: [ [NativeConnection] ],
    models: { Product: Model { Product } },
    events: EventEmitter {
      _events: [Object: null prototype] {},
      _eventsCount: 0,
      _maxListeners: undefined,
      [Symbol(kCapture)]: false
    },
    options: {
      pluralization: true,
      autoIndex: true,
      autoCreate: true,
      [Symbol(mongoose:default)]: true
    },
    _pluralize: [Function: pluralize],
    Schema: [Function: Schema] {
      reserved: [Object: null prototype],
      Types: [Object],
      ObjectId: [Function]
    },
    model: [Function (anonymous)],
    plugins: [ [Array], [Array], [Array], [Array], [Array], [Array] ],
    default: [Circular *1],
    mongoose: [Circular *1]
  }
}
"
1647203549616,"END RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce
"
1647203549616,"REPORT RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce   Duration: 5005.75 ms    Billed Duration: 5000 ms    Memory Size: 128 MB Max Memory Used: 76 MB  Init Duration: 366.30 ms    
"
1647203549616,"2022-03-13T20:32:29.616Z 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Task timed out after 5.01 seconds

"

Solution

As explained in this GitHub issue

A few suggestions:

  • You should either choose between a full callback approach and a full promise approach
  • Don’t mix async / await with .then syntax when you can avoid it
import mongoose from "mongoose"
import { APIGatewayProxyHandler } from "aws-lambda"

let mongoConnection: Promise<typeof mongoose> | null = null

const connectMongoose = async () => {
  if (mongoConnection == null) {
    const mongoURI = `mongodb+srv://YOUR_CLUSTER_URL`
    mongoConnection = mongoose
      .connect(mongoURI, { serverSelectionTimeoutMS: 3000 })
      .then((mongooseReply) => {
        console.log({ mongooseReply })
        return mongoose
      })
      .catch((mongooseError) => {
        console.log({ mongooseError })
        return mongoose
      })
    await mongoConnection
  }
  return mongoConnection
}

const Model = mongoose.model(
  "Product",
  new mongoose.Schema(
    {
      title: String,
      description: String,
    },
    {
      timestamps: true,
      versionKey: false,
    }
  )
)

export const myRoute: APIGatewayProxyHandler = async (event, context) => {
  try {
    await connectMongoose();
    const reply = await Model.find({}).exec();
    return {
      statusCode: 200,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(reply),
    };
  } catch (error) {
    return {
     statusCode: 400,
     headers: { "Content-Type": "application/json" },
     body: "Server error",
    };
  }
}

Answered By – Luis Marroquin

Answer Checked By – Candace Johnson (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *