typeorm 0.3.1 → 0.3.2-dev.c8fb1bb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +470 -525
- package/browser/error/index.d.ts +0 -1
- package/browser/error/index.js +0 -1
- package/browser/error/index.js.map +1 -1
- package/cli.js +0 -0
- package/error/index.d.ts +0 -1
- package/error/index.js +0 -1
- package/error/index.js.map +1 -1
- package/index.mjs +0 -2
- package/package.json +1 -253
- package/browser/error/RepositoryNotFoundError.d.ts +0 -8
- package/browser/error/RepositoryNotFoundError.js +0 -30
- package/browser/error/RepositoryNotFoundError.js.map +0 -1
- package/error/RepositoryNotFoundError.d.ts +0 -8
- package/error/RepositoryNotFoundError.js +0 -34
- package/error/RepositoryNotFoundError.js.map +0 -1
package/README.md
CHANGED
|
@@ -34,133 +34,138 @@ which means you can write high quality, loosely coupled, scalable,
|
|
|
34
34
|
maintainable applications the most productive way.
|
|
35
35
|
|
|
36
36
|
TypeORM is highly influenced by other ORMs, such as [Hibernate](http://hibernate.org/orm/),
|
|
37
|
-
|
|
37
|
+
[Doctrine](http://www.doctrine-project.org/) and [Entity Framework](https://www.asp.net/entity-framework).
|
|
38
38
|
|
|
39
39
|
## Features
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
41
|
+
- Supports both [DataMapper](./docs/active-record-data-mapper.md#what-is-the-data-mapper-pattern) and [ActiveRecord](./docs/active-record-data-mapper.md#what-is-the-active-record-pattern) (your choice).
|
|
42
|
+
- Entities and columns.
|
|
43
|
+
- Database-specific column types.
|
|
44
|
+
- Entity manager.
|
|
45
|
+
- Repositories and custom repositories.
|
|
46
|
+
- Clean object relational model.
|
|
47
|
+
- Associations (relations).
|
|
48
|
+
- Eager and lazy relations.
|
|
49
|
+
- Uni-directional, bi-directional and self-referenced relations.
|
|
50
|
+
- Supports multiple inheritance patterns.
|
|
51
|
+
- Cascades.
|
|
52
|
+
- Indices.
|
|
53
|
+
- Transactions.
|
|
54
|
+
- Migrations and automatic migrations generation.
|
|
55
|
+
- Connection pooling.
|
|
56
|
+
- Replication.
|
|
57
|
+
- Using multiple database instances.
|
|
58
|
+
- Working with multiple databases types.
|
|
59
|
+
- Cross-database and cross-schema queries.
|
|
60
|
+
- Elegant-syntax, flexible and powerful QueryBuilder.
|
|
61
|
+
- Left and inner joins.
|
|
62
|
+
- Proper pagination for queries using joins.
|
|
63
|
+
- Query caching.
|
|
64
|
+
- Streaming raw results.
|
|
65
|
+
- Logging.
|
|
66
|
+
- Listeners and subscribers (hooks).
|
|
67
|
+
- Supports closure table pattern.
|
|
68
|
+
- Schema declaration in models or separate configuration files.
|
|
69
|
+
- Connection configuration in json / xml / yml / env formats.
|
|
70
|
+
- Supports MySQL / MariaDB / Postgres / CockroachDB / SQLite / Microsoft SQL Server / Oracle / SAP Hana / sql.js.
|
|
71
|
+
- Supports MongoDB NoSQL database.
|
|
72
|
+
- Works in NodeJS / Browser / Ionic / Cordova / React Native / NativeScript / Expo / Electron platforms.
|
|
73
|
+
- TypeScript and JavaScript support.
|
|
74
|
+
- ESM and CommonJS support.
|
|
75
|
+
- Produced code is performant, flexible, clean and maintainable.
|
|
76
|
+
- Follows all possible best practices.
|
|
77
|
+
- CLI.
|
|
78
78
|
|
|
79
79
|
And more...
|
|
80
80
|
|
|
81
81
|
With TypeORM your models look like this:
|
|
82
82
|
|
|
83
83
|
```javascript
|
|
84
|
-
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
|
|
84
|
+
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"
|
|
85
85
|
|
|
86
86
|
@Entity()
|
|
87
87
|
export class User {
|
|
88
|
-
|
|
89
88
|
@PrimaryGeneratedColumn()
|
|
90
|
-
id: number
|
|
89
|
+
id: number
|
|
91
90
|
|
|
92
91
|
@Column()
|
|
93
|
-
firstName: string
|
|
92
|
+
firstName: string
|
|
94
93
|
|
|
95
94
|
@Column()
|
|
96
|
-
lastName: string
|
|
95
|
+
lastName: string
|
|
97
96
|
|
|
98
97
|
@Column()
|
|
99
|
-
age: number
|
|
100
|
-
|
|
98
|
+
age: number
|
|
101
99
|
}
|
|
102
100
|
```
|
|
103
101
|
|
|
104
102
|
And your domain logic looks like this:
|
|
105
103
|
|
|
106
104
|
```javascript
|
|
107
|
-
const
|
|
105
|
+
const userRepository = MyDataSource.getRepository(User)
|
|
108
106
|
|
|
109
|
-
const user = new User()
|
|
110
|
-
user.firstName = "Timber"
|
|
111
|
-
user.lastName = "Saw"
|
|
112
|
-
user.age = 25
|
|
113
|
-
await
|
|
107
|
+
const user = new User()
|
|
108
|
+
user.firstName = "Timber"
|
|
109
|
+
user.lastName = "Saw"
|
|
110
|
+
user.age = 25
|
|
111
|
+
await userRepository.save(user)
|
|
114
112
|
|
|
115
|
-
const allUsers = await
|
|
116
|
-
const firstUser = await
|
|
117
|
-
|
|
113
|
+
const allUsers = await userRepository.find()
|
|
114
|
+
const firstUser = await userRepository.findOneBy({
|
|
115
|
+
id: 1,
|
|
116
|
+
}) // find by id
|
|
117
|
+
const timber = await userRepository.findOneBy({
|
|
118
|
+
firstName: "Timber",
|
|
119
|
+
lastName: "Saw",
|
|
120
|
+
}) // find by firstName and lastName
|
|
118
121
|
|
|
119
|
-
await
|
|
122
|
+
await userRepository.remove(timber)
|
|
120
123
|
```
|
|
121
124
|
|
|
122
125
|
Alternatively, if you prefer to use the `ActiveRecord` implementation, you can use it as well:
|
|
123
126
|
|
|
124
127
|
```javascript
|
|
125
|
-
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm"
|
|
128
|
+
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from "typeorm"
|
|
126
129
|
|
|
127
130
|
@Entity()
|
|
128
131
|
export class User extends BaseEntity {
|
|
129
|
-
|
|
130
132
|
@PrimaryGeneratedColumn()
|
|
131
|
-
id: number
|
|
133
|
+
id: number
|
|
132
134
|
|
|
133
135
|
@Column()
|
|
134
|
-
firstName: string
|
|
136
|
+
firstName: string
|
|
135
137
|
|
|
136
138
|
@Column()
|
|
137
|
-
lastName: string
|
|
139
|
+
lastName: string
|
|
138
140
|
|
|
139
141
|
@Column()
|
|
140
|
-
age: number
|
|
141
|
-
|
|
142
|
+
age: number
|
|
142
143
|
}
|
|
143
144
|
```
|
|
144
145
|
|
|
145
146
|
And your domain logic will look this way:
|
|
146
147
|
|
|
147
148
|
```javascript
|
|
148
|
-
const user = new User()
|
|
149
|
-
user.firstName = "Timber"
|
|
150
|
-
user.lastName = "Saw"
|
|
151
|
-
user.age = 25
|
|
152
|
-
await user.save()
|
|
153
|
-
|
|
154
|
-
const allUsers = await User.find()
|
|
155
|
-
const firstUser = await User.
|
|
156
|
-
|
|
149
|
+
const user = new User()
|
|
150
|
+
user.firstName = "Timber"
|
|
151
|
+
user.lastName = "Saw"
|
|
152
|
+
user.age = 25
|
|
153
|
+
await user.save()
|
|
154
|
+
|
|
155
|
+
const allUsers = await User.find()
|
|
156
|
+
const firstUser = await User.findOneBy({
|
|
157
|
+
id: 1,
|
|
158
|
+
})
|
|
159
|
+
const timber = await User.findOneBy({
|
|
160
|
+
firstName: "Timber",
|
|
161
|
+
lastName: "Saw"
|
|
162
|
+
})
|
|
157
163
|
|
|
158
|
-
await timber.remove()
|
|
164
|
+
await timber.remove()
|
|
159
165
|
```
|
|
160
166
|
|
|
161
167
|
## Installation
|
|
162
168
|
|
|
163
|
-
|
|
164
169
|
1. Install the npm package:
|
|
165
170
|
|
|
166
171
|
`npm install typeorm --save`
|
|
@@ -171,7 +176,7 @@ await timber.remove();
|
|
|
171
176
|
|
|
172
177
|
and import it somewhere in the global place of your app (for example in `app.ts`):
|
|
173
178
|
|
|
174
|
-
`import "reflect-metadata"
|
|
179
|
+
`import "reflect-metadata"`
|
|
175
180
|
|
|
176
181
|
3. You may need to install node typings:
|
|
177
182
|
|
|
@@ -179,56 +184,55 @@ await timber.remove();
|
|
|
179
184
|
|
|
180
185
|
4. Install a database driver:
|
|
181
186
|
|
|
182
|
-
|
|
187
|
+
- for **MySQL** or **MariaDB**
|
|
183
188
|
|
|
184
189
|
`npm install mysql --save` (you can install `mysql2` instead as well)
|
|
185
190
|
|
|
186
|
-
|
|
191
|
+
- for **PostgreSQL** or **CockroachDB**
|
|
187
192
|
|
|
188
193
|
`npm install pg --save`
|
|
189
194
|
|
|
190
|
-
|
|
195
|
+
- for **SQLite**
|
|
191
196
|
|
|
192
197
|
`npm install sqlite3 --save`
|
|
193
198
|
|
|
194
|
-
|
|
199
|
+
- for **Microsoft SQL Server**
|
|
195
200
|
|
|
196
201
|
`npm install mssql --save`
|
|
197
202
|
|
|
198
|
-
|
|
203
|
+
- for **sql.js**
|
|
199
204
|
|
|
200
205
|
`npm install sql.js --save`
|
|
201
206
|
|
|
202
|
-
|
|
207
|
+
- for **Oracle**
|
|
203
208
|
|
|
204
209
|
`npm install oracledb --save`
|
|
205
210
|
|
|
206
211
|
To make the Oracle driver work, you need to follow the installation instructions from
|
|
207
212
|
[their](https://github.com/oracle/node-oracledb) site.
|
|
208
213
|
|
|
209
|
-
|
|
214
|
+
- for **SAP Hana**
|
|
210
215
|
|
|
211
216
|
```
|
|
212
217
|
npm i @sap/hana-client
|
|
213
|
-
|
|
218
|
+
npm i hdb-pool
|
|
214
219
|
```
|
|
215
220
|
|
|
216
|
-
|
|
221
|
+
_SAP Hana support made possible by the sponsorship of [Neptune Software](https://www.neptune-software.com/)._
|
|
217
222
|
|
|
218
|
-
|
|
223
|
+
- for **MongoDB** (experimental)
|
|
219
224
|
|
|
220
225
|
`npm install mongodb@^3.6.0 --save`
|
|
221
226
|
|
|
222
|
-
|
|
227
|
+
- for **NativeScript**, **react-native** and **Cordova**
|
|
223
228
|
|
|
224
229
|
Check [documentation of supported platforms](./docs/supported-platforms.md)
|
|
225
230
|
|
|
226
|
-
Install only
|
|
227
|
-
|
|
231
|
+
Install only _one_ of them, depending on which database you use.
|
|
228
232
|
|
|
229
233
|
##### TypeScript configuration
|
|
230
234
|
|
|
231
|
-
Also, make sure you are using TypeScript version **
|
|
235
|
+
Also, make sure you are using TypeScript version **4.5** or higher,
|
|
232
236
|
and you have enabled the following settings in `tsconfig.json`:
|
|
233
237
|
|
|
234
238
|
```json
|
|
@@ -244,16 +248,10 @@ The quickest way to get started with TypeORM is to use its CLI commands to gener
|
|
|
244
248
|
Quick start works only if you are using TypeORM in a NodeJS application.
|
|
245
249
|
If you are using other platforms, proceed to the [step-by-step guide](#step-by-step-guide).
|
|
246
250
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
```
|
|
250
|
-
npm install typeorm -g
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Then go to the directory where you want to create a new project and run the command:
|
|
251
|
+
To create a new project using CLI, run the follwing command:
|
|
254
252
|
|
|
255
|
-
```
|
|
256
|
-
typeorm init --name MyProject --database
|
|
253
|
+
```shell
|
|
254
|
+
npx typeorm init --name MyProject --database postgres
|
|
257
255
|
```
|
|
258
256
|
|
|
259
257
|
Where `name` is the name of your project and `database` is the database you'll use.
|
|
@@ -264,49 +262,43 @@ This command will generate a new project in the `MyProject` directory with the f
|
|
|
264
262
|
|
|
265
263
|
```
|
|
266
264
|
MyProject
|
|
267
|
-
├── src
|
|
268
|
-
│ ├── entity
|
|
269
|
-
│ │ └── User.ts
|
|
270
|
-
│ ├── migration
|
|
271
|
-
│
|
|
272
|
-
|
|
273
|
-
├──
|
|
274
|
-
├── package.json
|
|
275
|
-
├── README.md
|
|
276
|
-
└── tsconfig.json
|
|
265
|
+
├── src // place of your TypeScript code
|
|
266
|
+
│ ├── entity // place where your entities (database models) are stored
|
|
267
|
+
│ │ └── User.ts // sample entity
|
|
268
|
+
│ ├── migration // place where your migrations are stored
|
|
269
|
+
│ ├── data-source.ts // data source and all connection configuration
|
|
270
|
+
│ └── index.ts // start point of your application
|
|
271
|
+
├── .gitignore // standard gitignore file
|
|
272
|
+
├── package.json // node module dependencies
|
|
273
|
+
├── README.md // simple readme file
|
|
274
|
+
└── tsconfig.json // TypeScript compiler options
|
|
277
275
|
```
|
|
278
276
|
|
|
279
277
|
> You can also run `typeorm init` on an existing node project, but be careful - it may override some files you already have.
|
|
280
278
|
|
|
281
279
|
The next step is to install new project dependencies:
|
|
282
280
|
|
|
283
|
-
```
|
|
281
|
+
```shell
|
|
284
282
|
cd MyProject
|
|
285
283
|
npm install
|
|
286
284
|
```
|
|
287
285
|
|
|
288
|
-
|
|
286
|
+
After you have all dependencies installed, edit the `data-source.ts` file and put your own database connection configuration options in there:
|
|
289
287
|
|
|
290
|
-
```
|
|
291
|
-
{
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
"src/migration/**/*.ts"
|
|
305
|
-
],
|
|
306
|
-
"subscribers": [
|
|
307
|
-
"src/subscriber/**/*.ts"
|
|
308
|
-
]
|
|
309
|
-
}
|
|
288
|
+
```ts
|
|
289
|
+
export const AppDataSource = new DataSource({
|
|
290
|
+
type: "postgres",
|
|
291
|
+
host: "localhost",
|
|
292
|
+
port: 5432,
|
|
293
|
+
username: "test",
|
|
294
|
+
password: "test",
|
|
295
|
+
database: "test",
|
|
296
|
+
synchronize: true,
|
|
297
|
+
logging: true,
|
|
298
|
+
entities: [Post, Category],
|
|
299
|
+
subscribers: [],
|
|
300
|
+
migrations: [],
|
|
301
|
+
})
|
|
310
302
|
```
|
|
311
303
|
|
|
312
304
|
Particularly, most of the time you'll only need to configure
|
|
@@ -314,7 +306,7 @@ Particularly, most of the time you'll only need to configure
|
|
|
314
306
|
|
|
315
307
|
Once you finish with configuration and all node modules are installed, you can run your application:
|
|
316
308
|
|
|
317
|
-
```
|
|
309
|
+
```shell
|
|
318
310
|
npm start
|
|
319
311
|
```
|
|
320
312
|
|
|
@@ -323,13 +315,13 @@ You can continue to work with this project and integrate other modules you need
|
|
|
323
315
|
creating more entities.
|
|
324
316
|
|
|
325
317
|
> You can generate an ESM project by running
|
|
326
|
-
`typeorm init --name MyProject --database postgres --module esm` command.
|
|
318
|
+
> `npx typeorm init --name MyProject --database postgres --module esm` command.
|
|
327
319
|
|
|
328
320
|
> You can generate an even more advanced project with express installed by running
|
|
329
|
-
`typeorm init --name MyProject --database mysql --express` command.
|
|
321
|
+
> `npx typeorm init --name MyProject --database mysql --express` command.
|
|
330
322
|
|
|
331
323
|
> You can generate a docker-compose file by running
|
|
332
|
-
`typeorm init --name MyProject --database postgres --docker` command.
|
|
324
|
+
> `npx typeorm init --name MyProject --database postgres --docker` command.
|
|
333
325
|
|
|
334
326
|
## Step-by-Step Guide
|
|
335
327
|
|
|
@@ -350,46 +342,46 @@ For example, you have a `Photo` model:
|
|
|
350
342
|
|
|
351
343
|
```javascript
|
|
352
344
|
export class Photo {
|
|
353
|
-
id: number
|
|
354
|
-
name: string
|
|
355
|
-
description: string
|
|
356
|
-
filename: string
|
|
357
|
-
views: number
|
|
358
|
-
isPublished: boolean
|
|
345
|
+
id: number
|
|
346
|
+
name: string
|
|
347
|
+
description: string
|
|
348
|
+
filename: string
|
|
349
|
+
views: number
|
|
350
|
+
isPublished: boolean
|
|
359
351
|
}
|
|
360
352
|
```
|
|
361
353
|
|
|
362
354
|
And you want to store photos in your database.
|
|
363
355
|
To store things in the database, first, you need a database table,
|
|
364
356
|
and database tables are created from your models.
|
|
365
|
-
Not all models, but only those you define as
|
|
357
|
+
Not all models, but only those you define as _entities_.
|
|
366
358
|
|
|
367
359
|
### Create an entity
|
|
368
360
|
|
|
369
|
-
|
|
361
|
+
_Entity_ is your model decorated by an `@Entity` decorator.
|
|
370
362
|
A database table will be created for such models.
|
|
371
|
-
You work with entities everywhere
|
|
363
|
+
You work with entities everywhere in TypeORM.
|
|
372
364
|
You can load/insert/update/remove and perform other operations with them.
|
|
373
365
|
|
|
374
|
-
Let's make our `Photo` model
|
|
366
|
+
Let's make our `Photo` model an entity:
|
|
375
367
|
|
|
376
368
|
```javascript
|
|
377
|
-
import { Entity } from "typeorm"
|
|
369
|
+
import { Entity } from "typeorm"
|
|
378
370
|
|
|
379
371
|
@Entity()
|
|
380
372
|
export class Photo {
|
|
381
|
-
id: number
|
|
382
|
-
name: string
|
|
383
|
-
description: string
|
|
384
|
-
filename: string
|
|
385
|
-
views: number
|
|
386
|
-
isPublished: boolean
|
|
373
|
+
id: number
|
|
374
|
+
name: string
|
|
375
|
+
description: string
|
|
376
|
+
filename: string
|
|
377
|
+
views: number
|
|
378
|
+
isPublished: boolean
|
|
387
379
|
}
|
|
388
380
|
```
|
|
389
381
|
|
|
390
382
|
Now, a database table will be created for the `Photo` entity and we'll be able to work with it anywhere in our app.
|
|
391
383
|
We have created a database table, however, what table can exist without columns?
|
|
392
|
-
Let's create
|
|
384
|
+
Let's create few columns in our database table.
|
|
393
385
|
|
|
394
386
|
### Adding table columns
|
|
395
387
|
|
|
@@ -397,28 +389,27 @@ To add database columns, you simply need to decorate an entity's properties you
|
|
|
397
389
|
with a `@Column` decorator.
|
|
398
390
|
|
|
399
391
|
```javascript
|
|
400
|
-
import { Entity, Column } from "typeorm"
|
|
392
|
+
import { Entity, Column } from "typeorm"
|
|
401
393
|
|
|
402
394
|
@Entity()
|
|
403
395
|
export class Photo {
|
|
404
|
-
|
|
405
396
|
@Column()
|
|
406
|
-
id: number
|
|
397
|
+
id: number
|
|
407
398
|
|
|
408
399
|
@Column()
|
|
409
|
-
name: string
|
|
400
|
+
name: string
|
|
410
401
|
|
|
411
402
|
@Column()
|
|
412
|
-
description: string
|
|
403
|
+
description: string
|
|
413
404
|
|
|
414
405
|
@Column()
|
|
415
|
-
filename: string
|
|
406
|
+
filename: string
|
|
416
407
|
|
|
417
408
|
@Column()
|
|
418
|
-
views: number
|
|
409
|
+
views: number
|
|
419
410
|
|
|
420
411
|
@Column()
|
|
421
|
-
isPublished: boolean
|
|
412
|
+
isPublished: boolean
|
|
422
413
|
}
|
|
423
414
|
```
|
|
424
415
|
|
|
@@ -437,28 +428,27 @@ This is a requirement and you can't avoid it.
|
|
|
437
428
|
To make a column a primary key, you need to use the `@PrimaryColumn` decorator.
|
|
438
429
|
|
|
439
430
|
```javascript
|
|
440
|
-
import { Entity, Column, PrimaryColumn } from "typeorm"
|
|
431
|
+
import { Entity, Column, PrimaryColumn } from "typeorm"
|
|
441
432
|
|
|
442
433
|
@Entity()
|
|
443
434
|
export class Photo {
|
|
444
|
-
|
|
445
435
|
@PrimaryColumn()
|
|
446
|
-
id: number
|
|
436
|
+
id: number
|
|
447
437
|
|
|
448
438
|
@Column()
|
|
449
|
-
name: string
|
|
439
|
+
name: string
|
|
450
440
|
|
|
451
441
|
@Column()
|
|
452
|
-
description: string
|
|
442
|
+
description: string
|
|
453
443
|
|
|
454
444
|
@Column()
|
|
455
|
-
filename: string
|
|
445
|
+
filename: string
|
|
456
446
|
|
|
457
447
|
@Column()
|
|
458
|
-
views: number
|
|
448
|
+
views: number
|
|
459
449
|
|
|
460
450
|
@Column()
|
|
461
|
-
isPublished: boolean
|
|
451
|
+
isPublished: boolean
|
|
462
452
|
}
|
|
463
453
|
```
|
|
464
454
|
|
|
@@ -468,28 +458,27 @@ Now, let's say you want your id column to be auto-generated (this is known as au
|
|
|
468
458
|
To do that, you need to change the `@PrimaryColumn` decorator to a `@PrimaryGeneratedColumn` decorator:
|
|
469
459
|
|
|
470
460
|
```javascript
|
|
471
|
-
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"
|
|
461
|
+
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"
|
|
472
462
|
|
|
473
463
|
@Entity()
|
|
474
464
|
export class Photo {
|
|
475
|
-
|
|
476
465
|
@PrimaryGeneratedColumn()
|
|
477
|
-
id: number
|
|
466
|
+
id: number
|
|
478
467
|
|
|
479
468
|
@Column()
|
|
480
|
-
name: string
|
|
469
|
+
name: string
|
|
481
470
|
|
|
482
471
|
@Column()
|
|
483
|
-
description: string
|
|
472
|
+
description: string
|
|
484
473
|
|
|
485
474
|
@Column()
|
|
486
|
-
filename: string
|
|
475
|
+
filename: string
|
|
487
476
|
|
|
488
477
|
@Column()
|
|
489
|
-
views: number
|
|
478
|
+
views: number
|
|
490
479
|
|
|
491
480
|
@Column()
|
|
492
|
-
isPublished: boolean
|
|
481
|
+
isPublished: boolean
|
|
493
482
|
}
|
|
494
483
|
```
|
|
495
484
|
|
|
@@ -501,30 +490,29 @@ We don't want all our columns to be limited varchars or integers.
|
|
|
501
490
|
Let's setup correct data types:
|
|
502
491
|
|
|
503
492
|
```javascript
|
|
504
|
-
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"
|
|
493
|
+
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm"
|
|
505
494
|
|
|
506
495
|
@Entity()
|
|
507
496
|
export class Photo {
|
|
508
|
-
|
|
509
497
|
@PrimaryGeneratedColumn()
|
|
510
|
-
id: number
|
|
498
|
+
id: number
|
|
511
499
|
|
|
512
500
|
@Column({
|
|
513
|
-
length: 100
|
|
501
|
+
length: 100,
|
|
514
502
|
})
|
|
515
|
-
name: string
|
|
503
|
+
name: string
|
|
516
504
|
|
|
517
505
|
@Column("text")
|
|
518
|
-
description: string
|
|
506
|
+
description: string
|
|
519
507
|
|
|
520
508
|
@Column()
|
|
521
|
-
filename: string
|
|
509
|
+
filename: string
|
|
522
510
|
|
|
523
511
|
@Column("double")
|
|
524
|
-
views: number
|
|
512
|
+
views: number
|
|
525
513
|
|
|
526
514
|
@Column()
|
|
527
|
-
isPublished: boolean
|
|
515
|
+
isPublished: boolean
|
|
528
516
|
}
|
|
529
517
|
```
|
|
530
518
|
|
|
@@ -532,79 +520,52 @@ Column types are database-specific.
|
|
|
532
520
|
You can set any column type your database supports.
|
|
533
521
|
More information on supported column types can be found [here](./docs/entities.md#column-types).
|
|
534
522
|
|
|
535
|
-
### Creating a
|
|
523
|
+
### Creating a new `DataSource`
|
|
536
524
|
|
|
537
|
-
Now, when our entity is created, let's create
|
|
525
|
+
Now, when our entity is created, let's create `index.ts` file and set up our `DataSource` there:
|
|
538
526
|
|
|
539
527
|
```javascript
|
|
540
|
-
import "reflect-metadata"
|
|
541
|
-
import {
|
|
542
|
-
import { Photo } from "./entity/Photo"
|
|
528
|
+
import "reflect-metadata"
|
|
529
|
+
import { DataSource } from "typeorm"
|
|
530
|
+
import { Photo } from "./entity/Photo"
|
|
543
531
|
|
|
544
|
-
|
|
545
|
-
type: "
|
|
532
|
+
const AppDataSource = new DataSource({
|
|
533
|
+
type: "postgres",
|
|
546
534
|
host: "localhost",
|
|
547
|
-
port:
|
|
535
|
+
port: 5432,
|
|
548
536
|
username: "root",
|
|
549
537
|
password: "admin",
|
|
550
538
|
database: "test",
|
|
551
|
-
entities: [
|
|
552
|
-
Photo
|
|
553
|
-
],
|
|
539
|
+
entities: [Photo],
|
|
554
540
|
synchronize: true,
|
|
555
|
-
logging: false
|
|
556
|
-
})
|
|
557
|
-
|
|
558
|
-
|
|
541
|
+
logging: false,
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
// to initialize initial connection with the database, register all entities
|
|
545
|
+
// and "synchronize" database schema, call "initialize()" method of a newly created database
|
|
546
|
+
// once in your application bootstrap
|
|
547
|
+
AppDataSource.initialize()
|
|
548
|
+
.then(() => {
|
|
549
|
+
// here you can start to work with your database
|
|
550
|
+
})
|
|
551
|
+
.catch((error) => console.log(error))
|
|
559
552
|
```
|
|
560
553
|
|
|
561
|
-
We are using
|
|
554
|
+
We are using Postgres in this example, but you can use any other supported database.
|
|
562
555
|
To use another database, simply change the `type` in the options to the database type you are using:
|
|
563
556
|
`mysql`, `mariadb`, `postgres`, `cockroachdb`, `sqlite`, `mssql`, `oracle`, `cordova`, `nativescript`, `react-native`,
|
|
564
557
|
`expo`, or `mongodb`.
|
|
565
558
|
Also make sure to use your own host, port, username, password and database settings.
|
|
566
559
|
|
|
567
|
-
We added our Photo entity to the list of entities for this
|
|
560
|
+
We added our Photo entity to the list of entities for this data source.
|
|
568
561
|
Each entity you are using in your connection must be listed there.
|
|
569
562
|
|
|
570
563
|
Setting `synchronize` makes sure your entities will be synced with the database, every time you run the application.
|
|
571
564
|
|
|
572
|
-
### Loading all entities from the directory
|
|
573
|
-
|
|
574
|
-
Later, when we create more entities we need to add them to the entities in our configuration.
|
|
575
|
-
This is not very convenient, so instead, we can set up the whole directory, from where all entities will be connected and used in our connection:
|
|
576
|
-
|
|
577
|
-
```javascript
|
|
578
|
-
import { createConnection } from "typeorm";
|
|
579
|
-
|
|
580
|
-
createConnection({
|
|
581
|
-
type: "mysql",
|
|
582
|
-
host: "localhost",
|
|
583
|
-
port: 3306,
|
|
584
|
-
username: "root",
|
|
585
|
-
password: "admin",
|
|
586
|
-
database: "test",
|
|
587
|
-
entities: [
|
|
588
|
-
__dirname + "/entity/*.js"
|
|
589
|
-
],
|
|
590
|
-
synchronize: true,
|
|
591
|
-
}).then(connection => {
|
|
592
|
-
// here you can start to work with your entities
|
|
593
|
-
}).catch(error => console.log(error));
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
But be careful with this approach.
|
|
597
|
-
If you are using `ts-node` then you need to specify paths to `.ts` files instead.
|
|
598
|
-
If you are using `outDir` then you'll need to specify paths to `.js` files inside the outDir directory.
|
|
599
|
-
If you are using `outDir` and when you remove or rename your entities make sure to clear `outDir` directory
|
|
600
|
-
and re-compile your project again, because when you remove your source `.ts` files their compiled `.js` versions
|
|
601
|
-
aren't removed from output directory and still are loaded by TypeORM because they are present in the `outDir` directory.
|
|
602
|
-
|
|
603
565
|
### Running the application
|
|
604
566
|
|
|
605
567
|
Now if you run your `index.ts`, a connection with the database will be initialized and a database table for your photos will be created.
|
|
606
568
|
|
|
607
|
-
|
|
608
569
|
```shell
|
|
609
570
|
+-------------+--------------+----------------------------+
|
|
610
571
|
| photo |
|
|
@@ -623,54 +584,25 @@ Now if you run your `index.ts`, a connection with the database will be initializ
|
|
|
623
584
|
Now let's create a new photo to save it in the database:
|
|
624
585
|
|
|
625
586
|
```javascript
|
|
626
|
-
import {
|
|
627
|
-
import { Photo } from "./entity/Photo"
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
photo.isPublished = true;
|
|
637
|
-
|
|
638
|
-
return connection.manager
|
|
639
|
-
.save(photo)
|
|
640
|
-
.then(photo => {
|
|
641
|
-
console.log("Photo has been saved. Photo id is", photo.id);
|
|
642
|
-
});
|
|
587
|
+
import { DataSource } from "typeorm"
|
|
588
|
+
import { Photo } from "./entity/Photo"
|
|
589
|
+
import { AppDataSource } from "./index"
|
|
590
|
+
|
|
591
|
+
const photo = new Photo()
|
|
592
|
+
photo.name = "Me and Bears"
|
|
593
|
+
photo.description = "I am near polar bears"
|
|
594
|
+
photo.filename = "photo-with-bears.jpg"
|
|
595
|
+
photo.views = 1
|
|
596
|
+
photo.isPublished = true
|
|
643
597
|
|
|
644
|
-
|
|
598
|
+
await AppDataSource.manager.save(photo)
|
|
599
|
+
console.log("Photo has been saved. Photo id is", photo.id)
|
|
645
600
|
```
|
|
646
601
|
|
|
647
602
|
Once your entity is saved it will get a newly generated id.
|
|
648
603
|
`save` method returns an instance of the same object you pass to it.
|
|
649
604
|
It's not a new copy of the object, it modifies its "id" and returns it.
|
|
650
605
|
|
|
651
|
-
### Using async/await syntax
|
|
652
|
-
|
|
653
|
-
Let's take advantage of the latest ES8 (ES2017) features and use async/await syntax instead:
|
|
654
|
-
|
|
655
|
-
```javascript
|
|
656
|
-
import { createConnection } from "typeorm";
|
|
657
|
-
import { Photo } from "./entity/Photo";
|
|
658
|
-
|
|
659
|
-
createConnection(/*...*/).then(async connection => {
|
|
660
|
-
|
|
661
|
-
let photo = new Photo();
|
|
662
|
-
photo.name = "Me and Bears";
|
|
663
|
-
photo.description = "I am near polar bears";
|
|
664
|
-
photo.filename = "photo-with-bears.jpg";
|
|
665
|
-
photo.views = 1;
|
|
666
|
-
photo.isPublished = true;
|
|
667
|
-
|
|
668
|
-
await connection.manager.save(photo);
|
|
669
|
-
console.log("Photo has been saved");
|
|
670
|
-
|
|
671
|
-
}).catch(error => console.log(error));
|
|
672
|
-
```
|
|
673
|
-
|
|
674
606
|
### Using Entity Manager
|
|
675
607
|
|
|
676
608
|
We just created a new photo and saved it in the database.
|
|
@@ -679,16 +611,12 @@ Using entity manager you can manipulate any entity in your app.
|
|
|
679
611
|
For example, let's load our saved entity:
|
|
680
612
|
|
|
681
613
|
```javascript
|
|
682
|
-
import {
|
|
683
|
-
import { Photo } from "./entity/Photo"
|
|
684
|
-
|
|
685
|
-
createConnection(/*...*/).then(async connection => {
|
|
686
|
-
|
|
687
|
-
/*...*/
|
|
688
|
-
let savedPhotos = await connection.manager.find(Photo);
|
|
689
|
-
console.log("All photos from the db: ", savedPhotos);
|
|
614
|
+
import { DataSource } from "typeorm"
|
|
615
|
+
import { Photo } from "./entity/Photo"
|
|
616
|
+
import { AppDataSource } from "./index"
|
|
690
617
|
|
|
691
|
-
|
|
618
|
+
const savedPhotos = await AppDataSource.manager.find(Photo)
|
|
619
|
+
console.log("All photos from the db: ", savedPhotos)
|
|
692
620
|
```
|
|
693
621
|
|
|
694
622
|
`savedPhotos` will be an array of Photo objects with the data loaded from the database.
|
|
@@ -701,29 +629,25 @@ Now let's refactor our code and use `Repository` instead of `EntityManager`.
|
|
|
701
629
|
Each entity has its own repository which handles all operations with its entity.
|
|
702
630
|
When you deal with entities a lot, Repositories are more convenient to use than EntityManagers:
|
|
703
631
|
|
|
704
|
-
|
|
705
632
|
```javascript
|
|
706
|
-
import {
|
|
707
|
-
import { Photo } from "./entity/Photo"
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
photo.isPublished = true;
|
|
717
|
-
|
|
718
|
-
let photoRepository = connection.getRepository(Photo);
|
|
633
|
+
import { DataSource } from "typeorm"
|
|
634
|
+
import { Photo } from "./entity/Photo"
|
|
635
|
+
import { AppDataSource } from "./index"
|
|
636
|
+
|
|
637
|
+
const photo = new Photo()
|
|
638
|
+
photo.name = "Me and Bears"
|
|
639
|
+
photo.description = "I am near polar bears"
|
|
640
|
+
photo.filename = "photo-with-bears.jpg"
|
|
641
|
+
photo.views = 1
|
|
642
|
+
photo.isPublished = true
|
|
719
643
|
|
|
720
|
-
|
|
721
|
-
console.log("Photo has been saved");
|
|
644
|
+
const photoRepository = AppDataSource.getRepository(Photo)
|
|
722
645
|
|
|
723
|
-
|
|
724
|
-
|
|
646
|
+
await photoRepository.save(photo)
|
|
647
|
+
console.log("Photo has been saved")
|
|
725
648
|
|
|
726
|
-
|
|
649
|
+
const savedPhotos = await photoRepository.find()
|
|
650
|
+
console.log("All photos from the db: ", savedPhotos)
|
|
727
651
|
```
|
|
728
652
|
|
|
729
653
|
Learn more about Repository [here](./docs/working-with-repository.md).
|
|
@@ -733,32 +657,33 @@ Learn more about Repository [here](./docs/working-with-repository.md).
|
|
|
733
657
|
Let's try more load operations using the Repository:
|
|
734
658
|
|
|
735
659
|
```javascript
|
|
736
|
-
import {
|
|
737
|
-
import { Photo } from "./entity/Photo"
|
|
660
|
+
import { DataSource } from "typeorm"
|
|
661
|
+
import { Photo } from "./entity/Photo"
|
|
662
|
+
import { AppDataSource } from "./index"
|
|
738
663
|
|
|
739
|
-
|
|
664
|
+
const photoRepository = AppDataSource.getRepository(Photo)
|
|
665
|
+
const allPhotos = await photoRepository.find()
|
|
666
|
+
console.log("All photos from the db: ", allPhotos)
|
|
740
667
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
let firstPhoto = await photoRepository.findOne(1);
|
|
746
|
-
console.log("First photo from the db: ", firstPhoto);
|
|
747
|
-
|
|
748
|
-
let meAndBearsPhoto = await photoRepository.findOne({ name: "Me and Bears" });
|
|
749
|
-
console.log("Me and Bears photo from the db: ", meAndBearsPhoto);
|
|
668
|
+
const firstPhoto = await photoRepository.findOneBy({
|
|
669
|
+
id: 1,
|
|
670
|
+
})
|
|
671
|
+
console.log("First photo from the db: ", firstPhoto)
|
|
750
672
|
|
|
751
|
-
|
|
752
|
-
|
|
673
|
+
const meAndBearsPhoto = await photoRepository.findOneBy({
|
|
674
|
+
name: "Me and Bears",
|
|
675
|
+
})
|
|
676
|
+
console.log("Me and Bears photo from the db: ", meAndBearsPhoto)
|
|
753
677
|
|
|
754
|
-
|
|
755
|
-
|
|
678
|
+
const allViewedPhotos = await photoRepository.findBy({ views: 1 })
|
|
679
|
+
console.log("All viewed photos: ", allViewedPhotos)
|
|
756
680
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
console.log("Photos count: ", photosCount);
|
|
681
|
+
const allPublishedPhotos = await photoRepository.findBy({ isPublished: true })
|
|
682
|
+
console.log("All published photos: ", allPublishedPhotos)
|
|
760
683
|
|
|
761
|
-
|
|
684
|
+
const [photos, photosCount] = await photoRepository.findAndCount()
|
|
685
|
+
console.log("All photos: ", photos)
|
|
686
|
+
console.log("Photos count: ", photosCount)
|
|
762
687
|
```
|
|
763
688
|
|
|
764
689
|
### Updating in the database
|
|
@@ -766,17 +691,16 @@ createConnection(/*...*/).then(async connection => {
|
|
|
766
691
|
Now let's load a single photo from the database, update it and save it:
|
|
767
692
|
|
|
768
693
|
```javascript
|
|
769
|
-
import {
|
|
770
|
-
import { Photo } from "./entity/Photo"
|
|
771
|
-
|
|
772
|
-
createConnection(/*...*/).then(async connection => {
|
|
773
|
-
|
|
774
|
-
/*...*/
|
|
775
|
-
let photoToUpdate = await photoRepository.findOne(1);
|
|
776
|
-
photoToUpdate.name = "Me, my friends and polar bears";
|
|
777
|
-
await photoRepository.save(photoToUpdate);
|
|
694
|
+
import { DataSource } from "typeorm"
|
|
695
|
+
import { Photo } from "./entity/Photo"
|
|
696
|
+
import { AppDataSource } from "./index"
|
|
778
697
|
|
|
779
|
-
|
|
698
|
+
const photoRepository = AppDataSource.getRepository(Photo)
|
|
699
|
+
const photoToUpdate = await photoRepository.findOneBy({
|
|
700
|
+
id: 1,
|
|
701
|
+
})
|
|
702
|
+
photoToUpdate.name = "Me, my friends and polar bears"
|
|
703
|
+
await photoRepository.save(photoToUpdate)
|
|
780
704
|
```
|
|
781
705
|
|
|
782
706
|
Now photo with `id = 1` will be updated in the database.
|
|
@@ -786,16 +710,15 @@ Now photo with `id = 1` will be updated in the database.
|
|
|
786
710
|
Now let's remove our photo from the database:
|
|
787
711
|
|
|
788
712
|
```javascript
|
|
789
|
-
import {
|
|
790
|
-
import { Photo } from "./entity/Photo"
|
|
791
|
-
|
|
792
|
-
createConnection(/*...*/).then(async connection => {
|
|
793
|
-
|
|
794
|
-
/*...*/
|
|
795
|
-
let photoToRemove = await photoRepository.findOne(1);
|
|
796
|
-
await photoRepository.remove(photoToRemove);
|
|
713
|
+
import { DataSource } from "typeorm"
|
|
714
|
+
import { Photo } from "./entity/Photo"
|
|
715
|
+
import { AppDataSource } from "./index"
|
|
797
716
|
|
|
798
|
-
|
|
717
|
+
const photoRepository = AppDataSource.getRepository(Photo)
|
|
718
|
+
const photoToRemove = await photoRepository.findOneBy({
|
|
719
|
+
id: 1,
|
|
720
|
+
})
|
|
721
|
+
await photoRepository.remove(photoToRemove)
|
|
799
722
|
```
|
|
800
723
|
|
|
801
724
|
Now photo with `id = 1` will be removed from the database.
|
|
@@ -806,33 +729,38 @@ Let's create a one-to-one relationship with another class.
|
|
|
806
729
|
Let's create a new class in `PhotoMetadata.ts`. This PhotoMetadata class is supposed to contain our photo's additional meta-information:
|
|
807
730
|
|
|
808
731
|
```javascript
|
|
809
|
-
import {
|
|
810
|
-
|
|
732
|
+
import {
|
|
733
|
+
Entity,
|
|
734
|
+
Column,
|
|
735
|
+
PrimaryGeneratedColumn,
|
|
736
|
+
OneToOne,
|
|
737
|
+
JoinColumn,
|
|
738
|
+
} from "typeorm"
|
|
739
|
+
import { Photo } from "./Photo"
|
|
811
740
|
|
|
812
741
|
@Entity()
|
|
813
742
|
export class PhotoMetadata {
|
|
814
|
-
|
|
815
743
|
@PrimaryGeneratedColumn()
|
|
816
|
-
id: number
|
|
744
|
+
id: number
|
|
817
745
|
|
|
818
746
|
@Column("int")
|
|
819
|
-
height: number
|
|
747
|
+
height: number
|
|
820
748
|
|
|
821
749
|
@Column("int")
|
|
822
|
-
width: number
|
|
750
|
+
width: number
|
|
823
751
|
|
|
824
752
|
@Column()
|
|
825
|
-
orientation: string
|
|
753
|
+
orientation: string
|
|
826
754
|
|
|
827
755
|
@Column()
|
|
828
|
-
compressed: boolean
|
|
756
|
+
compressed: boolean
|
|
829
757
|
|
|
830
758
|
@Column()
|
|
831
|
-
comment: string
|
|
759
|
+
comment: string
|
|
832
760
|
|
|
833
|
-
@OneToOne(
|
|
761
|
+
@OneToOne(() => Photo)
|
|
834
762
|
@JoinColumn()
|
|
835
|
-
photo: Photo
|
|
763
|
+
photo: Photo
|
|
836
764
|
}
|
|
837
765
|
```
|
|
838
766
|
|
|
@@ -868,43 +796,41 @@ If you run the app, you'll see a newly generated table, and it will contain a co
|
|
|
868
796
|
Now let's save a photo, its metadata and attach them to each other.
|
|
869
797
|
|
|
870
798
|
```javascript
|
|
871
|
-
import {
|
|
872
|
-
import { Photo } from "./entity/Photo"
|
|
873
|
-
import { PhotoMetadata } from "./entity/PhotoMetadata"
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}).catch(error => console.log(error));
|
|
799
|
+
import { DataSource } from "typeorm"
|
|
800
|
+
import { Photo } from "./entity/Photo"
|
|
801
|
+
import { PhotoMetadata } from "./entity/PhotoMetadata"
|
|
802
|
+
|
|
803
|
+
// create a photo
|
|
804
|
+
const photo = new Photo()
|
|
805
|
+
photo.name = "Me and Bears"
|
|
806
|
+
photo.description = "I am near polar bears"
|
|
807
|
+
photo.filename = "photo-with-bears.jpg"
|
|
808
|
+
photo.views = 1
|
|
809
|
+
photo.isPublished = true
|
|
810
|
+
|
|
811
|
+
// create a photo metadata
|
|
812
|
+
const metadata = new PhotoMetadata()
|
|
813
|
+
metadata.height = 640
|
|
814
|
+
metadata.width = 480
|
|
815
|
+
metadata.compressed = true
|
|
816
|
+
metadata.comment = "cybershoot"
|
|
817
|
+
metadata.orientation = "portrait"
|
|
818
|
+
metadata.photo = photo // this way we connect them
|
|
819
|
+
|
|
820
|
+
// get entity repositories
|
|
821
|
+
const photoRepository = AppDataSource.getRepository(Photo)
|
|
822
|
+
const metadataRepository = AppDataSource.getRepository(PhotoMetadata)
|
|
823
|
+
|
|
824
|
+
// first we should save a photo
|
|
825
|
+
await photoRepository.save(photo)
|
|
826
|
+
|
|
827
|
+
// photo is saved. Now we need to save a photo metadata
|
|
828
|
+
await metadataRepository.save(metadata)
|
|
829
|
+
|
|
830
|
+
// done
|
|
831
|
+
console.log(
|
|
832
|
+
"Metadata is saved, and the relation between metadata and photo is created in the database too",
|
|
833
|
+
)
|
|
908
834
|
```
|
|
909
835
|
|
|
910
836
|
### Inverse side of the relationship
|
|
@@ -917,31 +843,35 @@ To fix this issue we should add an inverse relation, and make relations between
|
|
|
917
843
|
Let's modify our entities:
|
|
918
844
|
|
|
919
845
|
```javascript
|
|
920
|
-
import {
|
|
921
|
-
|
|
846
|
+
import {
|
|
847
|
+
Entity,
|
|
848
|
+
Column,
|
|
849
|
+
PrimaryGeneratedColumn,
|
|
850
|
+
OneToOne,
|
|
851
|
+
JoinColumn,
|
|
852
|
+
} from "typeorm"
|
|
853
|
+
import { Photo } from "./Photo"
|
|
922
854
|
|
|
923
855
|
@Entity()
|
|
924
856
|
export class PhotoMetadata {
|
|
925
|
-
|
|
926
857
|
/* ... other columns */
|
|
927
858
|
|
|
928
|
-
@OneToOne(
|
|
859
|
+
@OneToOne(() => Photo, (photo) => photo.metadata)
|
|
929
860
|
@JoinColumn()
|
|
930
|
-
photo: Photo
|
|
861
|
+
photo: Photo
|
|
931
862
|
}
|
|
932
863
|
```
|
|
933
864
|
|
|
934
865
|
```javascript
|
|
935
|
-
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from "typeorm"
|
|
936
|
-
import { PhotoMetadata } from "./PhotoMetadata"
|
|
866
|
+
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from "typeorm"
|
|
867
|
+
import { PhotoMetadata } from "./PhotoMetadata"
|
|
937
868
|
|
|
938
869
|
@Entity()
|
|
939
870
|
export class Photo {
|
|
940
|
-
|
|
941
871
|
/* ... other columns */
|
|
942
872
|
|
|
943
|
-
@OneToOne(
|
|
944
|
-
metadata: PhotoMetadata
|
|
873
|
+
@OneToOne(() => PhotoMetadata, (photoMetadata) => photoMetadata.photo)
|
|
874
|
+
metadata: PhotoMetadata
|
|
945
875
|
}
|
|
946
876
|
```
|
|
947
877
|
|
|
@@ -960,31 +890,42 @@ If you use ESM in your TypeScript project, you should use the `Relation` wrapper
|
|
|
960
890
|
Let's modify our entities:
|
|
961
891
|
|
|
962
892
|
```javascript
|
|
963
|
-
import {
|
|
964
|
-
|
|
893
|
+
import {
|
|
894
|
+
Entity,
|
|
895
|
+
Column,
|
|
896
|
+
PrimaryGeneratedColumn,
|
|
897
|
+
OneToOne,
|
|
898
|
+
JoinColumn,
|
|
899
|
+
Relation,
|
|
900
|
+
} from "typeorm"
|
|
901
|
+
import { Photo } from "./Photo"
|
|
965
902
|
|
|
966
903
|
@Entity()
|
|
967
904
|
export class PhotoMetadata {
|
|
968
|
-
|
|
969
905
|
/* ... other columns */
|
|
970
906
|
|
|
971
|
-
@OneToOne(
|
|
907
|
+
@OneToOne(() => Photo, (photo) => photo.metadata)
|
|
972
908
|
@JoinColumn()
|
|
973
|
-
photo: Relation<Photo
|
|
909
|
+
photo: Relation<Photo>
|
|
974
910
|
}
|
|
975
911
|
```
|
|
976
912
|
|
|
977
913
|
```javascript
|
|
978
|
-
import {
|
|
979
|
-
|
|
914
|
+
import {
|
|
915
|
+
Entity,
|
|
916
|
+
Column,
|
|
917
|
+
PrimaryGeneratedColumn,
|
|
918
|
+
OneToOne,
|
|
919
|
+
Relation,
|
|
920
|
+
} from "typeorm"
|
|
921
|
+
import { PhotoMetadata } from "./PhotoMetadata"
|
|
980
922
|
|
|
981
923
|
@Entity()
|
|
982
924
|
export class Photo {
|
|
983
|
-
|
|
984
925
|
/* ... other columns */
|
|
985
926
|
|
|
986
|
-
@OneToOne(
|
|
987
|
-
metadata: Relation<PhotoMetadata
|
|
927
|
+
@OneToOne(() => PhotoMetadata, (photoMetadata) => photoMetadata.photo)
|
|
928
|
+
metadata: Relation<PhotoMetadata>
|
|
988
929
|
}
|
|
989
930
|
```
|
|
990
931
|
|
|
@@ -996,17 +937,17 @@ Let's use `find*` methods first.
|
|
|
996
937
|
`find*` methods allow you to specify an object with the `FindOneOptions` / `FindManyOptions` interface.
|
|
997
938
|
|
|
998
939
|
```javascript
|
|
999
|
-
import {
|
|
1000
|
-
import { Photo } from "./entity/Photo"
|
|
1001
|
-
import { PhotoMetadata } from "./entity/PhotoMetadata"
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
})
|
|
940
|
+
import { DataSource } from "typeorm"
|
|
941
|
+
import { Photo } from "./entity/Photo"
|
|
942
|
+
import { PhotoMetadata } from "./entity/PhotoMetadata"
|
|
943
|
+
import { AppDataSource } from "./index"
|
|
944
|
+
|
|
945
|
+
const photoRepository = AppDataSource.getRepository(Photo)
|
|
946
|
+
const photos = await photoRepository.find({
|
|
947
|
+
relations: {
|
|
948
|
+
metadata: true,
|
|
949
|
+
},
|
|
950
|
+
})
|
|
1010
951
|
```
|
|
1011
952
|
|
|
1012
953
|
Here, photos will contain an array of photos from the database, and each photo will contain its photo metadata.
|
|
@@ -1016,21 +957,15 @@ Using find options is good and dead simple, but if you need a more complex query
|
|
|
1016
957
|
`QueryBuilder` allows more complex queries to be used in an elegant way:
|
|
1017
958
|
|
|
1018
959
|
```javascript
|
|
1019
|
-
import {
|
|
1020
|
-
import { Photo } from "./entity/Photo"
|
|
1021
|
-
import { PhotoMetadata } from "./entity/PhotoMetadata"
|
|
960
|
+
import { DataSource } from "typeorm"
|
|
961
|
+
import { Photo } from "./entity/Photo"
|
|
962
|
+
import { PhotoMetadata } from "./entity/PhotoMetadata"
|
|
963
|
+
import { AppDataSource } from "./index"
|
|
1022
964
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
.getRepository(Photo)
|
|
1028
|
-
.createQueryBuilder("photo")
|
|
1029
|
-
.innerJoinAndSelect("photo.metadata", "metadata")
|
|
1030
|
-
.getMany();
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
}).catch(error => console.log(error));
|
|
965
|
+
const photos = await AppDataSource.getRepository(Photo)
|
|
966
|
+
.createQueryBuilder("photo")
|
|
967
|
+
.innerJoinAndSelect("photo.metadata", "metadata")
|
|
968
|
+
.getMany()
|
|
1034
969
|
```
|
|
1035
970
|
|
|
1036
971
|
`QueryBuilder` allows the creation and execution of SQL queries of almost any complexity.
|
|
@@ -1047,10 +982,10 @@ Let's change our photo's `@OneToOne` decorator a bit:
|
|
|
1047
982
|
export class Photo {
|
|
1048
983
|
/// ... other columns
|
|
1049
984
|
|
|
1050
|
-
@OneToOne(
|
|
985
|
+
@OneToOne(() => PhotoMetadata, (metadata) => metadata.photo, {
|
|
1051
986
|
cascade: true,
|
|
1052
987
|
})
|
|
1053
|
-
metadata: PhotoMetadata
|
|
988
|
+
metadata: PhotoMetadata
|
|
1054
989
|
}
|
|
1055
990
|
```
|
|
1056
991
|
|
|
@@ -1058,34 +993,32 @@ Using `cascade` allows us not to separately save photo and separately save metad
|
|
|
1058
993
|
Now we can simply save a photo object, and the metadata object will be saved automatically because of cascade options.
|
|
1059
994
|
|
|
1060
995
|
```javascript
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
// create photo object
|
|
1064
|
-
let photo = new Photo();
|
|
1065
|
-
photo.name = "Me and Bears";
|
|
1066
|
-
photo.description = "I am near polar bears";
|
|
1067
|
-
photo.filename = "photo-with-bears.jpg";
|
|
1068
|
-
photo.isPublished = true;
|
|
996
|
+
import { AppDataSource } from "./index"
|
|
1069
997
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
metadata.orientation = "portrait";
|
|
998
|
+
// create photo object
|
|
999
|
+
const photo = new Photo()
|
|
1000
|
+
photo.name = "Me and Bears"
|
|
1001
|
+
photo.description = "I am near polar bears"
|
|
1002
|
+
photo.filename = "photo-with-bears.jpg"
|
|
1003
|
+
photo.isPublished = true
|
|
1077
1004
|
|
|
1078
|
-
|
|
1005
|
+
// create photo metadata object
|
|
1006
|
+
const metadata = new PhotoMetadata()
|
|
1007
|
+
metadata.height = 640
|
|
1008
|
+
metadata.width = 480
|
|
1009
|
+
metadata.compressed = true
|
|
1010
|
+
metadata.comment = "cybershoot"
|
|
1011
|
+
metadata.orientation = "portrait"
|
|
1079
1012
|
|
|
1080
|
-
|
|
1081
|
-
let photoRepository = connection.getRepository(Photo);
|
|
1013
|
+
photo.metadata = metadata // this way we connect them
|
|
1082
1014
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1015
|
+
// get repository
|
|
1016
|
+
const photoRepository = AppDataSource.getRepository(Photo)
|
|
1085
1017
|
|
|
1086
|
-
|
|
1018
|
+
// saving a photo also save the metadata
|
|
1019
|
+
await photoRepository.save(photo)
|
|
1087
1020
|
|
|
1088
|
-
|
|
1021
|
+
console.log("Photo is saved, photo metadata is saved too.")
|
|
1089
1022
|
```
|
|
1090
1023
|
|
|
1091
1024
|
Notice that we now set the photo's `metadata` property, instead of the metadata's `photo` property as before. The `cascade` feature only works if you connect the photo to its metadata from the photo's side. If you set the metadata's side, the metadata would not be saved automatically.
|
|
@@ -1097,20 +1030,25 @@ Let's say a photo has one author, and each author can have many photos.
|
|
|
1097
1030
|
First, let's create an `Author` class:
|
|
1098
1031
|
|
|
1099
1032
|
```javascript
|
|
1100
|
-
import {
|
|
1101
|
-
|
|
1033
|
+
import {
|
|
1034
|
+
Entity,
|
|
1035
|
+
Column,
|
|
1036
|
+
PrimaryGeneratedColumn,
|
|
1037
|
+
OneToMany,
|
|
1038
|
+
JoinColumn,
|
|
1039
|
+
} from "typeorm"
|
|
1040
|
+
import { Photo } from "./Photo"
|
|
1102
1041
|
|
|
1103
1042
|
@Entity()
|
|
1104
1043
|
export class Author {
|
|
1105
|
-
|
|
1106
1044
|
@PrimaryGeneratedColumn()
|
|
1107
|
-
id: number
|
|
1045
|
+
id: number
|
|
1108
1046
|
|
|
1109
1047
|
@Column()
|
|
1110
|
-
name: string
|
|
1048
|
+
name: string
|
|
1111
1049
|
|
|
1112
|
-
@OneToMany(
|
|
1113
|
-
photos: Photo[]
|
|
1050
|
+
@OneToMany(() => Photo, (photo) => photo.author) // note: we will create author property in the Photo class below
|
|
1051
|
+
photos: Photo[]
|
|
1114
1052
|
}
|
|
1115
1053
|
```
|
|
1116
1054
|
|
|
@@ -1120,17 +1058,16 @@ export class Author {
|
|
|
1120
1058
|
Now let's add the owner side of the relation into the Photo entity:
|
|
1121
1059
|
|
|
1122
1060
|
```javascript
|
|
1123
|
-
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from "typeorm"
|
|
1124
|
-
import { PhotoMetadata } from "./PhotoMetadata"
|
|
1125
|
-
import { Author } from "./Author"
|
|
1061
|
+
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from "typeorm"
|
|
1062
|
+
import { PhotoMetadata } from "./PhotoMetadata"
|
|
1063
|
+
import { Author } from "./Author"
|
|
1126
1064
|
|
|
1127
1065
|
@Entity()
|
|
1128
1066
|
export class Photo {
|
|
1129
|
-
|
|
1130
1067
|
/* ... other columns */
|
|
1131
1068
|
|
|
1132
|
-
@ManyToOne(
|
|
1133
|
-
author: Author
|
|
1069
|
+
@ManyToOne(() => Author, (author) => author.photos)
|
|
1070
|
+
author: Author
|
|
1134
1071
|
}
|
|
1135
1072
|
```
|
|
1136
1073
|
|
|
@@ -1139,7 +1076,6 @@ It means that the class that uses `@ManyToOne` will store the id of the related
|
|
|
1139
1076
|
|
|
1140
1077
|
After you run the application, the ORM will create the `author` table:
|
|
1141
1078
|
|
|
1142
|
-
|
|
1143
1079
|
```shell
|
|
1144
1080
|
+-------------+--------------+----------------------------+
|
|
1145
1081
|
| author |
|
|
@@ -1171,20 +1107,25 @@ Let's say a photo can be in many albums, and each album can contain many photos.
|
|
|
1171
1107
|
Let's create an `Album` class:
|
|
1172
1108
|
|
|
1173
1109
|
```javascript
|
|
1174
|
-
import {
|
|
1110
|
+
import {
|
|
1111
|
+
Entity,
|
|
1112
|
+
PrimaryGeneratedColumn,
|
|
1113
|
+
Column,
|
|
1114
|
+
ManyToMany,
|
|
1115
|
+
JoinTable,
|
|
1116
|
+
} from "typeorm"
|
|
1175
1117
|
|
|
1176
1118
|
@Entity()
|
|
1177
1119
|
export class Album {
|
|
1178
|
-
|
|
1179
1120
|
@PrimaryGeneratedColumn()
|
|
1180
|
-
id: number
|
|
1121
|
+
id: number
|
|
1181
1122
|
|
|
1182
1123
|
@Column()
|
|
1183
|
-
name: string
|
|
1124
|
+
name: string
|
|
1184
1125
|
|
|
1185
|
-
@ManyToMany(
|
|
1126
|
+
@ManyToMany(() => Photo, (photo) => photo.albums)
|
|
1186
1127
|
@JoinTable()
|
|
1187
|
-
photos: Photo[]
|
|
1128
|
+
photos: Photo[]
|
|
1188
1129
|
}
|
|
1189
1130
|
```
|
|
1190
1131
|
|
|
@@ -1196,12 +1137,12 @@ Now let's add the inverse side of our relation to the `Photo` class:
|
|
|
1196
1137
|
export class Photo {
|
|
1197
1138
|
/// ... other columns
|
|
1198
1139
|
|
|
1199
|
-
@ManyToMany(
|
|
1200
|
-
albums: Album[]
|
|
1140
|
+
@ManyToMany(() => Album, (album) => album.photos)
|
|
1141
|
+
albums: Album[]
|
|
1201
1142
|
}
|
|
1202
1143
|
```
|
|
1203
1144
|
|
|
1204
|
-
After you run the application, the ORM will create a **album_photos_photo_albums**
|
|
1145
|
+
After you run the application, the ORM will create a **album_photos_photo_albums** _junction table_:
|
|
1205
1146
|
|
|
1206
1147
|
```shell
|
|
1207
1148
|
+-------------+--------------+----------------------------+
|
|
@@ -1215,41 +1156,46 @@ After you run the application, the ORM will create a **album_photos_photo_albums
|
|
|
1215
1156
|
Don't forget to register the `Album` class with your connection in the ORM:
|
|
1216
1157
|
|
|
1217
1158
|
```javascript
|
|
1218
|
-
const options:
|
|
1159
|
+
const options: DataSourceOptions = {
|
|
1219
1160
|
// ... other options
|
|
1220
|
-
entities: [Photo, PhotoMetadata, Author, Album]
|
|
1221
|
-
}
|
|
1161
|
+
entities: [Photo, PhotoMetadata, Author, Album],
|
|
1162
|
+
}
|
|
1222
1163
|
```
|
|
1223
1164
|
|
|
1224
1165
|
Now let's insert albums and photos to our database:
|
|
1225
1166
|
|
|
1226
1167
|
```javascript
|
|
1227
|
-
|
|
1168
|
+
import { AppDataSource } from "./index"
|
|
1228
1169
|
|
|
1229
1170
|
// create a few albums
|
|
1230
|
-
|
|
1231
|
-
album1.name = "Bears"
|
|
1232
|
-
await
|
|
1171
|
+
const album1 = new Album()
|
|
1172
|
+
album1.name = "Bears"
|
|
1173
|
+
await AppDataSource.manager.save(album1)
|
|
1233
1174
|
|
|
1234
|
-
|
|
1235
|
-
album2.name = "Me"
|
|
1236
|
-
await
|
|
1175
|
+
const album2 = new Album()
|
|
1176
|
+
album2.name = "Me"
|
|
1177
|
+
await AppDataSource.manager.save(album2)
|
|
1237
1178
|
|
|
1238
1179
|
// create a few photos
|
|
1239
|
-
|
|
1240
|
-
photo.name = "Me and Bears"
|
|
1241
|
-
photo.description = "I am near polar bears"
|
|
1242
|
-
photo.filename = "photo-with-bears.jpg"
|
|
1180
|
+
const photo = new Photo()
|
|
1181
|
+
photo.name = "Me and Bears"
|
|
1182
|
+
photo.description = "I am near polar bears"
|
|
1183
|
+
photo.filename = "photo-with-bears.jpg"
|
|
1243
1184
|
photo.views = 1
|
|
1244
1185
|
photo.isPublished = true
|
|
1245
|
-
photo.albums = [album1, album2]
|
|
1246
|
-
await
|
|
1186
|
+
photo.albums = [album1, album2]
|
|
1187
|
+
await AppDataSource.manager.save(photo)
|
|
1247
1188
|
|
|
1248
1189
|
// now our photo is saved and albums are attached to it
|
|
1249
1190
|
// now lets load them:
|
|
1250
|
-
const loadedPhoto = await
|
|
1251
|
-
|
|
1252
|
-
|
|
1191
|
+
const loadedPhoto = await AppDataSource.getRepository(Photo).findOne({
|
|
1192
|
+
where: {
|
|
1193
|
+
id: 1,
|
|
1194
|
+
},
|
|
1195
|
+
relations: {
|
|
1196
|
+
albums: true,
|
|
1197
|
+
},
|
|
1198
|
+
})
|
|
1253
1199
|
```
|
|
1254
1200
|
|
|
1255
1201
|
`loadedPhoto` will be equal to:
|
|
@@ -1275,8 +1221,7 @@ const loadedPhoto = await connection
|
|
|
1275
1221
|
You can use QueryBuilder to build SQL queries of almost any complexity. For example, you can do this:
|
|
1276
1222
|
|
|
1277
1223
|
```javascript
|
|
1278
|
-
|
|
1279
|
-
.getRepository(Photo)
|
|
1224
|
+
const photos = await AppDataSource.getRepository(Photo)
|
|
1280
1225
|
.createQueryBuilder("photo") // first argument is an alias. Alias is what you are selecting - photos. You must specify it.
|
|
1281
1226
|
.innerJoinAndSelect("photo.metadata", "metadata")
|
|
1282
1227
|
.leftJoinAndSelect("photo.albums", "album")
|
|
@@ -1286,7 +1231,7 @@ let photos = await connection
|
|
|
1286
1231
|
.skip(5)
|
|
1287
1232
|
.take(10)
|
|
1288
1233
|
.setParameters({ photoName: "My", bearName: "Mishka" })
|
|
1289
|
-
.getMany()
|
|
1234
|
+
.getMany()
|
|
1290
1235
|
```
|
|
1291
1236
|
|
|
1292
1237
|
This query selects all published photos with "My" or "Mishka" names.
|
|
@@ -1304,33 +1249,33 @@ Take a look at the samples in [sample](https://github.com/typeorm/typeorm/tree/m
|
|
|
1304
1249
|
|
|
1305
1250
|
There are a few repositories which you can clone and start with:
|
|
1306
1251
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1252
|
+
- [Example how to use TypeORM with TypeScript](https://github.com/typeorm/typescript-example)
|
|
1253
|
+
- [Example how to use TypeORM with JavaScript](https://github.com/typeorm/javascript-example)
|
|
1254
|
+
- [Example how to use TypeORM with JavaScript and Babel](https://github.com/typeorm/babel-example)
|
|
1255
|
+
- [Example how to use TypeORM with TypeScript and SystemJS in Browser](https://github.com/typeorm/browser-example)
|
|
1256
|
+
- [Example how to use TypeORM with TypeScript and React in Browser](https://github.com/ItayGarin/typeorm-react-swc)
|
|
1257
|
+
- [Example how to use Express and TypeORM](https://github.com/typeorm/typescript-express-example)
|
|
1258
|
+
- [Example how to use Koa and TypeORM](https://github.com/typeorm/typescript-koa-example)
|
|
1259
|
+
- [Example how to use TypeORM with MongoDB](https://github.com/typeorm/mongo-typescript-example)
|
|
1260
|
+
- [Example how to use TypeORM in a Cordova/PhoneGap app](https://github.com/typeorm/cordova-example)
|
|
1261
|
+
- [Example how to use TypeORM with an Ionic app](https://github.com/typeorm/ionic-example)
|
|
1262
|
+
- [Example how to use TypeORM with React Native](https://github.com/typeorm/react-native-example)
|
|
1263
|
+
- [Example how to use TypeORM with Nativescript-Vue](https://github.com/typeorm/nativescript-vue-typeorm-sample)
|
|
1264
|
+
- [Example how to use TypeORM with Nativescript-Angular](https://github.com/betov18x/nativescript-angular-typeorm-example)
|
|
1265
|
+
- [Example how to use TypeORM with Electron using JavaScript](https://github.com/typeorm/electron-javascript-example)
|
|
1266
|
+
- [Example how to use TypeORM with Electron using TypeScript](https://github.com/typeorm/electron-typescript-example)
|
|
1322
1267
|
|
|
1323
1268
|
## Extensions
|
|
1324
1269
|
|
|
1325
1270
|
There are several extensions that simplify working with TypeORM and integrating it with other modules:
|
|
1326
1271
|
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1272
|
+
- [TypeORM + GraphQL framework](http://vesper-framework.com)
|
|
1273
|
+
- [TypeORM integration](https://github.com/typeorm/typeorm-typedi-extensions) with [TypeDI](https://github.com/pleerock/typedi)
|
|
1274
|
+
- [TypeORM integration](https://github.com/typeorm/typeorm-routing-controllers-extensions) with [routing-controllers](https://github.com/pleerock/routing-controllers)
|
|
1275
|
+
- Models generation from existing database - [typeorm-model-generator](https://github.com/Kononnable/typeorm-model-generator)
|
|
1276
|
+
- Fixtures loader - [typeorm-fixtures-cli](https://github.com/RobinCK/typeorm-fixtures)
|
|
1277
|
+
- ER Diagram generator - [typeorm-uml](https://github.com/eugene-manuilov/typeorm-uml/)
|
|
1278
|
+
- Create/Drop database - [typeorm-extension](https://github.com/Tada5hi/typeorm-extension)
|
|
1334
1279
|
|
|
1335
1280
|
## Contributing
|
|
1336
1281
|
|