Skip to content

Commit

Permalink
Merge feat-typeorm-create-connection
Browse files Browse the repository at this point in the history
  • Loading branch information
Romakita committed Dec 18, 2019
2 parents 12fd940 + 7630cda commit 666238b
Show file tree
Hide file tree
Showing 17 changed files with 258 additions and 94 deletions.
19 changes: 8 additions & 11 deletions docs/tutorials/snippets/typeorm/typeorm-async-provider.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import {Configuration, registerProvider} from "@tsed/di";
import {Connection, ConnectionOptions, getConnectionManager} from "typeorm";
import {createConnection} from "@tsed/typeorm";
import {Connection, ConnectionOptions} from "typeorm";

export const CONNECTION = Symbol.for("CONNECTION"); // declare your own
export const CONNECTION = Symbol.for("CONNECTION"); // declare your own symbol
export type CONNECTION = Connection; // Set alias types (optional)

const CONNECTION_NAME = "default"; // change the name according to your server configuration

registerProvider({
provide: CONNECTION,
deps: [Configuration],
async useAsyncFactory(configuration: Configuration) {
const settings = configuration.get<ConnectionOptions>("customSettings")!;

const connection = getConnectionManager().create(settings!);
await connection.connect();

// Add hook to close connection when server is killed
// @ts-ignore
connection.$onDestroy = () => connection.close();
const settings = configuration.get<ConnectionOptions[]>("typeorm")!;
const connectionOptions = settings.find(o => o.name === CONNECTION_NAME);

return connection;
return createConnection(connectionOptions!);
}
});
8 changes: 8 additions & 0 deletions docs/tutorials/snippets/typeorm/typeorm-entity-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {EntityRepository} from "@tsed/typeorm";
import {Repository} from "typeorm";
import {User} from "../entity/User";

@EntityRepository(User)
export class UserRepository extends Repository<User> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {Inject, Injectable} from "@tsed/di";
import {UserRepository} from "./repository/UserRepository";

@Injectable()
export class OtherService {
constructor(userRepository: UserRepository) {

}
}
33 changes: 18 additions & 15 deletions docs/tutorials/typeorm.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ meta:
- name: keywords
content: ts.ed express typescript typeorm node.js javascript decorators
---
# TypeORM <Badge text="beta" type="warn"/> <Badge text="Contributors are welcome" />
# TypeORM <Badge text="Contributors are welcome" />

<Banner src="https://github.com/typeorm/typeorm/raw/master/resources/logo_big.png" href="https://typeorm.io/" height="128" />

Expand All @@ -16,8 +16,6 @@ This tutorial provide two way to create connection:

Additionally, this topic show you how you can use Entity from Typeorm with Ts.ED controller (on bottom of this page).

## Solution with @tsed/typeorm

::: tip
You can find a working example on [TypeORM here](https://github.com/TypedProject/tsed-example-typeorm).
:::
Expand All @@ -28,6 +26,7 @@ Currently, `@tsed/typeorm` allows you:

- Configure one or more TypeORM connections via the `@ServerSettings` configuration. All databases will be initialized when the server starts during the server's `OnInit` phase.
- Use the Entity TypeORM as Model for Controllers, AJV Validation and Swagger.
- Declare connection with asyncProvider or automatically by server configuration.

### Installation

Expand All @@ -48,26 +47,19 @@ TypeORMService let you to retrieve an instance of TypeORM Connection.

For more information about TypeORM look his documentation [here](https://github.com/typeorm/typeorm);

## Solution with Async provider

This solution let you manage your connection and create provider around this with `useAsyncFactory` feature (See [custom providers](/docs/custom-provider.md))
### Declare your connection as provider

::: warning
`@tsed/typeorm` must not installed on your project but you have to install `typeorm` npm module instead.
:::
It also possible to create your connection with `useAsyncFactory` feature (See [custom providers](/docs/custom-provider.md))
This approach allow to inject your connection as a Service to another one.

### Declare you connection as provider
To create a new connection, declare your custom provider as follow:

<<< @/docs/tutorials/snippets/typeorm/typeorm-async-provider.ts

### Inject connection in another service
Then inject your connection to another service or provide like this:

<<< @/docs/tutorials/snippets/typeorm/typeorm-injection-async-provider.ts

### Configure your connection

<<< @/docs/tutorials/snippets/typeorm/typeorm-custom-configuration.ts

## Use Entity TypeORM with Controller

We need to define an Entity TypeORM like this and use Ts.ED Decorator to define the JSON Schema.
Expand All @@ -81,4 +73,15 @@ We can use this model with a Controller like that:

<<< @/docs/tutorials/snippets/typeorm/typeorm-entity-controller.ts

## EntityRepository

TypeORM provide EntityRepository to manipulate an Entity. TsED provide a decorator to declare @@EntityRepository@@
for both TypeORM and TsED. This decorator is useful if you have to inject the repository to another TsED service, controller or provider.

Here a quick example:

<<< @/docs/tutorials/snippets/typeorm/typeorm-entity-repository.ts

Then inject your repository to another service:

<<< @/docs/tutorials/snippets/typeorm/typeorm-injection-entity-repository.ts
24 changes: 18 additions & 6 deletions packages/typeorm/src/TypeORMModule.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {Constant, Module, OnDestroy, OnInit} from "@tsed/common";
import {Configuration, OnDestroy, registerProvider} from "@tsed/common";
import {ConnectionOptions} from "typeorm";
import {TypeORMService} from "./services/TypeORMService";

@Module()
export class TypeORMModule implements OnInit, OnDestroy {
@Constant("typeorm", {})
export class TypeORMModule implements OnDestroy {
private settings: {[key: string]: ConnectionOptions};

constructor(private typeORMService: TypeORMService) {}
constructor(configuration: Configuration, private typeORMService: TypeORMService) {
this.settings = configuration.get<{[key: string]: ConnectionOptions}>("typeorm") || {};
}

$onInit(): Promise<any> | void {
async init(): Promise<any> {
const promises = Object.keys(this.settings).map(key => this.typeORMService.createConnection(key, this.settings[key]));

return Promise.all(promises);
Expand All @@ -19,3 +19,15 @@ export class TypeORMModule implements OnInit, OnDestroy {
return this.typeORMService.closeConnections();
}
}

registerProvider({
provide: TypeORMModule,
deps: [Configuration, TypeORMService],
injectable: false,
async useAsyncFactory(configuration: Configuration, typeORMService: TypeORMService) {
const typeORMModule = new TypeORMModule(configuration, typeORMService);
await typeORMModule.init();

return typeORMModule;
}
});
26 changes: 26 additions & 0 deletions packages/typeorm/src/decorators/entityRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {applyDecorators, Type} from "@tsed/core";
import {Configuration, registerProvider} from "@tsed/di";
import {EntityRepository as TypeORMEntityRepository, getCustomRepository} from "typeorm";
import {TypeORMModule} from "../TypeORMModule";

/**
* Declare a new typeORM EntityRepository which can injected to TsED service.
* @param model
* @decorator
*/
export function EntityRepository(model: any) {
return applyDecorators(TypeORMEntityRepository(model), (target: Type<any>) => {
registerProvider({
provide: target,
imports: [TypeORMModule],
deps: [Configuration],
useFactory(configuration: Configuration) {
if (configuration.get("typeorm")) {
return getCustomRepository(target);
}

return {};
}
});
});
}
3 changes: 2 additions & 1 deletion packages/typeorm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ declare global {
}
}
}

export * from "./utils/createConnection";
export * from "./services/TypeORMService";
export * from "./decorators/entityRepository";
export * from "./TypeORMModule";
40 changes: 24 additions & 16 deletions packages/typeorm/src/services/TypeORMService.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import {Service} from "@tsed/common";
import {$log} from "ts-log-debug";
import {Inject, InjectorService, Service} from "@tsed/common";
import {Connection, ConnectionManager, ConnectionOptions, getConnectionManager} from "typeorm";
import {createConnection} from "../utils/createConnection";

@Service()
export class TypeORMService {
/**
*
* @type {Map<any, any>}
* @deprecated
* @type {"typeorm".ConnectionManager}
* @private
*/
readonly instances: Map<string, Connection> = new Map();
readonly connectionManager: ConnectionManager = getConnectionManager();
@Inject(InjectorService)
private injector: InjectorService;

/**
*
* @type {"typeorm".ConnectionManager}
* @private
* @type {Map<any, any>}
* @deprecated
*/
readonly connectionManager: ConnectionManager = getConnectionManager();
// istanbul ignore next
get instances() {
return this.connectionManager;
}

/**
*
Expand All @@ -29,19 +34,18 @@ export class TypeORMService {
return await this.get(key)!;
}

$log.info(`Create connection with typeorm to database: ${key}`);
$log.debug(`options: ${JSON.stringify(settings)}`);
this.injector.logger.info(`Create connection with typeorm to database: ${key}`);
this.injector.logger.debug(`options: ${JSON.stringify(settings)}`);

try {
const connection = this.connectionManager.create(settings!);
await connection.connect();
$log.info(`Connected with typeorm to database: ${key}`);
this.instances.set(key, connection);
const connection = await createConnection({...settings, name: key});

this.injector.logger.info(`Connected with typeorm to database: ${key}`);

return connection;
} catch (err) {
/* istanbul ignore next */
$log.error(err);
console.error(err);
/* istanbul ignore next */
process.exit();
}
Expand All @@ -65,7 +69,11 @@ export class TypeORMService {
}

closeConnections(): Promise<any> {
const promises = this.connectionManager.connections.map(instance => instance.close());
const promises = this.connectionManager.connections.map(instance => {
if (instance.isConnected) {
return instance.close();
}
});

return Promise.all(promises);
}
Expand Down
23 changes: 23 additions & 0 deletions packages/typeorm/src/utils/createConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {ConnectionOptions, getConnectionManager} from "typeorm";

const connections = new Map();

export async function createConnection(connectionOptions: ConnectionOptions) {
const connectionManager = getConnectionManager();
const name = connectionOptions.name!;

if (!connections.has(name)) {
const connection = connectionManager.create(connectionOptions!);
connections.set(name, connection.connect());
}

await connections.get(name);

const connection = connectionManager.get(name);

// Add hook to close connection when server is killed
// @ts-ignore
connection.$onDestroy = connection.$onDestroy || (() => connection.isConnected && connection.close());

return connection;
}
43 changes: 0 additions & 43 deletions packages/typeorm/test/TypeORMModule.spec.ts

This file was deleted.

35 changes: 35 additions & 0 deletions packages/typeorm/test/helpers/Server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {ServerLoader, ServerSettings} from "@tsed/common";
import * as Path from "path";
import "../../src/TypeORMModule";
import "./connections/ConnectionProvider";

const cookieParser = require("cookie-parser"),
bodyParser = require("body-parser"),
compress = require("compression"),
methodOverride = require("method-override");

const rootDir = Path.resolve(__dirname);

@ServerSettings({
rootDir,
port: 8001,
httpsPort: false,
logger: {
level: "info",
logRequest: true
}
})
export class Server extends ServerLoader {
public $beforeRoutesInit(): void {
this
.use(bodyParser.json())
.use(
bodyParser.urlencoded({
extended: true
})
)
.use(cookieParser())
.use(compress({}))
.use(methodOverride());
}
}

0 comments on commit 666238b

Please sign in to comment.