Search code examples
node.jsaws-lambdatypeormserverless-frameworkaws-serverless

Typeorm doesn't work with Serverless Framework AWS Lambda


Well, basically I get this error when I try to serverless deploy or serverless offline:

PS C:\Users\joaov\Desktop\lambda-ts> serverless offline
Running "serverless" from node_modules
X [ERROR] Could not resolve "mock-aws-s3"

    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28:
      43 │     const AWSMock = require('mock-aws-s3');
         ╵                             ~~~~~~~~~~~~~

  You can mark the path "mock-aws-s3" as external to exclude it from the bundle, which will remove 
  this error. You can also surround this "require" call with a try/catch block to handle this      
  failure at run-time instead of bundle-time.

X [ERROR] Could not resolve "nock"

    node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23:
      112 │   const nock = require('nock');
          ╵                        ~~~~~~

  You can mark the path "nock" as external to exclude it from the bundle, which will remove this   
  error. You can also surround this "require" call with a try/catch block to handle this failure at
  run-time instead of bundle-time.

X [ERROR] Could not resolve "pg-native"

    node_modules/pg/lib/native/client.js:4:21:
      4 │ var Native = require('pg-native')
        ╵                      ~~~~~~~~~~~

  You can mark the path "pg-native" as external to exclude it from the bundle, which will remove
  this error. You can also surround this "require" call with a try/catch block to handle this   
  failure at run-time instead of bundle-time.

Environment: win32, node 16.14.0, framework 3.14.0 (local) 3.14.0v (global), plugin 6.2.1, SDK 4.3.2
Docs:        docs.serverless.com
Support:     forum.serverless.com
Bugs:        github.com/serverless/serverless/issues

Error:
Error: Build failed with 3 errors:
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:43:28: ERROR: Could not resolve "mock-aws-s3"   
node_modules/@mapbox/node-pre-gyp/lib/util/s3_setup.js:112:23: ERROR: Could not resolve "nock"
node_modules/pg/lib/native/client.js:4:21: ERROR: Could not resolve "pg-native"
    at failureErrorWithLog (C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:1603:15) 
    at C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:1249:28
    at runOnEndCallbacks (C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:1162:65)   
    at buildResponseToResult (C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:1247:7)
    at C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:1356:14
    at C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:666:9
    at handleIncomingPacket (C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:763:9)
    at Socket.readFromStdout (C:\Users\joaov\Desktop\lambda-ts\node_modules\esbuild\lib\main.js:632:7)
    at Socket.emit (node:events:520:28)
    at Socket.emit (node:domain:475:12)
    at addChunk (node:internal/streams/readable:315:12)
    at readableAddChunk (node:internal/streams/readable:289:9)
    at Socket.Readable.push (node:internal/streams/readable:228:10)
    at Pipe.onStreamRead (node:internal/stream_base_commons:190:23)
PS C:\Users\joaov\Desktop\lambda-ts>

This is the code: package.json dependencies:

"dependencies": {
        "aws-lambda": "^1.0.7",
        "bcrypt": "^5.0.1",
        "dotenv": "^16.0.0",
        "pg": "^8.7.3",
        "reflect-metadata": "^0.1.13",
        "typeorm": "^0.3.6",
        "uuid": "^8.3.2",
        "yup": "^0.32.11"
    },
    "devDependencies": {
        "@serverless/typescript": "^3.8.0",
        "@types/aws-lambda": "^8.10.93",
        "@types/bcrypt": "^5.0.0",
        "@types/node": "^17.0.25",
        "@types/pg": "^8.6.5",
        "@types/uuid": "^8.3.4",
        "esbuild": "^0.14.36",
        "serverless": "^3.14.0",
        "serverless-esbuild": "^1.26.2",
        "serverless-offline": "^8.7.0",
        "ts-node": "^10.7.0",
        "typescript": "^4.6.3"
    }
// src/configs/consts.ts

import {
    AWS_RDS_POSTGRES,
    AWS_RDS_POSTGRES_DB,
    AWS_RDS_POSTGRES_PASSWORD,
    AWS_RDS_POSTGRES_PORT,
    AWS_RDS_POSTGRES_USER
} from '@configs/env_vars';

export const HOST = AWS_RDS_POSTGRES;
export const PORT = AWS_RDS_POSTGRES_PORT;
export const DATABASE = AWS_RDS_POSTGRES_DB;
export const USER = AWS_RDS_POSTGRES_USER;
export const PASSWORD = AWS_RDS_POSTGRES_PASSWORD;
// src/database/config.ts

import { DataSource } from 'typeorm';
import { User } from '@entities/User';
import { HOST, PORT, DATABASE, USER, PASSWORD } from '@configs/consts';

export const connConfig = new DataSource({
    type: 'postgres',
    host: HOST,
    port: Number(PORT),
    database: DATABASE,
    username: USER,
    password: PASSWORD,
    synchronize: false,
    entities: [User],
    migrations: []
});
// src/entities/User.ts

import { BaseEntity, Column, PrimaryGeneratedColumn } from 'typeorm';

export class User extends BaseEntity {
    @PrimaryGeneratedColumn('uuid')
    id: string;

    @Column()
    name: string;

    @Column()
    age: number;

    @Column()
    password: string;
}
// src/lambdas/CreateUser

import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import { handle } from '@services/CreateUser';
import { connConfig } from '@database/config';
import { userSchema } from '@validations/UserSchema';

export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
    try {
        const { body } = event;
        const parsedBody = JSON.parse(body);
        userSchema.isValid(parsedBody).catch(() => {
            throw Error('Invalid user data.');
        });

        const user = await handle(connConfig, parsedBody);

        if (!user) {
            return {
                statusCode: 400,
                body: JSON.stringify({
                    message: "Couldn't create user."
                })
            };
        }

        return {
            statusCode: 200,
            body: JSON.stringify({
                message: 'User created successfully.',
                user
            })
        };
    } catch (error) {
        return {
            statusCode: 500,
            body: JSON.stringify({
                error: (error as Error).message
            })
        };
    }
};
// src/services/CreateUser

import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { hash } from 'bcrypt';
import { User } from '@entities/User';

export const handle = async (dataSource: DataSource, userData: User): Promise<User | undefined> => {
    try {
        await dataSource.initialize();
        const user = new User();
        const { name, password, age } = userData;
        user.name = name;
        user.age = age;

        const hashedPassword = await hash(password, 10);
        user.password = hashedPassword;

        const savedUser = await dataSource.manager.save(user);

        return savedUser;
    } catch (error) {
        throw Error("Couldn't connect to database.");
    }
};
// src/validations/UserSchema

import * as yup from 'yup';

export const userSchema = yup.object().shape({
    name: yup.string().required(),
    age: yup.number().required(),
    password: yup.string().required()
});

What should I do in order to fix that and get a working aws-lambda + typeorm?


Solution

  • Looks like you're using esbuild to bundle your lambdas? Some node modules don't like being bundled with esbuild and you have to add them as externals. Try adding to esbuild config:

    external:
     - pg-native
     - mock-aws-s3
     - nock