Open andrestone opened 3 years ago
@andrestone
See https://medium.com/safara-engineering/wiring-up-typeorm-with-serverless-5cc29a18824f
It explain how to use (and reuse) TypeORM connection in a serverless environment.
I am currently using "the pattern" without any particular issues.
Here is my code:
export class Database {
public static readonly DEFAULT_CONNECTION_NAME: string = 'default';
public static readonly DEFAULT_CONNECTION_OPTIONS: ConnectionOptions = {
type: 'aurora-data-api-pg',
database: 'database_name',
name: Database.DEFAULT_CONNECTION_NAME,
region: 'us-east-1',
secretArn: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:password',
resourceArn: 'arn:aws:rds:us-east-1:123456789012:cluster:password',
entities: [User],
...(process.env.NODE_ENV !== 'production' && {
synchronize: true,
serviceConfigOptions: {
endpoint: 'http://127.0.0.1:5432',
},
}),
};
private readonly connectionManager: ConnectionManager;
public constructor() {
this.connectionManager = getConnectionManager();
}
public async getOrCreateConnection(
options: Omit<
BaseConnectionOptions,
| 'type'
| 'entities'
| 'subscribers'
| 'migrations'
| 'migrationsTableName'
| 'migrationsTransactionMode'
| 'namingStrategy'
| 'migrationsRun'
| 'entityPrefix'
| 'cli'
> = {},
): Promise<Connection> {
let connection: Connection;
const connectionOptions: ConnectionOptions = {
...Database.DEFAULT_CONNECTION_OPTIONS,
...options,
};
const connectionName: string = connectionOptions.name || Database.DEFAULT_CONNECTION_NAME;
if (this.connectionManager.has(connectionName)) {
connection = this.connectionManager.get(Database.DEFAULT_CONNECTION_NAME);
if (!connection.isConnected) {
connection = await connection.connect();
}
} else {
connection = await createConnection(connectionOptions);
}
return connection;
}
}
And use it in the handler:
import 'reflect-metadata';
import type { APIGatewayProxyEventV2, APIGatewayProxyResultV2, Context } from 'aws-lambda';
import express from 'express';
import serverless from 'serverless-http';
import { Database } from './Database';
// Express
const app = express();
// Database
const database = new Database();
// Construct server handler
const handler = serverless(app);
// Export handler
module.exports.handler = async (
event: APIGatewayProxyEventV2,
context: Context,
): Promise<APIGatewayProxyResultV2> => {
context.callbackWaitsForEmptyEventLoop = false;
// Obtain a database connection
await database.getOrCreateConnection();
// Call server handler
const result = await handler(event, context);
return result;
};
If you want to obtain the connection:
const database = new Database();
// 'default' connection name
const connection = await database.getOrCreateConnection();
// 'anothername'
const connection_2 = await database.getOrCreateConnection({ name: 'anothername' });
// Remember that connection !== connection_2
Hope it helps 🤯
PS: In addition you can customize the connection options when calling the getOrCreateConnection method. PS: Calling getRepository(...), getConnection(...), etc... also works outside the handler.
TypeORM connections can be consumed as global scope objects like so:
When using this driver, the above code throws this error:
ConnectionNotFoundError: Connection "default" was not found.
While using native typeorm connection to postgresql it works.
This driver also works when calling the
useConnection
method on the entity like so:But ideally, this shouldn't be required?