DEV Community

Cover image for Build an AI-Powered PPT Generator Using NestJS
Arun Kumar Dorepally
Arun Kumar Dorepally

Posted on

Build an AI-Powered PPT Generator Using NestJS

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
Enter fullscreen mode Exit fullscreen mode

Create a new NestJS project:

nest new ai-ppt-generator
Enter fullscreen mode Exit fullscreen mode

Move into the project directory:

cd ai-ppt-generator
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Dependencies

Install the required dependencies:

npm install axios pptxgenjs @nestjs/config
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

This will create:

src/mistral/
├── mistral.module.ts
├── mistral.service.ts
Enter fullscreen mode Exit fullscreen mode

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.");
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

This will create:

src/ppt-generator/
├── ppt-generator.module.ts
├── ppt-generator.controller.ts
├── ppt-generator.service.ts
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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,
      });
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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 {}
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Loads environment variables globally.
  • Registers PptGeneratorModule and MistralModule.
  • Configures the app to connect all modules.

5. Running the Project

Start the project:

npm run start:dev
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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)