typeorm-entitysharer
v1.0.1
Published
Tool to generate client and server specific classes based on TypeORM entities
Downloads
1
Readme
TypeORM Shared models creator
This tool can be used to create client/server classes based on the same TypeORM entities, allowing you to share them.
This is a work in progress.
Shared interface for client - server architecture
We use typescript to ensure data models integrity in all our programs.
On a client - server architecture, we share data models between both client & server. Doing this way, every update in data models will be automatically visible to both client & server.
The generic architecture we use :
shared interfaces repo
/ \
server repo client repo
Data model integrity on server side
We use typeorm as it allows us to perform database request while keeping these strong typings. Typescript works well with typeorm entities, which are js classes, so there is no special needs on this side.
We also use class-validator
as it allows us to check if a data coming into the api respects the corresponding class structure.
There is also a swagger
on the api.
A basic entity :
import { Entity, Column, PrimaryGeneratedColumn, BaseEntity, OneToMany } from 'typeorm';
import { Project } from './entities';
import { MinLength, MaxLength, IsEmail } from 'class-validator';
import { ApiModelProperty } from '@nestjs/swagger';
@Entity()
export class User extends BaseEntity {
@ApiModelProperty()
@PrimaryGeneratedColumn('uuid')
public id: string;
@ApiModelProperty()
@MinLength(2, { message: 'ERRORS.USERS.FIRSTNAME_TOO_SHORT' })
@MaxLength(50, { message: 'ERRORS.USERS.FIRSTNAME_TOO_LONG' })
@Column()
public firstName: string;
@ApiModelProperty()
@MinLength(2, { message: 'ERRORS.USERS.LASTNAME_TOO_SHORT' })
@MaxLength(50, { message: 'ERRORS.USERS.LASTNAME_TOO_LONG' })
@Column()
public lastName: string;
@ApiModelProperty()
@IsEmail({}, { message: 'ERRORS.USERS.EMAIL' })
@Column({ unique: true })
public email: string;
@ApiModelProperty()
@MinLength(12, { message: 'ERRORS.USERS.PASSWORD_TOO_SHORT' })
@Column()
public password?: string;
@ApiModelProperty()
@Column({ default: false })
public admin: boolean;
@ApiModelProperty({ isArray: true })
@OneToMany(type => Project, project => project.user, {
cascade: true,
})
public projects: Project[];
}
How do we use this :
import { User } from 'shared-entities'
const User = new User()
Client side validation
On client side, we wants to make sure that data coming from the api will respects our model structure, so we will use class-validator
too.
However, typeorm
can not be used on client-side, furthermore it would load a useless amount of code. swagger
is also useless on client side.
What we need on client side is :
- class structure
- class-validator decorators
The expected file would looks like this:
import { Project } from './entities';
import { MinLength, MaxLength, IsEmail } from 'class-validator';
export class User {
public id: string;
@MinLength(2, { message: 'ERRORS.USERS.FIRSTNAME_TOO_SHORT' })
@MaxLength(50, { message: 'ERRORS.USERS.FIRSTNAME_TOO_LONG' })
public firstName: string;
@MinLength(2, { message: 'ERRORS.USERS.LASTNAME_TOO_SHORT' })
@MaxLength(50, { message: 'ERRORS.USERS.LASTNAME_TOO_LONG' })
public lastName: string;
@IsEmail({}, { message: 'ERRORS.USERS.EMAIL' })
public email: string;
@MinLength(12, { message: 'ERRORS.USERS.PASSWORD_TOO_SHORT' })
public password?: string;
public admin: boolean;
public projects: Project[];
}
What has been removed :
typeorm
&swagger
importsextends BaseEntity
keywords- Typeorm decorators
How do we use this :
import { User } from 'shared-entities'
const User = new User()
Build process
Our goal : transform a js class based on typeorm & swagger in a standalone-typescript class.
Perform it client side ?
As the problem remains mainly client side, we could transform our structure when bundling our angular app with webpack.
As described here https://github.com/typeorm/typeorm/issues/62#issuecomment-264490738, we could use a shim in webpack so all typeorm decorators would return an empty function. We would have to do the same for swagger decorators.
Using typescript mixin, we could also transform BasicEntity
class in an empty class, so our User
class would not have any additionnal attributes & methods coming from parent class.
We chose not to do this way for 2 reasons :
- it leads to poor code understanding (all those decorators in a typescript class, which are useless which are not?)
- We think it's the shared interface package reponsability to ensure it's compatibily with both client & server
Build for both client & server
We expects this structure in our shared gitlab repository:
/dist
/client
... client entities : ts + js
/server
... server entities : ts + js
We use gulp as the task runner for the build process.
Build for server :
- take all entities (.ts)
- copy them in
dist/server
(.ts) - transpile them to .js in
dist/server
Build for client
- take all entities (.ts)
- remove useless decorators, imports & extends
- copy result in
dist/client
(.ts) - transpile result in
dist/client
(.js)
See gulpfile.js
for detailed process
Deploy as distinct npm repositories
It is possible to create two npm packages from the same gitlab repository.
Client side :
- install with
npm install shared-entities-client
- use with
import { User } from 'shared-entities-client
Server side :
- install with
npm install shared-entities-server
- use with
import { User } from 'shared-entities-server
Why we should do this :
- only code that we need is imported as dependency (no server entities en client side)
Why we do not do this :
- we would have to pay : )
Deploy this as a single npm repository
It should be possible (i'm on it) to import distinct classes from the same npm package :
Client side :
- install with
npm install shared-entities/client
- use with
import { User } from 'shared-entities/client
Server side :
- install with
npm install shared-entities/server
- use with
import { User } from 'shared-entities/server
Why we do this :
- it's free : )
Why we should not do this :
- useless code imported
Configuration file
You need to create a configuration file called typeorm-entitysharer.json
at your project root, containing the following data:
{
"decoratorsWhitelist": [
"TranslationMinLength",
"TranslationMaxLength"
],
"importsWhitelist": [
"./index",
"../index",
"../errors",
"class-validator",
"./validators"
],
"tsconfigPath": "tsconfig.json",
"entitiesPath": "src/**/*.ts",
"errorsPath": "src/errors/*.ts"
}
- decoratorsWhitelist: the decorators not to be removed
- importsWhitelist: the imports not to be removed
- tsconfigPath: typescript config path (from project root)
- entitiesPath: your entities path (from project root, regex)
- errorsPath: this project also allows to share translations. This is WIP.