Skip to content

Commit a44032e

Browse files
authored
Merge pull request #16 from hodfords-solutions/chore/update-readme
chore: update readme
2 parents b9fc31e + cdb897d commit a44032e

File tree

4 files changed

+124
-97
lines changed

4 files changed

+124
-97
lines changed

README.md

+120-93
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,138 @@
1-
# TYPEORM HELPER
1+
<p align="center">
2+
<a href="http://opensource.hodfords.uk" target="blank"><img src="https://opensource.hodfords.uk/img/logo.svg" width="320" alt="Hodfords Logo" /></a>
3+
</p>
24

3-
Provide functions for relational handling in Typeorm.
5+
<p align="center"> <b>typeorm-helper</b> enhances TypeORM integration in your NestJS projects, streamlining database interactions and management. It offers utilities for easier setup and configuration of TypeORM, simplifies common database operations, and promotes best practices for managing entities and migrations. You can efficiently manage your data layer, ensuring robust and maintainable data access within your application. </p>
46

5-
## How to use?
7+
## Installation 🤖
68

7-
Extend BaseEntity from typeorm-helper
9+
Install the `typeorm-helper` package with:
810

9-
```typescript
10-
export class Post extends BaseEntity {
11-
}
11+
```bash
12+
npm install @hodfords/typeorm-helper --save
1213
```
1314

14-
Extend BaseRepository from typeorm-helper
15+
## Usage 🚀
16+
17+
### Defining custom repositories and entities
18+
19+
When managing different entities, you can define custom repositories and entities. Below is an example for the Category entity and its corresponding repository.
20+
21+
#### Entity
22+
23+
The `Category` table in the database is modeled by the `CategoryEntity`, `typeorm` decorators should be used to define this entity.
1524

1625
```typescript
17-
@EntityRepository(Post)
18-
export class PostRepository extends BaseRepository<Post> {
26+
import { BaseEntity } from '@hodfords/typeorm-helper';
27+
import { Column, Entity, ManyToMany, JoinTable, PrimaryGeneratedColumn } from 'typeorm';
28+
29+
@Entity('Category')
30+
export class CategoryEntity extends BaseEntity {
31+
@PrimaryGeneratedColumn()
32+
id: number;
33+
34+
@Column()
35+
name: string;
36+
37+
@ManyToMany(() => PostEntity, (post) => post.categories)
38+
@JoinTable({ name: 'PostCategory' })
39+
posts: PostEntity[];
1940
}
2041
```
2142

22-
## RELATION LOADER
43+
#### Repository
2344

24-
##### Single
45+
The `CategoryRepository` is a custom repository that handles all database operations related to the `CategoryEntity`. By using the `@CustomRepository` decorator and extending `BaseRepository`, you ensure that your repository has both common CRUD functionality and can be easily customized with entity-specific methods.
2546

2647
```typescript
27-
let user = await User.createQueryBuilder().getOne();
28-
await user.loadRelation(['posts', 'roles']);
48+
import { CustomRepository, BaseRepository } from '@hodfords/typeorm-helper';
49+
50+
@CustomRepository(CategoryEntity)
51+
export class CategoryRepository extends BaseRepository<CategoryEntity> {}
2952
```
3053

31-
##### Multiple
54+
### Lazy Relations
55+
56+
Lazy relations allow you to load related entities only when they are needed. This can significantly improve performance by preventing the fetching of unnecessary data upfront.
57+
58+
This functionality supports handling single entity, collection of entities, and paginated collection. Below is an example of how to load a list of posts associated with a specific category.
59+
60+
##### Single entity
3261

3362
```typescript
34-
let users = await User.createQueryBuilder().find();
35-
await users.loadRelation({
36-
'posts': (query) => {
37-
query.where(' orginationId != :orginationId ', { orginationId: 1 })
38-
}
39-
});
63+
const categoryRepo = getDataSource().getCustomRepository(CategoryRepository);
64+
const category = await categoryRepo.findOne({});
65+
await category.loadRelation(['posts']);
4066
```
4167

42-
##### Pagination
68+
##### Collection of entities
4369

4470
```typescript
45-
let userRepo = getConnection().getCustomRepository(UserRepository);
46-
let userPagination = await userRepo.pagination({}, { page: 1, perPage: 10 });
47-
await userPagination.loadRelation('posts');
71+
const categoryRepo = getDataSource().getCustomRepository(CategoryRepository);
72+
const categories = await categoryRepo.findOne({ name: ILIKE('%football' });
73+
await this.categories.loadRelations(['posts']);
4874
```
4975
50-
## Repository
76+
##### Paginate collection
5177
5278
```typescript
53-
@EntityRepository(Category)
54-
export class CategoryRepository extends BaseRepository<Category> {
55-
}
79+
const categoryRepo = getDataSource().getCustomRepository(CategoryRepository);
80+
const pagedCategories = await categoryRepo.pagination({}, { page: 1, perPage: 10 });
81+
await pagedCategories.loadRelation('posts');
5682
```
5783
58-
## Entity
84+
You can also make use of the loadRelations function to efficiently load and retrieve related data
5985
6086
```typescript
61-
@Entity()
62-
export class Category extends BaseEntity {
63-
@PrimaryGeneratedColumn()
64-
id: number;
65-
66-
@Column()
67-
name: string;
87+
await loadRelations(categories, ['posts']);
88+
```
6889
69-
@ManyToMany(() => Post, (post) => post.categories)
70-
posts: Post[];
90+
### Relation Condition
7191
72-
@OneToMany(() => PostCategory, postToCategory => postToCategory.category)
73-
public postCategories!: PostCategory[];
74-
}
75-
```
92+
Sometimes, you need to add custom conditions when loading related entities. `typeorm-helper` provides the
93+
`@RelationCondition` decorator for this purpose.
7694
77-
## Relation condition
95+
##### Simple condition
7896
79-
##### Simple
97+
This ensures that the posts relation is only loaded when the condition `posts.id = :postId` is satisfied.
8098
8199
```typescript
82-
@Entity()
83-
export class User extends BaseEntity {
100+
@Entity('User')
101+
export class UserEntity extends BaseEntity {
84102
@PrimaryGeneratedColumn()
85103
id: number;
86104

87105
@Column()
88106
name: string;
89107

90-
@RelationCondition(
91-
(query: SelectQueryBuilder<any>) => {
92-
query.where(' posts.id = :postId', { postId: 1 });
93-
}
94-
)
95-
@OneToMany(() => Post, (post) => post.user, { cascade: true })
96-
posts: Post[];
108+
@RelationCondition((query: SelectQueryBuilder<any>) => {
109+
query.where(' posts.id = :postId', { postId: 1 });
110+
})
111+
@OneToMany(() => PostEntity, (post) => post.user, { cascade: true })
112+
posts: PostEntity[];
97113

98-
@RelationCondition(
99-
(query: SelectQueryBuilder<any>, entities) => {
100-
query.orderBy('id', 'DESC');
101-
if (entities.length === 1) {
102-
query.limit(1);
103-
} else {
104-
query.andWhere(' "latestPost".id in (select max(id) from "post" "maxPost" where "maxPost"."userId" = "latestPost"."userId")');
105-
}
114+
@RelationCondition((query: SelectQueryBuilder<any>, entities) => {
115+
query.orderBy('id', 'DESC');
116+
if (entities.length === 1) {
117+
query.limit(1);
118+
} else {
119+
query.andWhere(
120+
' "latestPost".id in (select max(id) from "post" "maxPost" where "maxPost"."userId" = "latestPost"."userId")'
121+
);
106122
}
107-
)
108-
@OneToOne(() => Post, (post) => post.user, { cascade: true })
109-
latestPost: Post;
123+
})
124+
@OneToOne(() => PostEntity, (post) => post.user, { cascade: true })
125+
latestPost: PostEntity;
110126
}
111127
```
112128
113-
### Map data
129+
#### Complex condition
130+
131+
Here, the condition applies a limit if only one entity is found, and fetches the latest post for each user if there are multiple entities.
114132
115133
```typescript
116-
@Entity()
117-
export class User extends BaseEntity {
134+
@Entity('User')
135+
export class UserEntity extends BaseEntity {
118136
@PrimaryGeneratedColumn()
119137
id: number;
120138

@@ -126,19 +144,18 @@ export class User extends BaseEntity {
126144
query.where(' posts.id = :postId', { postId: 1 });
127145
},
128146
(entity, result, column) => {
129-
if (entity.id === 2) {
130-
return false;
131-
}
132-
return true;
147+
return entity.id !== 2;
133148
}
134149
)
135-
@OneToMany(() => Post, (post) => post.user, { cascade: true })
136-
posts: Post[];
150+
@OneToMany(() => PostEntity, (post) => post.user, { cascade: true })
151+
posts: PostEntity[];
137152
}
138153
```
139154
140-
## WHERE EXPRESSION
141-
>For queries that are complex, need to be reused, or contain a lot of logic. We should use a class to store it.
155+
### Where Expression
156+
157+
For complex queries that need to be reused or involve a lot of logic, it's best to put them in a class
158+
142159
```typescript
143160
export class BelongToUserWhereExpression extends BaseWhereExpression {
144161
constructor(private userId: number) {
@@ -151,40 +168,40 @@ export class BelongToUserWhereExpression extends BaseWhereExpression {
151168
}
152169
```
153170
154-
Use:
155171
```typescript
156-
this.postRepo.find({
157-
where: new BelongToUserWhereExpression(1)
158-
})
172+
const posts = await this.postRepo.find({ where: new BelongToUserWhereExpression(1) });
159173
```
160174
161-
## Query Builder
162-
>For queries that are complex, need to be reused, or contain a lot of logic. We should use a class to store it.
175+
### Query Builder
176+
177+
For complex and reusable queries, it's helpful to put the logic inside a class. This makes it easier to manage and reuse the query, resulting in cleaner and more maintainable code.
178+
163179
```typescript
164-
export class PostOfUserQuery extends BaseQuery<Post> {
180+
export class PostOfUserQuery extends BaseQuery<PostEntity> {
165181
constructor(private userId: number) {
166182
super();
167183
}
168184

169-
query(query: SelectQueryBuilder<Post>) {
170-
query.where({ userId: this.userId })
171-
.limit(10);
185+
query(query: SelectQueryBuilder<PostEntity>) {
186+
query.where({ userId: this.userId }).limit(10);
172187
}
173188

174-
order(query: SelectQueryBuilder<Post>) {
189+
order(query: SelectQueryBuilder<PostEntity>) {
175190
query.orderBy('id', 'DESC');
176191
}
177192
}
178193
```
179194
180195
```typescript
181-
this.postRepo.find(new PostOfUserQuery(1));
196+
const posts = await this.postRepo.find(new PostOfUserQuery(1));
182197
```
183198
184-
## MIGRATIONS
185-
> We create a class, which wraps the migration of typeorm, allowing for simpler and more readable. For the update command, let's use pure queries for the time being.
199+
### Migrations
200+
201+
We develop a class that abstracts the typeorm migration, making it easier to understand. For the update command, let's use pure queries for the time being.
202+
203+
### Example
186204
187-
- Example:
188205
```typescript
189206
export class CreateUserTable1626749239046 extends BaseMigration {
190207
async run(queryRunner: QueryRunner) {
@@ -205,12 +222,15 @@ export class CreateUserTable1626749239046 extends BaseMigration {
205222
}
206223

207224
async rollback(queryRunner: QueryRunner) {
208-
await this.drop('Fuel');
225+
await this.drop('User');
209226
}
210227
}
211228
```
212229
213-
### Table method
230+
### Table methods
231+
232+
The Table class provides various methods for defining columns in a database schema
233+
214234
```typescript
215235
string(name: string, length?: number, options?: Partial<TableColumnOptions>): BaseColumn;
216236
strings(name: string, options?: Partial<TableColumnOptions>): BaseColumn;
@@ -231,6 +251,9 @@ export class CreateUserTable1626749239046 extends BaseMigration {
231251
```
232252
233253
### Column method
254+
255+
The BaseColumn class provides methods that define and configure properties for a database column, including length, nullability, uniqueness, indexing, default values, and foreign key relationships.
256+
234257
```typescript
235258
length(length: number): this;
236259
nullable(): this;
@@ -239,3 +262,7 @@ export class CreateUserTable1626749239046 extends BaseMigration {
239262
default(value: any): this;
240263
foreign(table: string, column?: string, onDelete?: string, onUpdate?: string): void;
241264
```
265+
266+
## License 📝
267+
268+
This project is licensed under the MIT License

cspell.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"version": "0.2",
33
"language": "en",
4-
"words": ["nestjs", "typeorm", "Metadatas", "bytea", "hodfords", "Tien", "ormconfig", "npmjs"],
4+
"words": ["nestjs", "typeorm", "Metadatas", "bytea", "hodfords", "Tien", "ormconfig", "npmjs", "ILIKE"],
55
"flagWords": ["hte"],
66
"ignorePaths": ["node_modules", "test", "*.spec.ts", "cspell.json"]
77
}

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hodfords/typeorm-helper",
3-
"version": "10.1.0",
3+
"version": "10.1.1",
44
"description": "Simplifies TypeORM usage in NestJS apps",
55
"license": "MIT",
66
"readmeFilename": "README.md",

0 commit comments

Comments
 (0)