Closed chiqui3d closed 4 years ago
In addition to the above marked supposedly when adding the typeorm
options to @ServerSettings
it should create a connection as shown in the TypeORM documentation https://typeorm.io/#example-with-express/adding-express-to-the-application, as this does not create it, I have had to manually add the function, also add ormconfig.yml
to make it work.
And finally based on your example of repository injection into the Controller/Service/Provider
http://tsed.io/tutorials/typeorm.html#entityrepository it doesn't work either.
I have had to do it without injection in a Controller
and with the options offered by TypeORM itself.
the behaviour expected:
https://github.com/typeorm/typeorm-typedi-extensions#injectrepository
Hello @chiqui3d The next release will solve issue on type checking (I guess). I also updated the typeorm example. You'll find a working example with a docker-compose file. The database is correctly connected to server application and you can use swagger to play with the API.
Can you checkout the new example and compare with your code, please :).
See you, Romain
Okay, perfect.
In my example I had to remove the type in the connection
property, because if I didn't get the same error as before, I guess because in my example I use Typescript 3.7
.
Then I need to be able to directly inject a custom EntityRepository
into the Controller
, without having to create a Service/Connection in between, very similar to how https://github.com/typeorm/typeorm-typedi-extensions#injectrepository does, since I followed your example http://tsed.io/tutorials/typeorm.html#entityrepository and it didn't work for me in the controller
.
Maybe I need something else? But the ideal is to be able to do it directly.
It should work for me. Controller and Service are build by injector with the same mechanism.
So this code should work:
import {Inject, Injectable} from "@tsed/di";
import {UserRepository} from "./repository/UserRepository";
@Controller("/")
export class MyController {
constructor(userRepository: UserRepository) {
}
}
For me not:
I'll give you the example:
import {Repository} from 'typeorm'
import { Order } from '../Entity/Orderp'
import {EntityRepository} from '@tsed/typeorm'
@EntityRepository(Order)
export class OrderRepository extends Repository<Order> {
findByID (id: number): Promise<Order> {
return this.findOne(id)
}
}
import {Inject, Injectable} from "@tsed/di";
import { Controller, Get, PathParams } from '@tsed/common'
import { Order } from '../Entity/Orderp'
import { NotFound } from 'ts-httpexceptions'
import { OrderRepository } from '../Repository/OrderRepository'
@Controller('/order')
export class OrderController {
constructor (private orderRepository: OrderRepository) {
}
@Get('/:id')
async get (@PathParams('id') id: number): Promise<Order | NotFound> {
const order = this.orderRepository.findByID(id)
if (!order) {
throw new NotFound('Not Order Found')
}
return order
}
}
Error:
"Cannot read property 'findOne' of undefined",
And with this example:
import {Inject, Injectable} from "@tsed/di";
import { Controller, Get, PathParams } from '@tsed/common'
import { Order } from '../Entity/Orderp'
import { NotFound } from 'ts-httpexceptions'
import { OrderRepository } from '../Repository/OrderRepository'
@Controller('/order')
export class OrderController {
@Inject() // use Inject decorator
private orderRepository: OrderRepository
@Get('/:id')
async get (@PathParams('id') id: number): Promise<Order | NotFound> {
const order = this.orderRepository.findByID(id)
if (!order) {
throw new NotFound('Not Order Found')
}
return order
}
}
Hey @Romakita, same error.
Ok I'll check on my side :)
Meanwhile, can you try this pls:
import {Inject, Injectable} from "@tsed/di";
import { Controller, Get, PathParams } from '@tsed/common'
import { Order } from '../Entity/Orderp'
import { NotFound } from 'ts-httpexceptions'
import { OrderRepository } from '../Repository/OrderRepository'
console.log('===> OrderRepository', OrderRepository) // if it's undefined, you have a circular ref
@Controller('/order')
export class OrderController {
@Inject() // use Inject decorator
private orderRepository: OrderRepository
@Get('/:id')
async get (@PathParams('id') id: number): Promise<Order | NotFound> {
const order = this.orderRepository.findByID(id)
if (!order) {
throw new NotFound('Not Order Found')
}
return order
}
}
See you
Result
===> OrderRepository [Function: OrderRepository]
ok I added example with a Repository. The example will be release soon.
Have you used the EntityRepository decorator from Ts.ED to declare your repository ?
import {EntityRepository} from "@tsed/typeorm"; /// important
@EntityRepository(User)
export class UserRepository extends Repository<User> {
}
import {Repository} from 'typeorm'
import { Order } from '../Entity/Orderp'
import {EntityRepository} from '@tsed/typeorm'
@EntityRepository(Order)
export class OrderRepository extends Repository<Order> {
findByID (id: number): Promise<Order> {
return this.findOne(id)
}
}
ok
I added also:
@ServerSettings({
componentsScan: [
`${rootDir}/repositories/*{.ts,.js}`
],
})
Not sure if it change something.
The version 5.38.2 is released. It should fix typescript et dependencies issues.
I tried that too, I put you the @ServerSettings just in case.
import { GlobalAcceptMimesMiddleware, ServerLoader, ServerSettings } from '@tsed/common'
import '@tsed/swagger'
import '@tsed/typeorm'
import { join } from 'path'
import * as bodyParser from "body-parser";
import * as compress from "compression";
import * as methodOverride from "method-override";
import * as session from "express-session";
const srcDir = __dirname
const rootDir = join(__dirname, '../')
@ServerSettings({
srcDir,
viewsDir: `${rootDir}views`,
statics: {
'/': `${rootDir}public`
},
uploadDir: `${rootDir}public/uploads`,
httpPort: '127.0.0.1:3000',
httpsPort: '127.0.0.1:8000',
acceptMimes: ['application/json'],
debug: true,
validationModelStrict: true,
mount: {
'/': `${srcDir}/Controller/**/*.ts`
},
componentsScan: [
`${srcDir}/Service/**/*.ts`,
`${srcDir}/Repository/**/*.ts`
],
logger: {
requestFields: ['method', 'url']
},
typeorm: [
{
name: 'default',
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '******',
database: 'api_wp',
synchronize: true,
logging: true,
entities: [
`${srcDir}/Entity/*{.ts,.js}`
],
migrations: [
`${srcDir}/Migration/*{.ts,.js}`
],
subscribers: [
`${srcDir}/Subscriber/*{.ts,.js}`
]
}
],
swagger: {
path: '/api-docs'
}
})
export class Server extends ServerLoader {
$beforeInit () {
this.set('views', this.settings.get('viewsDir'))
this.set('view engine', 'twig')
}
$beforeRoutesInit (): void | Promise<any> {
this
.use(GlobalAcceptMimesMiddleware)
.use(compress({}))
.use(methodOverride())
.use(bodyParser.json())
.use(bodyParser.urlencoded({
extended: true
}))
this.set('trust proxy', 1)
this.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: {
secure: false, // `true` require HTTPS. Set `false` if you reach the server with HTTP
path: '/',
httpOnly: true,
maxAge: null
}
}))
return null
}
}
Can you try a npm list typeorm
. You must have this:
├─┬ @tsed/typeorm@5.38.2
│ └── typeorm@0.2.22 deduped // dedup is important
└── typeorm@0.2.22
If not run npm dedup
:)
New example version is available :) https://github.com/TypedProject/tsed-example-typeorm/tree/v5.38.2
Yeah!! :), Now it works perfectly. Thank you
Hello @Romakita
I am now injecting my custom UserRepository
into a Service that implements BeforeRoutesInit, AfterRoutesInit
for Local Passport
Before I confirm that the componentScan
option I have assigned the correct folders, in this case Auth
directory.
I haven't finished the code yet, I'm testing it, I also tried to use getCustomRepository
of typeORM
and it works fine with it, but injecting the repository with DI doesn't work.
LocalAuth.ts
import {
AfterRoutesInit,
BeforeRoutesInit,
Configuration,
ExpressApplication,
Inject,
Service
} from '@tsed/common'
import * as Passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';
import { UserRepository } from '../Repository/UserRepository';
console.log('UserRepository => ',UserRepository) // UserRepository => [Function: UserRepository]
@Service()
export class LocalAuth implements BeforeRoutesInit, AfterRoutesInit {
// I've also tried with @Inject(UserRepository)
constructor (private userRepository: UserRepository,
@Configuration() private configuration: Configuration,
@Inject(ExpressApplication) private expressApplication: ExpressApplication) {
}
$beforeRoutesInit () {
const options: any = this.configuration.get('passport') || {} as any;
const { userProperty, pauseStream } = options;
this.expressApplication.use(Passport.initialize({ userProperty }));
this.expressApplication.use(Passport.session({ pauseStream }));
}
$afterRoutesInit () {
Passport.use('local', new LocalStrategy(this.localStrategy));
}
localStrategy (username, password, done) {
const user = this.userRepository.findOne({ 'username': username });
if (!user) {
return done(null, false, { message: 'An incorrect username or password.' });
}
return done(null, user);
}
}
The error is the same as above:
"Cannot read property 'findOne' of undefined",
Hello @Romakita
Is this fixed?
Hello :)
I try to reproduce the problem. And give you a complete example ;)
See you
Fixed with v5.39.0 :)
The example you gave me in the other issue I haven't tested it, now what I've done is update the version to 5.39.1 and test it in my current code:
import {
AfterRoutesInit,
BeforeRoutesInit,
Configuration,
ExpressApplication,
Inject,
Service
} from '@tsed/common'
import * as Passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';
import { UserRepository } from '../Repository/UserRepository';
import { getCustomRepository } from 'typeorm'
import { User } from '../Entity/User'
import { plainToClass } from 'class-transformer';
@Service()
export class LocalAuth implements BeforeRoutesInit, AfterRoutesInit {
constructor (private userRepository: UserRepository,
@Configuration() private configuration: Configuration,
@Inject(ExpressApplication) private expressApplication: ExpressApplication) {
}
$beforeRoutesInit () {
this.expressApplication.use(Passport.initialize());
this.expressApplication.use(Passport.session());
}
$afterRoutesInit () {
Passport.use('local', new LocalStrategy({ passReqToCallback: true }, this.localStrategy));
Passport.serializeUser((user, done) => LocalAuth.serialize(user, done));
Passport.deserializeUser((id, done) => LocalAuth.deserialize(id, done));
}
async localStrategy (req, username: string, password: string, done: Function) {
const bcrypt = require('bcrypt');
const user = await getCustomRepository(UserRepository).findOne({ 'username': username });
if (!user) {
return done(null, false, { message: 'Username incorrect' });
}
const matchPassword = await bcrypt.compare(password, user.password);
if (matchPassword) {
return done(null, user);
}
return done(null, false, { message: 'Password incorrect.' });
}
static async serialize (user, done) {
done(null, user.id);
}
static async deserialize (id, done) {
const user: User = await getCustomRepository(UserRepository).findOne(id);
const userDto: User = plainToClass(User, user);
// const userConverter = converter.deserialize(user, UserDTO);
done(null, userDto);
}
}
This code currently works perfectly, but without using the repository injection dependency, I've tried it and it doesn't work. So do I forget to inject my custom repository through DI? or is there something that doesn't work when you implement the BeforeRoutesInit, AfterRoutesInit
in the service.
I also noticed that the custom repository in your example has put
import {EntityRepository, Repository} from "typeorm";
should we finally use your library or the typeorm library, or is it the same thing?
I've also seen that you've uploaded new documentation to the repository, but I don't see it updated on the tsed.io page either, I don't know if you've forgotten or need to finish something.
Hello @chiqui3d Now you can use EntityRepository directly from repository. I introduce a new feature, that allow to configure external DI with Ts.ED. The @tsed/typeorm package configure your app to share symbols between TypeORM DI and Ts.ED DI.
I forgot to update typeorm documentation page.
This code currently works perfectly, but without using the repository injection dependency, I've tried it and it doesn't work.
I don't know why, because the tsed-typeorm example work perfectly with the injection. The only way to understand why it doesn't is to have an access to your repository so that I can test your application.
BeforeRoutesInit, AfterRoutesInit are just a lifecycle hook to intercept event. Normally you must have the repository instance on the constructor.
See you Romain
Sorry I just tried it again and it works, don't tell me how, but I'm sure it would be something caching or I don't know, lol.
Thank you!
Ha ok Good news. (probably you compile your tsfile before run your node process? do you use tsnode ?)
Yes I use ts-node, exactly:
"start:dev": "nodemon --watch \"src/**/*.ts\" --ignore \"node_modules/**/*\" --exec ts-node src/index.ts",
Ok strange ^^
Information
Packages:
"@tsed/common": "^5.38.0", "@tsed/core": "^5.38.0", "@tsed/di": "^5.38.0", "@tsed/socketio": "^5.38.0", "@tsed/swagger": "^5.38.0", "@tsed/testing": "^5.38.0", "@tsed/typeorm": "^5.38.0", "bcrypt": "^3.0.7", "body-parser": "^1.18.3", "compression": "^1.7.3", "cookie-parser": "^1.4.3", "express": "^4.17.1", "express-session": "^1.17.0", "method-override": "^3.0.0", "mysql": "^2.17.1", "pg": "^7.17.1", "secure-password": "^3.1.0", "serve-static": "^1.13.1", "socket.io": "^2.1.0", "sodium-native": "^2.4.6", "ts-httpexceptions": "^4.1.0", "twig": "^1.14.0", "typeorm": "^0.2.22"
I've been a while without using TSED, now I'm back to take a project I have started that worked before and now it doesn't work.
Errors
It gives me a lot of errors regarding types, plus it can't connect to the database either, I think it's because of this.
The exact error:
this.connection = this.typeORMService.get()
Example
This code is taken from the same example repository with TypeORM https://github.com/TypedProject/tsed-example-typeorm/