Logging Prisma Client events in NestJs

|5 min read

This tutorial will explore how you can log Prisma Client events in your NestJs application.

Prisma Client provides two patterns in which you can log events in your app:

  • Logging to stdout (default)
  • Event-based logging

stdout === standard output

This guide will use the typescript/rest-nestjs starter project from the Prisma examples repository. Follow the instructions in the example README to get up and running with the example on your computer.

Logging to stdout

Logging events to the stdout is the easiest way you can start logging events to the console. To get started logging, make the following changes to your PrismaService:

  1. Define the constructor method
  2. Use the super keyword to define the log property with an empty array ([])
  3. Define the type of logs in the array — e.g., error, info, query, and warn
import {
  Injectable,
  OnModuleInit,
  INestApplication,
  Logger,
} from "@nestjs/common";
import { PrismaClient } from "@prisma/client";

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  constructor() {
    super({
      log: ["error", "info", "query", "warn"],
    });
  }

  async onModuleInit() {
    await this.$connect();
  }
}

When the app reloads, you should see some logs from Prisma Client printed on the console:

prisma:info Starting a sqlite pool with 9 connections.
[Nest] 34583  - 13/11/2022, 22:24:14     LOG [NestApplication] Nest application successfully started +6ms
[Nest] 75445  - 17/11/2022, 22:21:05     LOG [NestApplication] Nest application successfully started +81ms
prisma:query SELECT 1
prisma:query SELECT `main`.`Post`.`id`, `main`.`Post`.`createdAt`, `main`.`Post`.`updatedAt`, `main`.`Post`.`title`, `main`.`Post`.`content`, `main`.`Post`.`published`, `main`.`Post`.`viewCount`, `main`.`Post`.`authorId` FROM `main`.`Post` WHERE (`main`.`Post`.`id` = ? AND 1=1) LIMIT ? OFFSET ?

The prisma:query is output to the console after making a GET request to http://localhost:3000/post/1

The extended version of the above snippet is defining an array of objects. Each object will have emit and level properties for every type of event you would like to log. The emit property should have a value of stdout to log the events to the stdout:

import { Injectable, OnModuleInit, INestApplication } from "@nestjs/common";
import { Prisma, PrismaClient } from "@prisma/client";

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  constructor() {
    super({
      log: [
        {
          emit: "stdout",
          level: "query",
        },
        {
          emit: "stdout",
          level: "error",
        },
        {
          emit: "stdout",
          level: "info",
        },
        {
          emit: "stdout",
          level: "warn",
        },
      ],
    });
  }

  async onModuleInit() {
    await this.$connect();
  }
}

Event-based logging

Another logging pattern that Prisma Client supports is event-logging. This means that you can subscribe to different kind of events, and then have custom logic of how you would like to handle the event.

You can use the built in Logger provided by NestJs in your PrismaService to provide a consistent behavior and formatting across both Nest system logging. It’s considered a good practice to instantiate a new logger for each service in your Nest.

To get started subscribing to Prisma Client events, make the following changes:

  1. Define a Prisma Client generic that will accept two parameters:
    • Prisma.PrismaClientOptions: a type from Prisma that specifies the options available in Prisma Client’s constructor method
    • Prisma.LogLevel: another type from Prisma specifying the logs types (emit, and level)
  2. Instantiate a logger for the service with service name as the context argument in the constructor
  3. Define the logs, setting the emit property’s value as event. Prisma Client will subscribe to the events
  4. Use the $this.on() method to subscribe to the events defined in the constructor using the logger defined in 2

Feel free to adjust the log levels.

import {
  Injectable,
  OnModuleInit,
  INestApplication,
  Logger,
} from "@nestjs/common";
import { Prisma, PrismaClient } from "@prisma/client";

@Injectable()
// 1.
export class PrismaService
  extends PrismaClient<Prisma.PrismaClientOptions, Prisma.LogLevel>
  implements OnModuleInit
{
  //2.
  private readonly logger = new Logger(PrismaService.name);

  constructor() {
    super({
      // 3.
      log: [
        {
          emit: "event",
          level: "query",
        },
        {
          emit: "event",
          level: "error",
        },
        {
          emit: "event",
          level: "info",
        },
        {
          emit: "event",
          level: "warn",
        },
      ],
    });
  }

  async onModuleInit() {
    //4.
    this.$on("error", (event) => {
      this.logger.error(event);
    });
    this.$on("warn", (event) => {
      this.logger.warn(event);
    });
    this.$on("info", (event) => {
      this.logger.verbose(event);
    });
    this.$on("query", (event) => {
      this.logger.log(event);
    });
    await this.$connect();
  }
}

Once you’re done updating the service, you should be able to see the [PrismaService] context printed out to the console

[Nest] 77319  - 17/11/2022, 22:39:34     LOG [PrismaService] Object:
{
  "timestamp": "2022-11-17T21:39:34.431Z",
  "message": "Starting a sqlite pool with 9 connections.",
  "target": "quaint::pooled"
}

[Nest] 77319  - 17/11/2022, 22:39:34     LOG [NestApplication] Nest application successfully started +2ms

If you make a GET request to http://localhost:3000/post/1

[Nest] 77319  - 17/11/2022, 22:39:45     LOG [PrismaService] Object:
{
  "timestamp": "2022-11-17T21:39:45.597Z",
  "query": "SELECT `main`.`Post`.`id`, `main`.`Post`.`createdAt`, `main`.`Post`.`updatedAt`, `main`.`Post`.`title`, `main`.`Post`.`content`, `main`.`Post`.`published`, `main`.`Post`.`viewCount`, `main`.`Post`.`authorId` FROM `main`.`Post` WHERE (`main`.`Post`.`id` = ? AND 1=1) LIMIT ? OFFSET ?",
  "params": "[1,1,0]",
  "duration": 0,
  "target": "quaint::connector::metrics"
}

The logs now look a little prettier 🌈 and more consistent with the NestJs logging style.

To learn more about NestJs logging, take a look at their docs for an in-depth guide.

Refer to Prisma’s docs to learn more about logging in Prisma Client

If you run into any issues in the guide, feel free to tweet at me or message me on Twitter.