I have a data layer that reads and writes to a MongoDB instance. I only want to deal with MongoDB documents at that layer and not expose that implementation to my services.
Right now I am doing something like:
// users.repository.ts
...
async getUserById(id: string): Promise<UserDto> {
const user = await this.model.findOne({ _id: id }).exec();
return this.transformToDto(user);
}
private transformToDto(user: UserDocument): UserDto {
return {
id: user._id,
...etc
}
}
...
This seems overly verbose and there must be a simpler way to achieve this without adding a helper to every repository.
Is there a cleaner way to achieve this?
You can use class-transformer for that and you don't need to use extra helper methods it can be returned instantly.
import { plainToClass } from 'class-transformer';
class UserDto {
id: string;
email: string;
role: string;
}
class Service {
async getUserById(id: string): Promise<UserDto> {
const user = await this.model.findOne({ _id: id }).exec();
return plainToClass(UserDto, user);
}
}
It will return transformed value which is UserDto
UserDto { id: 'U-111', email: 'U-111#email', role: 'user' }
Related
Hi I am trying to figure out how to create factory and define relationship between models.
For example I have UserFactory with User entity and this entity has connection to userType table. In factory I have not access to EntityManager so I couldn´t find any existing.
export class UserFactory extends Factory<User> {
model = User
definition(faker: Faker): Partial<User> {
const user = {
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
...
userType: // Here I need do something like this:
// EntityManager.findOne(UserType, {id: 1}}
// But EntityManager is private in Factory class
}
return user
}
}
Itried also something like this but this return me an error:
ValidationError: Value for User.type is required, 'undefined' found
DatabaseSeeder
export class DatabaseSeeder extends Seeder {
async run(em: EntityManager): Promise<void> {
const users: User[] = new UserFactory(em).each(async user => {
const userType : UserType| null = await em.findOne(UserType, 1)
console.log(tenant)
const userType = await em.findOne(UserType, 1)
if (userType !== null) {
user.type = userType
} else {
user.type = em.create(UserType, {
type: 'test'
})
}
}).make(10)
}
}
What is the proper way to achieve this please?
You can use the shared seeder context as describer in the docs:
https://mikro-orm.io/docs/seeding#shared-context
export class AuthorSeeder extends Seeder {
async run(em: EntityManager, context: Dictionary): Promise<void> {
// save the entity to the context
context.author = em.create(Author, {
name: '...',
email: '...',
});
}
}
export class BookSeeder extends Seeder {
async run(em: EntityManager, context: Dictionary): Promise<void> {
em.create(Book, {
title: '...',
author: context.author, // use the entity from context
});
}
}
I guess this shared context should be also available in the seeder factories, but you can always handle this yourself, as both the seeder and factory is your implementation, so you can pass any additional options in there. Its you who initializes the factory so I dont think there is a better way than doing it in your code.
I would suggest not to flush and findOne things in your seeder, you should aim for a single flush and use the shared context instead for entity look up.
I am trying to get a user instance based on id (same happens for other attributes such as email. Inside the Service, this is my code:
#Injectable()
export class UserService {
#InjectRepository(User)
private readonly repository: Repository<User>;
async findOne(id: number): Promise<User> {
const user = await this.repository.findOne(id);
return user;
}
}
and my User entity is:
#Entity()
export class User {
#PrimaryGeneratedColumn()
public id: number;
#Column({ type: 'varchar', length: 120 })
public name: string;
#Column({ type: 'varchar', length: 120 })
public email: string;
}
The problem is that I always get this error:
src/api/user/user.service.ts - error TS2559: Type 'number' has no properties in common with type 'FindOneOptions<User>'.
Other methods such as getAll work just fine:
public getAllUsers(): Promise<User[]> {
return this.repository.find();
}
There are some breaking changes in typeorm. I wouldn't suggest downgrading, instead check the latest methods.
findOne(id);
is now changed to
findOneBy({
id: id // where id is your column name
})
And find() is now
find({
select: {
id: true,
email: true,
password: true,
},
});
Please check this link for more information.
are you using the latest version of typeorm? Then downgrade it to typeorm#0.2 because #nestjs/typeorm#8.0 might not support the latest one yet. You can read the changes of typeorm#0.3 here: https://github.com/typeorm/typeorm/releases/tag/0.3.0
Actually you don't need to downgrade the typeorm package. Just changed to findOne by this:
async findOne(id: number): Promise<User> {
const user = await this.repository.findOne({
where: { id }
});
return user;
}
check-in your package.json file and replace your version of typeorm with this one "typeorm": "^0.2.34"
the problem is the typeorm version , try typeorm version 0.2.25 and it will be works
Also can someone explain this. I am looking at a code where
async findOne(id: FindOneOptions<User>): Promise<User> {
const user = await this.repository.findOne(id)
return user;
}
this is done. Although it does not work but why did we declare the number id as FindOneOptions<User>
findOne(id) signature was dropped. Use following syntax instead:
const user = await userRepository.findOneBy({
id: id // where id is your column name
})
According to the latest version of Typeorm, findOne expression has been changed as above.
I'm learning Relay to use in a React-Relay project. After my research and learning on the internet, I've run into problems with my graphql schema and resolvers. I can't seem to figure out what resolvers do I need and how go about it correctly. If someone can point me in the right direction, that would be great. I have attached my code below. All I'm trying to do is fetch elements of a list using the relay graphql server specification.
Graphql Schema
interface Node {
id: ID!
}
type Link implements Node {
id: ID!
title: String!
description: String!
}
type LinkConnection {
edges: [LinkEdge]
pageInfo: PageInfo!
}
type LinkEdge {
cursor: String!
node: Link
}
type Query {
links(after: String, before: String, first: Int, last: Int): LinkConnection
node(id: ID!): Node
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
Resolvers
const resolvers = {
Query,
Node: {
__resolveType(node, context, info){
if(node.title){
return 'Link'
}
return null
}
},
}
Query Resolver
const node = async (parent, args, {prisma}) => {
try{
console.log('hit')
const data = await prisma.link.findUnique({
where: {
id: Number(args.id)
}
})
console.log(data)
return data
} catch(err){
return err
}
}
export default {
node,
}
P.S. Im using Apollo Server and Prisma under the hood
Edit:
I solved this issue by realizing that the resolves are invoked in the same order the schema is nested. So by writing separate resolvers for each type and passing the information in parent argument, things worked.
I have the following model defined:
import db from "../connection.js";
import objection from "objection";
const { Model } = objection;
Model.knex(db);
class User extends Model {
static get tableName() {
return "users";
}
static get relationMappings() {
return {
emails: {
relation: Model.HasManyRelation,
modelClass: Email,
join: {
from: "users.id",
to: "emails.user_id",
}
}
}
}
}
class Email extends Model {
static get tableName() {
return "emails";
}
static get relationMappings() {
return {
user: {
relation: Model.BelongsToOneRelation,
modelClass: User,
join: {
from: "emails.user_id",
to: "users.id",
},
},
};
}
}
And to query a User with their email addresses would require withGraphFetched() to be explicitly run every time as:
const myUser = await User.query().withGraphFetched("emails").findById(1)
I haven't been able to figure out what to write in the Model definition to make this possible, and I don't see any such examples online. Is it possible to ALWAYS have withGraphFetched("emails") automatically included in the query so it doesn't have to be explicitly written out every time?
Such thing doesnt exist. Maybe you could create the logic in the beforeFind hook adding the eager loader to the knex instance, but it could generate a lot of undesired and strange side-effects.
The normal practice is adding a method to that specific case:
class User extends Model {
static get tableName() {
return "users";
}
static get relationMappings() {
return {
emails: {
relation: Model.HasManyRelation,
modelClass: Email,
join: {
from: "users.id",
to: "emails.user_id",
}
}
}
}
static async getUser (id) { // Or maybe getUserWithEmails
return await this.query().withGraphFetched("emails").findById(id)
}
}
Then you can just:
const myUser = await User.getUser(id)
Using the following code (which uses ES6's "type":"module" in package.json), I can't seem to access the related Model Group:
import db from "../connection.js";
import objection from "objection";
const { Model } = objection;
Model.knex(db);
class User extends Model {
static get tableName() {
return "users";
}
static get relationMappings() {
return {
groups: {
relation: Model.ManyToManyRelation,
modelClass: Group,
join: {
from: "users.id",
through: {
from: "users_groups.user_id",
to: "users_groups.group_id",
},
to: "groups.id",
}
}
}
}
}
class Group extends Model {
static get tableName() {
return "groups";
}
}
If I run
const myUser = await User.query().findById(1)
It outputs:
User {id: 1, name: "r", email: "raj#raj.raj", username: "raj", … }
But I still can't access the Group relation:
myUser.groups
Outputs:
undefined
What am I doing wrong?
You have to use eager loading in the query to load the desired relations.
It you are using Objection.js v1:
const myUser = await User.query().eager('groups').findById(1)
And since Objection.js v2, eager was renamed as withGraphFetched:
const myUser = await User.query().withGraphFetched('groups').findById(1)
Extra: Loading relations after instantiation
You can load the relations after instantiation using $relatedQuery. Note all instance methods starts with $:
const myUser = await User.query().findById(1)
const groupsOfMyUser = await myUser.$relatedQuery('groups')