ArsenyYankovsky / typeorm-aurora-data-api-driver

A bridge between TypeORM and Aurora Data API
MIT License
175 stars 38 forks source link

Does this driver support ActiveRecord pattern without calling `useConnection`? #61

Open andrestone opened 3 years ago

andrestone commented 3 years ago

TypeORM connections can be consumed as global scope objects like so:

  // inside async context
  await createConnection({
    /* connection details */  
  });
  const user = new User();
  user.firstName = "Timber";
  await user.save();  // error using this driver but works with native typeorm (see below)

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:

  // inside async context
  const connection = await createConnection({
    /* connection details */  
  });
  Article.useConnection(connection);
  const user = new User();
  user.firstName = "Timber";
  await user.save();  // works with this driver

But ideally, this shouldn't be required?

carlocorradini commented 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.