File export using Angular, GraphQL and NestJS




This is the 7th article of the NestJS tutorial series with GraphQL, PostGraphile, Angular, BullJS, and TypeORM with PostgreSQL. Please check previous articles,

  1. File uploading example using NestJS and Angular 11 - Part 1
  2. File uploading example using NestJS and Angular 11 - Part 2
  3. Use Bull with NestJS
  4. Introduction to PostGraphile
  5. GraphQL and PostGraphile with NestJS
  6. CRUD operation with GraphQL and NestJS

In this article, I will add a file export scenario using NestJS with GraphQL and complete the application. I am not going very deep level, I just want to explain how to use GraphQL and Bull with NestJS.


Install required libraries


I will use json-2-csv library in the file exporting process in the NestJS application.

npm install json-2-csv


Create a new Processor for the export function


Create a new file in the processor directory. Because I will use Bull queue operations to handle files. This is very similar to the file upload functions which I explained in the third article, Use Bull with NestJS.



import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
import { SocketService } from '../socket/socket.service';

@Processor('export-queue')
export class ExportProcessor {

constructor(private socketService: SocketService) {}

converter = require('json-2-csv');
fs = require('fs');
i = 0;

@Process('myFile')
exportCsv(job: Job) {

this.converter.json2csv(job.data.vehicleList, async (err, csv) => {
let fileName = 'vehicles_' + this.i;
if (err) {
throw err;
}
this.fs.writeFileSync(fileName.concat('.csv'), csv);
this.i++;
});
}
}


The file writing process will be handled here. 


Register a new queue to file export function


We need to modify the automobile.module.ts file and register a new queue, export-queue which I already mentioned in the ExportProcessor.


import { Module } from '@nestjs/common';
import { AutomobileController } from './automobile.controller';
import { AutomobileService } from './automobile.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Vehicle } from './vehicle';
import { BullModule } from '@nestjs/bull';
import { UploadProcessor } from './processors/upload.processor';
import { AutomobileResolver } from './automobile.resolver';
import { ExportProcessor } from './processors/export.processor';
import { SocketService } from './socket/socket.service';

@Module({
controllers: [AutomobileController],
providers: [AutomobileService, UploadProcessor, AutomobileResolver, ExportProcessor, SocketService],
imports: [
TypeOrmModule.forFeature([Vehicle]),
BullModule.forRoot({
redis: {
host: 'localhost',
port: 6379
},
}),
BullModule.registerQueue({
name: 'upload-queue'
}),
BullModule.registerQueue({
name: 'export-queue'
})]
})
export class AutomobileModule { }



Refactor the resolver 


I used TypeORM for the file uploading, but now I will use GraphQL with PostGraphile for the file exporting with Bull. I am going to export automobile data against the manufacture year of a vehicle. Let's create a query to retrieve data and insert data into the Bull queue.


@Query(() => [Vehicle])
async vehiclesByYear(@Args('year', { type: () => Int }) year: number) {
const query = gql`query MyQuery {
allVehicles(condition: {year: ${year}}) {
nodes {
id
make
model
year
engineNumber
}
}
}`

return request('http://localhost:5000/graphql', query).then((data) => {

const vehicleList = data.allVehicles.nodes;
this.fileQueue.add('myFile', { vehicleList: vehicleList });
return vehicleList;
})
}


As you can see the query is used to extract data from the PostgresDB using PostGraphile. Then extracted data will be added to the fileQueue with the reference of myFile. The ExportProcessor will handle these data to produce a CSV file.

The back-end part with NestJS is now completed, let's move with the front-end part.


Add export file link to the nav-bar


A new link can be added to the nav-bar component

          <li><a [routerLink]="'/export'" routerLinkActive="active">Export file</a></li>


Create a new component for file export


ng g c vehicle/export

This will create three files. I am going to modify them.


export.component.css

html, body, .container-table {
height: 100%;
}
.container-table {
display: table;
border: 1px rgb(226, 230, 236) solid;
padding: 20px;
}
.vertical-center-row {
display: table-cell;
vertical-align: middle;
}



export.component.html


<div class="container container-table">
<div class="row vertical-center-row">
<div class="text-center col-md-4 col-md-offset-4">
<form [formGroup]="form" (ngSubmit)="onExportClick()">
<label style="margin-right: 10px;">Enter vehicle year</label>
<p>
<input required [autofocus] type="text" name="year" id="year"
class="form-control"
formControlName="year">
</p>

<div *ngIf="f.year.invalid && (f.year.dirty || f.year.touched)"
class="alert alert-danger">
<div>Year of the vehicle is required.</div>
</div>

<button type="submit" class="btn btn-primary" [disabled]="!form.valid">
Export to CSV
</button>

</form>
</div>
</div>
</div>



export.component.ts


import { Component } from '@angular/core';
import { Apollo, gql, QueryRef } from 'apollo-angular';
import { FormGroup, FormControl, Validators} from '@angular/forms';

export const EXPORT_VEHICLES = gql`query($year: String!) {
vehiclesByYear(year:$year) {
id
make
model
year
engineNumber
}
}`

@Component({
selector: 'app-export',
templateUrl: './export.component.html',
styleUrls: ['./export.component.css']
})
export class ExportComponent {

year: any;
query!: QueryRef<any, any>;
status: any;

constructor(
private apollo: Apollo) {
}

form = new FormGroup({
year: new FormControl('', [Validators.required])
});

get f(){
return this.form.controls;
}

onExportClick() {

const year = this.form.get('year')?.value;

this.query = this.apollo.watchQuery({
query: EXPORT_VEHICLES, variables: { year }
});

this.query.valueChanges.subscribe((data) => {
if (data.error) {
alert("File export error !")
} else {
alert("File exported successfully !")
}
});

window.location.href = './export';
}
}


Add a route to the ExportComponent


Then you can modify the app-routing.module.ts file to add a router link to the ExportComponent


    {
    path: 'export',
    pathMatch: 'full',
    component: ExportComponent
    }



Now everything is completed. You can run the application. Please mind you to run the Redis server and PostGraphile too. 

It is better if we can use a separate socket service to get notifications, once the file export is completed. Here I use just an alert to notify users to make it simple. You can find the complete code from the below GIT repository. 







File export using Angular, GraphQL and NestJS File export using Angular, GraphQL and NestJS Reviewed by Ravi Yasas on 11:48 AM Rating: 5

No comments:

Powered by Blogger.