I'm quite new to coding so apologies if there's a simple solution i'm glossing over but I've been trying for a while an do haven't been able to get it
import { Injectable } from '#nestjs/common';
import { MailerService } from '#nestjs-modules/mailer';
import { InjectRepository } from '#nestjs/typeorm';
import { ChecklistItemsEntity } from 'src/checklist-items/checklist-items.entity';
const mail = 'welcome'
#Injectable()
export class MailService {
constructor( public mailerService: MailerService) {}
public example(): void {
this
.mailerService
.sendMail({
to: 'ashwin.iyer01#gmail.com', // List of receivers email address
from: 'ashwin.iyer01#gmail.com', // Senders email address
subject: 'Testing Nest MailerModule ✔', // Subject line
text: 'welcome', // plaintext body
html: mail , // HTML body content
})
.then((success) => {
console.log(success)
})
.catch((err) => {
console.log(err)
});
}
This is a part of the application i'm building, its doing its job as intended and sending sample emails however i need the emails to include data from the MySQL database that the application is linked to instead of 'welcome'.
Specifically i need it to contain all the records of one table in the database, an entity(i'm using TypeORM).
How would i go about doing this?
You should be able to just have a typeorm repository added to the service, make the query to the data, serialize the data back into an HTML compliant format, and then add it to the html or text property. Or even better, you could inject the MailService into something like the UsertService that has a UserRepo already in it, and create a function that takes the already serialized information, then from the UserService call this.mailService.sendMail(data) and let everything happen from there.
Related
In Laravel (php) has route /article/:article, and in controller method I get the model:
function getArticle(ArticleModel $article) {...}
How to make this in NestJS?
My controller:
#Controller('/articles')
export class ArticlesController {
#Get('/:article/edit')
editArticle(#Param('article') articleId: number) {...}
}
How to transform #Param('article') to custom decorator #ArticleParam() which will return my Article entity by id in request?
You can implement a custom pipe that injects a TypeORM repository and returns the database entity when prompted with an ID, something like this:
#Injectable()
export class ArticlePipe implements PipeTransform {
constructor(#InjectRepository(Article) private repository: Repository<Article>) {}
transform(value: id, metadata: ArgumentsMetadata): Promise<Article|null> {
return this.repository.findOneBy({ id });
}
}
Then use it like
#Get('/article/:id')
getArticle(#Param('id', ArticlePipe) article: Article) { ... }
You just need to make sure to use the pipe only on modules that provide the Article EntityRepository.
Then, if you need the specific #ArticleParam, it should be like this:
export function ArticleParam = () => applyDecorators(
Param(ArticlePipe)
)
I have a nestjs project that is mostly in RESTful structure. Everything works fine, but my concern is that some of the routes check for the presence of some query parameters to fetch data.
for instance
#Get('/some-resources')
async getSomeResource(
#Query() query: any
): Promise<HTTPResponseDTO>{
const startDate = query.startDate ? DateTime.fromISO(query.startDate).startOf('day').toISO(): null;
const endDate = query.endDate ? DateTime.fromISO(query.endDate).endOf('day').toISO() : null;
.
.
.
const result = await this.someResourceService.findAll(startDate, endDate,...)
}
Now my question is, is there a cleaner approach to this? Because this can get become a pain to maintain when we have many resources.
As mentioned by Micael Levi, you should be able to do this by creating your own custom pipe. Assuming that what you posted works, you should be able to do something along the lines of:
#Get('/some-resources')
async getSomeResource(
#Query('startDate', ParseDateIsoPipe) startDate?: string,
#Query('endDate', ParseDateIsoPipe) endDate?: string
): Promise<HTTPResponseDTO>{
<code>
}
With your ParseDateIsoPipe as follows (Note that you will still need to import DateTime from the package you are using):
import { PipeTransform, Injectable, ArgumentMetadata } from '#nestjs/common';
#Injectable()
export class ParseDateIsoPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
return value ? DateTime.fromISO(value).startOf('day').toISO(): null;
}
}
You can use the built-in validation pipe: https://docs.nestjs.com/techniques/validation with the auto validation feature.
I am new to nestjs and tring to apply Get and Post simultaneously on a method in my controller.
For simplicity, I just post the core logic code snippet:
Customized Decorator
import { Get, Post } from "#nestjs/common";
import { RenderReact } from 'my-personal-package';
export function Page(path: string, view?: React.ComponentType, methodDecorators?: ((path?: string | string[]) => MethodDecorator)[]): MethodDecorator {
return (target: any, key: string, desc: PropertyDescriptor) => {
const decorators = [
Get(path), // Add Get first.
Post(path) // Add Post then.
];
if (view) {
decorators.push(RenderReact(view)); // RenderReact will return a MethodDecorator as well.
}
decorators.forEach(decorate => decorate(target, key, desc));
return desc;
};
}
Controller method:
#Page("my-path", ThisIsMyPageFunctionalComponent, [Post]) // Post was from #nestjs/common
async return() {
// method logic
}
The array "decorators" at the very beginning in the Page function,
Add Get, then Post, Only Post works.
Add Post, then Get, Only Get works.
How can we apply Get/Post simultaneously here?
As #Micael Levi mentioned above, as the machanism of how decorator factory works, we can not apply both Get and Post in this way. I have tried it for a long time.
Please refer to the question here, like #Kim Kern posted
We extracted common logic into a method.
Separate the method Get and Post which will call the common logic.
I have a class CreateFolderDto with two readonly fields:
export class CreateFolderDto {
public readonly name: string
public readonly user_id: number
}
I have a controller which is:
#UseGuards(AuthGuard('jwt'))
#Post()
public create(#Request() req, #Body() createFolderDto: CreateFolderDto) {
return this.folderService.create(createFolderDto)
}
The request send to my controller is a good one, I only send the name in json format with an accessToken in the header. The accessToken permit me to get my user_id from the request with req.user.id.
The DTO field user_id is not automatically filled. I would like to fill it automatically.
Is it a way to auto-fill my createFolderDto.user_id variable ?
#Body only wraps actual request body into instance of the CreateFolderDto class. As the body which comes to your endpoint has no such a field, you need to add it manually.
Normally, aggregated fields could be added with custom constructor of your DTO:
export class CreateFolderDto {
public readonly name: string
public readonly session_uuid: string
constructor(bodyValue: any = {}) {
this.name = bodyValue.name
this.session_uuid = generateUuid()
}
}
But in your case, user is attached to request itself, so I believe you have the following options:
Check out your code which attaches the user to request itself. If you are using JWT Auth described in NestJS docs, you cannot do this that way.
You can write custom Interceptor:
Injectable()
export class ExtendBodyWithUserId implements NestInterceptor {
async intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest()
request.body.user_id = request.user
return next.handle()
}
}
// usage
#UseGuards(AuthGuard('jwt'))
#UseInterceptors(ExtendBodyWithUserId)
#Post()
public create(#Request() req, #Body() createFolderDto: CreateFolderDto) {
return this.folderService.create(createFolderDto)
}
Last but not least, some personal recommendation. Consider how much you will use this interceptor as an extension, as too many of 'extras' like this bloat the codebase.
I would recommend to change the folderService signature to:
create(createFolderDto: CreateFolderDto, user: User), where folder dto has only the name, without user-related entry. You keep the consistency, separation and clear intentions. In the implementation of create you can just pass user.id further.
And going this way, you don't have to write custom interceptors.
Pick your way and may the consistency in your codebase be with you!
I want to design a TypeScript (2.7) module for accessing external IS, let's call it InfoSys. I used the following approach.
I created info-sys.ts which defines a API class and related interfaces and enums, like:
class Api {
constructor(private endpoint: Endpoint) {
// ...
}
}
enum Endpoint {
CONTACTS = "contacts"
}
interface Contact {
name: string;
}
Now I want to export all the stuff under specific name. So I appended the export statement:
export const InfoSys = {
Api,
Endpoint,
Contact
};
When I try to use the module in another file, like:
import { InfoSys } from "info-sys";
// this line throws error: "Cannot find namespace 'InfoSys'"
private api: InfoSys.Api;
// but this line is ok
api = new InfoSys.Api(InfoSys.Endpoint.CONTACTS);
The way that works is the following - to export every piece individually:
export class Api {
constructor(private endpoint: Endpoint) {
// ...
}
}
export enum Endpoint {
CONTACTS = "contacts"
}
export interface Contact {
name: string;
}
and import them all to a single variable:
import * as InfoSys from "info-sys";
But the name of the variable can be whatever. It is not critical for functionality but I want to force developers, who will use the info-sys module, to use a specific name while accessing it (for easier readability and maintainability). How to properly design such module?
You can use namespace:
export namespace InfoSys {
Api,
Endpoint,
Contact
};
In general, this approach should be avoided. But in your case, it is fine as you are delivering things that are tightly related.
If Api is the single entry point to all these, I would also recommend this:
export class InfoSysApi { ... }
export namespace InfoSysApi {
export enum Endpoint = { ... }
export interface Contact { ... }
}
UPDATE:
To make sure I get the point through, DON'T do the following:
export namespace Foo {
export function X() { return 'x' }
export function Y() { return 'y' }
}
Only use export namespace to export "tugged in types", not values.
In TypeScript handbook: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
Although the table says namespace can contain values, it is considered bad practice if you are writing ESM (import/export).
Namespace and ESM are two different mechanisms to achieve similar result.
Don't mix them together.