-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add TypeORM support #5801
Conversation
af3d788
to
82385ba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a quick skim and noticed some minor issues.
Also, should we have a booter for TypeORM entities, similar to the ones for lb4 models?
Other than that, everything else LGTM.
packages/typeorm/LICENSE
Outdated
@@ -0,0 +1,25 @@ | |||
Copyright (c) IBM Corp. 2017,2019. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent copyright.
Ditto for the other copyright headers.
packages/typeorm/LICENSE
Outdated
@@ -0,0 +1,25 @@ | |||
Copyright (c) IBM Corp. 2017,2019. | |||
Node module: @loopback/repository |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent node module name header.
Ditto for the other module name headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, yea need to clean up.
Yes, there's a booter - https://github.com/strongloop/loopback-next/pull/5801/files#diff-2a23c8ed814e1a88b05bcacbeec6689c. |
82385ba
to
c7157f8
Compare
tsconfig.json
Outdated
"path": "packages/typeorm/tsconfig.json" | ||
}, | ||
{ | ||
"path": "sandbox/appa/tsconfig.json" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rm -rf sandbox/appa
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, need to clean up.
// Server, | ||
// Subscription, | ||
// } from '@loopback/core'; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the commented out code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
export function connection(connectionName?: string) { | ||
return inject( | ||
'', | ||
{decorator: '@typeormConnection'}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We typically use typeorm.connection
as the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. That's a remnant of something I was trying, will clean it up.
packages/typeorm/README.md
Outdated
constructor(options: ApplicationConfig = {}) { | ||
super(options); | ||
... | ||
this.component(TypeOrmComponent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am proposing to modify TypeOrmMixin
to call this.component(TypeOrmComponent)
automatically, to get the following user experience:
- As a LB4 app developer wanting to add TypeORM to my app, I need to change a single place in my app file only.
To be honest, I think it should be possible to design this extension to provide the app Mixin only, it should not be necessary to have a Component class. See how @loopback/repository
is implemented - it does not have any Component class either.
injection: Readonly<Injection>, | ||
session: ResolutionSession, | ||
) => { | ||
return getConnection(ctx, connectionName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, this creates "virtual" bindings that can be injected but that are not tracked by Context and therefore not visible in context-inspector UI.
Can we find a way how to create regular bindings for individual connections please?
The same comment applies to other inject-like decorators below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, this creates "virtual" bindings that can be injected but that are not tracked by Context and therefore not visible in context-inspector UI.
Can we find a way how to create regular bindings for individual connections please?
☝️ Please make sure this comment is either addressed as part of this original PR or there is a follow-up issue to fix binding inspection ASAP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: Create follow up task.
superClass: T, | ||
) { | ||
return class extends superClass { | ||
typeormConnectionOptions: ConnectionOptions[] = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we discussed in the call, I find the design where we keep an index-ordered list of connection options as suboptimal. Instead, I am proposing to store TypeORM's connection manager instance on the application object and create a method like app.connection
to register new connections with the app. The API should also allow the user to give each connection a name.
// in the mixin
return class extends superClass {
connectionManager: ConnectionManager;
constructor(...args: any[]) {
super(args);
this.connectionManager = new ConnectionManager();
}
connection(connectionConfig: ConnectionOptions) {
const connection = this.connectionManager.create(connectionConfig);
const name = connection.name;
this.bind(`connections.${name}`).toDynamicValue(() => this.connectionManager.getConnection(name));
}
}
A use case to consider:
- It should be possible to add new connections (and remove existing ones) at application runtime, after the app has booted & started. See How to build models, repositories and controllers dynamically at runtime #4296.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we discussed in the call, I find the design where we keep an index-ordered list of connection options as suboptimal. Instead, I am proposing to store TypeORM's connection manager instance on the application object and create a method like
app.connection
to register new connections with the app. The API should also allow the user to give each connection a name.
❗ IMO, this comment must be resolved before this pull request can be landed.
packages/typeorm/src/types.ts
Outdated
ExpoConnectionOptions, | ||
ExpoDriver, | ||
ExpoQueryRunner, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need that long list of exports 👆 and the long list of imports 👇 ? Isn't it enough to export * from 'typeorm';
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason they seem to have decided to export only these from ./driver
.
export {Driver} from "./driver/Driver";
export * from "./driver/mongodb/typings";
export * from "./driver/sqlserver/MssqlParameter";
export {ColumnType} from "./driver/types/ColumnTypes";
export * from "./driver/types/DatabaseType";
So we have to do the importing and exporting for our users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough 🤷
packages/typeorm/src/utils.ts
Outdated
* @param entityCtor - The entity constructor (e.g. `Product`) | ||
* @param options - Additional options | ||
*/ | ||
export function getModelSchemaRef<T extends object>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in the call, I am proposing to drop Ref
part and emit regular JSON schema. The ref
-based schemas were needed before OpenAPI schema consolidation was introduced. I believe with consolidation in place, we no longer need to worry about avoiding duplicated schema definitions in controller methods.
export function modelToJsonSchema<T extends object>(
ctor: Function & {prototype: T},
jsonSchemaOptions: JsonSchemaOptions<T> = {},
): JsonSchema {
const allColumns: ColumnMetadataArgs[]= getMetadataArgsStorage().columns;
const modelColumns = allColumns.filter(c => c.target === modelCtor);
const schema: JsonSchema = {
// set title (to modelCtor.modelName?), etc.
properties: {}
};
for (c of modelColumns) {
schema.properties[c.propertyName] = {
type: c.options.type,
}
}
return schema;
}
// getJsonSchema(modelCtor) --> call modelToJsonSchema under the hood + cache the result
// getModelSchema(modelCtor) --> convert getJsonSchema() to OpenApiSpec format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in the call, I am proposing to drop
Ref
part and emit regular JSON schema. Theref
-based schemas were needed before OpenAPI schema consolidation was introduced. I believe with consolidation in place, we no longer need to worry about avoiding duplicated schema definitions in controller methods.
❗I'd like you to address my comment before the PR is landed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's too much work to get even a minimal conversion working, then I am proposing the following:
- rename
getModelSchemaRef
togetModelSchema
- change the return type from
SchemaRef
toSchemaObject
- change the implementation to return a "dummy" schema describing any object value
export function getModelSchemaRef<T extends object>(
entityCtor: Function & {prototype: T},
options?: JsonSchemaOptions<T>,
): SchemaRef {
// TODO: generate proper OpenAPI schema describing entity properties, etc.
return {
title: entityCtor.name,
type: 'object'
};
}
Personally, I'll try to get rid of SchemaRef
(and thus the dependency on @loopback/rest
) by removing the description of the return type or using object
instead. For the first iteration, we don't need options
either, since they are not supported anyways.
export function getModelSchemaRef<T extends object>(
entityCtor: Function & {prototype: T},
) {
return {
// ...
};
}
Posting few ideas from slack chat: Juggler implementation starts here: https://github.com/strongloop/loopback-connector/blob/7d43b461d13e2e67fd07cee4ae22c1e19d0ea848/lib/sql.js#L1332-L1362 For TypeORM, we want something like this:
IMO, TypeORM users should not be forced to require In LB3, we created loopback-filters package with a similar intention, but it was half-baked and quickly got out of sync with the main filtering logic implemented in the memory connector. So I think it's a valid move to start building a shared filtering package again, and get it right this time :) |
c7157f8
to
f77bd30
Compare
super( | ||
projectRoot, | ||
// Set TypeORM connection options if passed in via bootConfig | ||
Object.assign({}, ConnectionDefaults, entityConfig), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong - all properties are merged into the 1st arg and we won't be able to access it afterward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe Object.assign
returns the 1st argument and therefore this line works as expected
$ node
> console.log(Object.assign({}, {foo: 'bar'}))
{ foo: 'bar' }
undefined
/** | ||
* Objects with open properties | ||
*/ | ||
export interface AnyObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use Record<string, any>
instead.
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see test coverage for the booter.
async load() { | ||
for (const file of this.discovered) { | ||
const exported = require(file); | ||
const connectionKey = Object.keys(exported)[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not clear to me how a connection
artifact look like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
I think the actual config object should have ConnectionOptions
type, see https://github.com/typeorm/typeorm/blob/master/src/connection/ConnectionOptions.ts and https://github.com/typeorm/typeorm/blob/fc7a4f13f6270c9d5206358ebd78adb0ca690b3d/src/index.ts#L127
const connectionKey = Object.keys(exported)[0]; | |
const connectionKey: ConnectionOptions = Object.keys(exported)[0]; |
IIUC, you are trying to register the first exported thing as the connection object. I find this design as providing poor user experience. If the file happens to export more than one config object, then we should either load all of them, or tell the user what has been ignored.
Please take a look at how existing booters are dealing with this edge case and implement the same behavior in your booter for consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am afraid most of my comments are still not addressed. Unfortunately, it's not clear what is the scope of your work (and this pull request), therefore it's difficult to draw the line between things that must be resolved now and improvements that can be made later.
IMO, it's important to address the following:
- Add few basic tests to ensure the code coverage is not dropping too much.
- Change
getModelSchemaRef
togetModelSchema
- Add a list of known limitations to README
- Redesign how connections are registered in the app (remove an array of connection options, introduce
app.connection()
method). - Get rid of
TypeOrmConnections
or at least rename it to a singular form. - Either remove all coupling with REST/OpenAPI, or at least depend on
@loopback/openapi-v3
instead of@loopback/rest
async load() { | ||
for (const file of this.discovered) { | ||
const exported = require(file); | ||
const connectionKey = Object.keys(exported)[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
I think the actual config object should have ConnectionOptions
type, see https://github.com/typeorm/typeorm/blob/master/src/connection/ConnectionOptions.ts and https://github.com/typeorm/typeorm/blob/fc7a4f13f6270c9d5206358ebd78adb0ca690b3d/src/index.ts#L127
const connectionKey = Object.keys(exported)[0]; | |
const connectionKey: ConnectionOptions = Object.keys(exported)[0]; |
IIUC, you are trying to register the first exported thing as the connection object. I find this design as providing poor user experience. If the file happens to export more than one config object, then we should either load all of them, or tell the user what has been ignored.
Please take a look at how existing booters are dealing with this edge case and implement the same behavior in your booter for consistency.
packages/typeorm/README.md
Outdated
npm install --save @loopback/typeorm | ||
``` | ||
|
||
## Usage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
## Usage | |
## Basic use |
See https://loopback.io/doc/en/contrib/README-guidelines.html
packages/typeorm/README.md
Outdated
```ts | ||
// src/controllers/book.controller.ts | ||
import {get, post, Request, requestBody} from '@loopback/rest'; | ||
import {getModelSchemaRef, Repository, typeorm} from '@loopback/typeorm'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed before, this should be just getModelSchema
. (No need to use $ref
schemas anymore, OpenAPI consolidation will take care of de-duplicating schemas.)
IMO, @loopback/typeorm
should not be coupled with OpenAPI, I think we should either tell users to apply jsonToSchemaObject
on the schema produced by @loopback/typeorm
, or else introduce a new package for things that are specific to TypeORM+OpenAPI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, @loopback/typeorm should not be coupled with OpenAPI,
Need elaboration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@loopback/openapi-v3
provides things like @get
and @api
decorators.
As a user building gRPC or Pub/Sub application using @loopback/typeorm
for persistence, why do I have to install dependencies for OpenAPI, when I am not using any of that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also if we add gRPC
support in the future and implement code to generate gRPC schemas (protocol buffer message type definitions) from TypeORM entities, are we going to add gRPC
to @loopback/typeorm
dependencies and thus force REST/OpenAPI users to install gRPC they are not using?
Anyhow, I think this is out of scope of this pull request, but please do create a follow-up task to continue this discussion and rework the code accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: Create follow up task.
packages/typeorm/README.md
Outdated
const book = new Book(); | ||
book.title = data.title; | ||
book.published = false; | ||
return await this.bookRepo.save(book); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use repository.create
API?
this.bookRepo.create(data);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The object is not being saved for some reason; don't want to troubleshoot right now, let's handle the highest priority items first. Will give a try again later today, if not, will open a follow up task.
Note to self: Create follow up task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a code comment to this code snippet, to explain the problem to our readers too?
packages/typeorm/src/utils.ts
Outdated
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {JsonSchemaOptions, SchemaRef} from '@loopback/rest'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❗ @loopback/typeorm
should not require consumers to use @loopback/rest
.
At minimum, change this line to import OpenAPI types from @loopback/openapi-v3
.
As I mentioned above, I think it would be best to move OpenAPI-related parts into a new package, but I guess that can be done in a follow-up pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: Create follow up task.
packages/typeorm/src/utils.ts
Outdated
* @param entityCtor - The entity constructor (e.g. `Product`) | ||
* @param options - Additional options | ||
*/ | ||
export function getModelSchemaRef<T extends object>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in the call, I am proposing to drop
Ref
part and emit regular JSON schema. Theref
-based schemas were needed before OpenAPI schema consolidation was introduced. I believe with consolidation in place, we no longer need to worry about avoiding duplicated schema definitions in controller methods.
❗I'd like you to address my comment before the PR is landed.
packages/typeorm/src/utils.ts
Outdated
* @param entityCtor - The entity constructor (e.g. `Product`) | ||
* @param options - Additional options | ||
*/ | ||
export function getModelSchemaRef<T extends object>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's too much work to get even a minimal conversion working, then I am proposing the following:
- rename
getModelSchemaRef
togetModelSchema
- change the return type from
SchemaRef
toSchemaObject
- change the implementation to return a "dummy" schema describing any object value
export function getModelSchemaRef<T extends object>(
entityCtor: Function & {prototype: T},
options?: JsonSchemaOptions<T>,
): SchemaRef {
// TODO: generate proper OpenAPI schema describing entity properties, etc.
return {
title: entityCtor.name,
type: 'object'
};
}
Personally, I'll try to get rid of SchemaRef
(and thus the dependency on @loopback/rest
) by removing the description of the return type or using object
instead. For the first iteration, we don't need options
either, since they are not supported anyways.
export function getModelSchemaRef<T extends object>(
entityCtor: Function & {prototype: T},
) {
return {
// ...
};
}
8231130
to
09faf13
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for addressing my comments, @hacksparrow, I like the new version sooo much more! 😍
The pull request looks mostly good now, I left few non-blocking comments below.
Please work with @raymondfeng to decide which of my suggestions are ok to leave out and make sure to get his approval before landing the PR.
packages/typeorm/README.md
Outdated
constructor(options: ApplicationConfig = {}) { | ||
super(options); | ||
... | ||
this.component(TypeOrmComponent); | ||
... | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: we no longer need to show the constructor.
export class AppaApplication extends BootMixin(
ServiceMixin(TypeOrmMixin(RestApplication)),
) {
// ...
}
packages/typeorm/README.md
Outdated
@@ -1,6 +1,7 @@ | |||
# @loopback/typeorm | |||
|
|||
This module enables TypeORM support in LoopBack. | |||
This module enables TypeORM support in LoopBack. For pending features, refer to | |||
the "Pending" section. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the "Pending" section. | |
the "Limitations" section. |
(To match the real section name.)
Even better:
the "Pending" section. | |
the [Limitations](#limitations) section below. |
(Just make sure the link works this way, I am not sure what's the exact anchor URL.)
packages/typeorm/README.md
Outdated
@@ -12,25 +13,22 @@ This module enables TypeORM support in LoopBack. | |||
npm install --save @loopback/typeorm | |||
``` | |||
|
|||
## Usage | |||
## Basic Usage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our README guidelines:
## Basic Usage | |
## Basic Use |
packages/typeorm/README.md
Outdated
|
||
1. Database migration | ||
2. Custom repository | ||
3. Query filters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's clarify this item. I believe it is already possible to use TypeORM filters, right? IIUC, we are missing support for LoopBack-style filters and exposing filters via REST API.
packages/typeorm/src/decorators.ts
Outdated
@@ -62,6 +62,6 @@ export namespace typeorm { | |||
* @param connectionName - Optional connection name | |||
*/ | |||
async function getConnection(ctx: Context, connectionName?: string) { | |||
const connections = await ctx.get(TypeOrmBindings.CONNECTIONS); | |||
return connections.manager.get(connectionName); | |||
const manager = await ctx.get(TypeOrmBindings.MANAGER); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect this method to be called on hot paths, can we make this method sync to improve performance?
function getConnection(ctx: Context, connectionName?: string): Connection {
const manager = ctx.getSync(TypeOrmBindings.MANAGER);
return manager.get(connectionName);
}
Feel free to leave this improvement out of scope of this pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self: Create follow up task.
packages/typeorm/src/mixin.ts
Outdated
@lifeCycleObserver('datasource', { | ||
scope: BindingScope.SINGLETON, | ||
}) | ||
export class StartStop implements LifeCycleObserver { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please mention "typeorm" in the class name, to distinguish this class from other similar observers.
For example:
export class StartStop implements LifeCycleObserver { | |
export class StartStopTypeOrmConnections implements LifeCycleObserver { |
I believe we use nouns in class names, perhaps something like TypeOrmLifeCycleManager
would be a better name? @raymondfeng do you have any suggestions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using TypeOrmLifeCycleManager
.
packages/typeorm/src/utils.ts
Outdated
import {ColumnType} from 'typeorm/driver/types/ColumnTypes'; | ||
import {ColumnMetadataArgs} from 'typeorm/metadata-args/ColumnMetadataArgs'; | ||
|
||
const ModelSchemaCache = new WeakMap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: we use camelCase
for variable names (CamelCase
is for type/class names).
const ModelSchemaCache = new WeakMap(); | |
const modelSchemaCache = new WeakMap(); |
packages/typeorm/src/utils.ts
Outdated
const cached = ModelSchemaCache.get(modelCtor); | ||
if (cached) { | ||
return cached; | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the else
block to reduce the indentation level.
if (cached) {
return cached;
}
const allColumns: ColumnMetadataArgs[] = getMetadataArgsStorage().columns;
// ...
packages/typeorm/src/utils.ts
Outdated
return 'boolean'; | ||
} else { | ||
throw new Error( | ||
`Type conversion of ${func} to OpenAPI format not implemented.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UX improvement:
- Please include the entity name and the property name in the error message.
Discussion point:
In your proposal, applications cannot use unsupported types at all. Isn't that too limiting, considering that those types works at ORM layer and we just don't know how to describe them in OpenAPI? How about changing this line to print a warning and return a value saying the property does not have any type constraints? (I think undefined
should work.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} else {
debug(
`${entity}.${property}: Type conversion of ${func} to OpenAPI format not implemented.`,
);
return undefined;
}
09faf13
to
d714638
Compare
beb8d98
to
23bb5f4
Compare
e178db3
to
150dd4a
Compare
title: 'How to use TypeORM with LoopBack' | ||
keywords: LoopBack 4.0, LoopBack 4, TypeORM | ||
sidebar: lb4_sidebar | ||
permalink: /doc/en/lb4/Using-TypeORM-with-LoopBack.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use Using-typeorm-with-loopback.html
to be consistent with other links.
|
||
## Overview | ||
|
||
If you would like to use TypeORM with LoopBack, it is possible with the use of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's introduce TypeORM and have a link here.
} | ||
``` | ||
|
||
### Creating Connections |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's better to have Creating Connections
come before Creating Entities
?
[TypeORM documentation](https://github.com/typeorm/typeorm#installation) for the | ||
supported databases and the underlying drivers. | ||
|
||
### Creating controllers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating Controllers
docs/site/sidebars/lb4_sidebar.yml
Outdated
- title: 'How to use TypeORM with LoopBack' | ||
url: Using-TypeORM-with-LoopBack.html | ||
output: 'web, pdf' | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move it to be after Accessing Databases
?
packages/typeorm/README.md
Outdated
|
||
## Overview | ||
|
||
**NOTE**: This module is experimental and evolving. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use the same disclaimer as https://github.com/strongloop/loopback-next/tree/master/extensions/cron#stability-%EF%B8%8Fexperimental%EF%B8%8F
packages/typeorm/package.json
Outdated
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"engines": { | ||
"node": ">=10" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
=10.16.
packages/typeorm/package.json
Outdated
"@loopback/rest": "^5.2.0", | ||
"@loopback/service-proxy": "2.3.4", | ||
"@loopback/testlab": "^3.1.7", | ||
"@types/json-schema": "^7.0.5", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use the following commands to update its deps on other LB4 packages:
cd packages/typeorm
lb4 update --yes --force --skip-install
cd ../..
node bin/rebuild-package-locks.js @loopback/typeorm
packages/typeorm/package.json
Outdated
@@ -0,0 +1,53 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should move it to extensions/typeorm
?
packages/typeorm/package.json
Outdated
"access": "public" | ||
}, | ||
"devDependencies": { | ||
"@loopback/boot": "^2.3.5", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a regular dependency.
packages/typeorm/package.json
Outdated
}, | ||
"dependencies": { | ||
"@loopback/core": "^2.4.1", | ||
"@loopback/openapi-v3": "3.4.4", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need it as a regular dependency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Default ArtifactOptions for TypeORM connection. | ||
*/ | ||
export const ConnectionDefaults: ArtifactOptions = { | ||
dirs: ['connections'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's prefix the directory, such as typeorm-connections
.
name: 'my-db', | ||
type: 'sqlite', | ||
database: path.join(__dirname, './mydb.sql'), | ||
entities: [Book], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to have a booter for TypeORM entities too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the TypeORM convention, I think we should leave it alone. We just provide the glue, everything else - Entities, Connections, Respository methods - remain 100% pure TypeORM.
dc00546
to
3e6edaf
Compare
extensions/typeorm/README.md
Outdated
(e.g. to implement bookRepo.findByTitle(title)). | ||
6. [Database migration](https://github.com/strongloop/loopback-next/issues/5898) | ||
|
||
We will be implementing them progressively. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might want to make it as "Community contribution is welcome".
permalink: /doc/en/lb4/Using-typeorm-with-loopback.html | ||
--- | ||
|
||
## Overview |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we can use the README.md content directly instead of duplicating the content?
See the JWT authentication extension docs as an example.
@@ -242,6 +242,13 @@ children: | |||
url: Database-migrations.html | |||
output: 'web, pdf' | |||
|
|||
- title: 'Using TypeORM' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason we need a parent item instead of having "How to use TypeORM with LoopBack" as the top level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There will be more articles under it later. Eg: "How to create custom repositories", etc.
a7c513f
to
3cc7cac
Compare
extensions/typeorm/package.json
Outdated
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/strongloop/loopback-next.git", | ||
"directory": "packages/typeorm" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extensions/typeorm
@@ -0,0 +1,16 @@ | |||
// Copyright IBM Corp. 2019. All Rights Reserved. | |||
// Node module: @loopback/boot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please fix license header
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use lb4 copyright
command under extensions/typeorm
.
@@ -0,0 +1,59 @@ | |||
// Copyright IBM Corp. 2020. All Rights Reserved. | |||
// Node module: @loopback/rest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
@@ -0,0 +1,18 @@ | |||
// Copyright IBM Corp. 2020. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
entities
-> typeorm-entities
?
@@ -0,0 +1,73 @@ | |||
// Copyright IBM Corp. 2020. All Rights Reserved. | |||
// Node module: @loopback/graphql |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
@@ -0,0 +1,73 @@ | |||
// Copyright IBM Corp. 2020. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typeorm-connection.booter.ts?
6b611bd
to
182ee62
Compare
f8e75e8
to
61a8c54
Compare
Taking the work in #4794 further, this PR adds TypeORM support in LB4. You can now use TypeORM entities and repositories in LB4.
The feature is implemented and working, for usage details refer to https://github.com/strongloop/loopback-next/blob/c7157f85cb8b05a43fdf59eabfd72892bf9a6662/packages/typeorm/README.md.
What's remaining?
Checklist
👉 Read and sign the CLA (Contributor License Agreement) 👈
npm test
passes on your machinepackages/cli
were updatedexamples/*
were updated👉 Check out how to submit a PR 👈