In this tutorial, I'll walk you through creating an AI-powered PowerPoint generator using NestJS. The goal is to:
- Use Mistral AI to generate slide content based on a topic and slide count.
- Convert the generated content into a PowerPoint presentation using
pptxgenjs
. - Structure the project using NestJS best practices.
We'll focus on four key files:
-
mistral.service.ts
– Handles AI interaction. -
ppt-generator.controller.ts
– Handles incoming HTTP requests. -
ppt-generator.service.ts
– Creates PowerPoint files. -
app.module.ts
– Configures the app and connects modules.
1. Setting Up the Project
Step 1: Create a New NestJS Project
Install the NestJS CLI globally if you haven't already:
npm install -g @nestjs/cli
Create a new NestJS project:
nest new ai-ppt-generator
Move into the project directory:
cd ai-ppt-generator
Step 2: Install Dependencies
Install the required dependencies:
npm install axios pptxgenjs @nestjs/config
- axios – For making HTTP requests to Mistral AI.
- pptxgenjs – For generating PowerPoint files.
- @nestjs/config – For loading environment variables.
Step 3: Set Up Environment Variables
Create a .env
file in the root directory:
MISTRAL_BASE_URL=<Mistral AI API URL>
MISTRAL_API_KEY=<Mistral API Key>
Ensure .env
is loaded by installing @nestjs/config
.
2. Create the Mistral Module
We’ll create a dedicated module to handle AI-based content generation.
Create the Mistral Service
Create a mistral
module and service:
nest generate module mistral
nest generate service mistral
This will create:
src/mistral/
├── mistral.module.ts
├── mistral.service.ts
Edit mistral.service.ts
Open mistral.service.ts
and add the following logic:
import { Injectable } from "@nestjs/common";
import axios from "axios";
@Injectable()
export class MistralService {
private readonly apiUrl = process.env.MISTRAL_BASE_URL;
private readonly apiKey = process.env.MISTRAL_API_KEY;
async generateSlideContent(
topic: string,
slideCount: number
): Promise<string[]> {
const prompt = `Generate ${slideCount} slides about "${topic}". Each slide should have a title(preferably short title) and bullet points.`;
try {
const response = await axios.post(
`${this.apiUrl}/v1/chat/completions`,
{
model: "mistral-small", // Options: 'mistral-small', 'mistral-large', 'codestral'
messages: [{ role: "user", content: prompt }],
max_tokens: 800,
},
{
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
}
);
const content = response.data.choices[0].message.content;
return content.split("\n"); // Split slides by paragraph
} catch (error) {
console.error("Error generating slide content:", error);
throw new Error("Failed to generate slide content.");
}
}
}
Explanation
- The
generateSlideContent()
method takes a topic and slide count. - It constructs a structured prompt.
- Uses
axios
to send a POST request to the Mistral AI API. - Returns an array of slides from the API response.
3. Create the PPT Generator Module
Next, we'll create the module to handle PowerPoint file creation.
Create the PPT Generator Module & Service
Create the module and services:
nest generate module ppt-generator
nest generate controller ppt-generator
nest generate service ppt-generator
This will create:
src/ppt-generator/
├── ppt-generator.module.ts
├── ppt-generator.controller.ts
├── ppt-generator.service.ts
Edit ppt-generator.service.ts
Open ppt-generator.service.ts
and add the following logic:
import { Injectable } from "@nestjs/common";
const PptxGenJS = require("pptxgenjs");
@Injectable()
export class PptGeneratorService {
async generatePpt(slideData: string[]): Promise<Buffer> {
const pptx = new PptxGenJS();
if (slideData.length === 0) {
slideData = ["No content generated"];
}
// Group slide content
const groupedSlides: string[][] = [];
let currentSlide: string[] = [];
slideData.forEach((line) => {
if (line.startsWith("Slide")) {
if (currentSlide.length > 0) {
groupedSlides.push(currentSlide);
}
currentSlide = [line];
} else {
currentSlide.push(line);
}
});
if (currentSlide.length > 0) {
groupedSlides.push(currentSlide);
}
// Create slides based on grouped data
groupedSlides.forEach((slideContent) => {
const slide = pptx.addSlide();
// Add background
slide.background = { color: "FFFFFF" };
// Add title
slide.addText(slideContent[0], {
x: "5%",
y: "5%",
w: "90%",
fontSize: 28,
color: "0000FF",
bold: true,
align: "center",
});
// Add bullet points
const bulletPoints = slideContent
.slice(1)
.map((point) => `${point}`)
.join("\n");
// Handle overflow and dynamic font size
const fontSize = bulletPoints.length > 500 ? 20 : 24;
const maxLength = 800;
if (bulletPoints.length > maxLength) {
const parts = bulletPoints.match(/(.|[\r\n]){1,800}/g) || [];
parts.forEach((part, i) => {
const subSlide = pptx.addSlide();
subSlide.addText(`${slideContent[0]} (Part ${i + 1})`, {
x: "5%",
y: "5%",
w: "90%",
fontSize: 28,
color: "0000FF",
bold: true,
align: "center",
});
subSlide.addText(part, {
x: "5%",
y: "20%",
w: "90%",
h: "65%",
fontSize: fontSize - 2,
color: "000000",
align: "left",
valign: "top",
});
});
} else {
slide.addText(bulletPoints, {
x: "5%",
y: "20%",
w: "90%",
h: "65%",
fontSize,
color: "000000",
align: "left",
valign: "top",
});
}
});
const data = (await pptx.write({ outputType: "uint8array" })) as Uint8Array;
return Buffer.from(data);
}
}
Explanation
- Groups slide content into structured slides.
- Creates slides using
pptxgenjs
. - Writes the presentation to a buffer and returns it.
Edit ppt-generator.controller.ts
Open ppt-generator.controller.ts
and set up the endpoint:
import { Body, Controller, HttpStatus, Post, Res } from "@nestjs/common";
import { MistralService } from "src/mistral/mistral.service";
import { PptGeneratorService } from "./ppt-generator.service";
@Controller("ppt")
export class PptGeneratorController {
constructor(
private readonly mistralService: MistralService,
private readonly pptGeneratorService: PptGeneratorService
) {}
@Post("generate")
async generatePpt(
@Body() body: { topic: string; slideCount: number },
@Res() res
) {
try {
const { topic, slideCount } = body;
if (!topic || !slideCount || slideCount <= 0) {
return res.status(HttpStatus.BAD_REQUEST).json({
message:
"Invalid input. Please provide a valid topic and slide count.",
});
}
// Generate slide content using MistralAI
const slideContent = await this.mistralService.generateSlideContent(
topic,
slideCount
);
// Split slides properly (trim to avoid empty strings)
const slides = slideContent
.map((slide) => slide.trim())
.filter((slide) => slide.length > 0);
if (slides.length === 0) {
return res.status(HttpStatus.NO_CONTENT).json({
message: "AI generated no valid content.",
});
}
console.log(slides);
// Generate PowerPoint file using updated pptx-genjs
const pptBuffer = await this.pptGeneratorService.generatePpt(slides);
// Send the file as a downloadable response
res.set({
"Content-Type":
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"Content-Disposition": `attachment; filename="generatedPPT.pptx"`,
});
return res.status(HttpStatus.OK).send(pptBuffer);
} catch (error) {
console.error("Error generating PowerPoint:", error);
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
message: "Failed to generate PowerPoint file.",
error: error.message,
});
}
}
}
Explanation
- Accepts a POST request at
/ppt/generate
. - Calls
generateSlideContent()
to get AI-generated slides. - Passes content to
PptGeneratorService
to create a PPT file. - Sends the file as a response.
4. Connect Everything in app.module.ts
Open app.module.ts
and register the modules:
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { PptGeneratorModule } from "./ppt-generator/ppt-generator.module";
import { MistralModule } from "./mistral/mistral.module";
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true, envFilePath: [".env"] }),
PptGeneratorModule,
MistralModule,
],
})
export class AppModule {}
Explanation
- Loads environment variables globally.
- Registers
PptGeneratorModule
andMistralModule
. - Configures the app to connect all modules.
5. Running the Project
Start the project:
npm run start:dev
Test the Endpoint
Use a REST client like Postman to test the endpoint:
-
POST
http://localhost:3000/ppt/generate
- Body:
{
"topic": "Artificial Intelligence",
"slideCount": 5
}
6. Final Thoughts
Congratulations! You’ve built a fully functional AI-powered PPT generator using NestJS.
How It Works:
- Mistral AI – Generates high-quality slide content using natural language processing.
- PptxGenJS – Creates well-formatted PowerPoint presentations.
- NestJS – Efficiently manages request flow and business logic.
Explore More
To explore the project further and deepen your understanding of the underlying technologies, check out these resources:
- GitHub Repository: AI-PPT Generator – Complete source code and setup instructions.
- Mistral AI Documentation: Mistral Completion Capabilities – Learn more about the API used for generating slide content.
- PptxGenJS Documentation: PptxGenJS – Detailed documentation on the library used to create PowerPoint slides.
- NestJS Documentation: NestJS – Official documentation to understand the framework’s core concepts and best practices.
Top comments (0)