Skip to main content
Prisma Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

Datadog

In this guide, you'll learn how to set up Datadog tracing for a new Prisma project. By combining the @prisma/instrumentation package with Prisma Client extensions, you can capture detailed spans for every database query. These spans are enriched with query metadata and sent to Datadog using dd-trace, Datadog's official APM library for Node.js, so you can see how each query performs inside your application's requests.

  • Spans are the individual operations or units of work within a distributed system or complex application. Each database query, service call, or external request is represented by a span.
  • Tracing ties these spans together to form a complete, end-to-end picture of a request's lifecycle. With tracing, you can visualize bottlenecks, identify problematic queries, and pinpoint where errors occur from your queries.

Datadog provides application performance monitoring (APM), metrics, logs, and dashboards to help you observe and debug production systems.

Prisma ORM generates the SQL for you, which can hide how each query performs unless you instrument it. By integrating Datadog with Prisma using @prisma/instrumentation and dd-trace, you can automatically capture spans for every database query.

This enables you to:

  • Measure latency per query.
  • Inspect query arguments and raw SQL.
  • Trace Prisma operations in the context of application-level requests.
  • Identify bottlenecks related to database access.

With this in place, slow queries and errors show up in Datadog as they happen.

Before you begin, ensure you have the following:

  • Node.js installed (v18+ recommended).
  • A local or hosted PostgreSQL database.
  • A Datadog account. If you do not have one, sign up here.
  • The Datadog Agent installed and running on your machine or server where this application will run. You can follow the Datadog Agent installation docs to set it up.

We will start by creating a new Node.js project to demonstrate tracing with Datadog and Prisma ORM. This will be a minimal, standalone setup focused on running and tracing Prisma queries, to understand the instrumentation flow in isolation.

If you're integrating tracing into an existing Prisma project, you can skip this step and directly follow from the setup tracing section. Apply the changes in your project's equivalent folder structure.

title="bun"
mkdir prisma-datadog-tracing

cd prisma-datadog-tracing

bun init
pnpm
mkdir prisma-datadog-tracing
cd prisma-datadog-tracing
pnpm init
yarn
mkdir prisma-datadog-tracing
cd prisma-datadog-tracing
yarn init
npm
mkdir prisma-datadog-tracing
cd prisma-datadog-tracing
npm init

In this setup, you'll:

  • Define a Prisma schema with basic models.
  • Connect to a Postgres database (Prisma Postgres or your own).
  • Configure Datadog tracing for all queries using @prisma/instrumentation and dd-trace.
  • Run a sample script that executes Prisma operations and sends spans to Datadog.

In this section, you will install Prisma, create your schema, and generate the Prisma Client. This prepares your application to run the database queries that you will trace with Datadog.

Run the following commands to install Prisma and a minimal TypeScript runner:

title="bun"
bun add --dev prisma@prev tsx
pnpm
pnpm add -D prisma@prev tsx
yarn
yarn add --dev prisma@prev tsx
npm
npm install -D prisma@prev tsx

Then initialize Prisma:

bunx --bun prisma init --output ../src/generated/prisma
Bash
pnpm prisma init --output ../src/generated/prisma
Bash
yarn prisma init --output ../src/generated/prisma
Bash
npx prisma init --output ../src/generated/prisma

[!NOTE] prisma init creates the Prisma scaffolding and a local DATABASE_URL. In the next step, create a Prisma Postgres database and replace that value with a direct postgres://... connection string.

This command does the following:

  • Creates a prisma directory with a schema.prisma file.
  • Generates the Prisma Client in the /src/generated/prisma directory (as specified in the --output flag).
  • Creates a .env file at the project root with your database connection string (DATABASE_URL).

The .env file should contain a standard connection string:

.env
# Placeholder url you have to replace
DATABASE_URL="postgresql://janedoe:mypassword@localhost:5432/mydb?schema=sample"

Install the driver adapter for PostgreSQL:

This command does the following:

  • Creates a prisma directory with a schema.prisma file.
  • Generates the Prisma Client in the /src/generated/prisma directory (as specified in the --output flag).
  • Creates a .env file at the project root with your database connection string (DATABASE_URL).

The .env file should contain a standard connection string:

title=".env"
# Placeholder url you have to replace

DATABASE_URL="postgresql://janedoe:mypassword@localhost:5432/mydb?schema=sample"

Install the driver adapter for PostgreSQL:

title="bun"
bun add @prisma/adapter-pg pg
pnpm
pnpm add @prisma/adapter-pg pg
yarn
yarn add @prisma/adapter-pg pg
npm
npm i @prisma/adapter-pg pg
bun add --dev @types/pg
Bash
pnpm add -D @types/pg
Bash
yarn add --dev @types/pg
Bash
npm i -D @types/pg

[!NOTE] If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of @prisma/adapter-pg. For more information, see Database drivers.

Create a Prisma Postgres database and replace the generated DATABASE_URL in your .env file with the postgres://... connection string from the CLI output:

Create a Prisma Postgres database and replace the generated DATABASE_URL in your .env file with the postgres://... connection string from the CLI output:

title="bun"
bunx create-db
pnpm
pnpm dlx create-db
yarn
yarn dlx create-db
npm
npx create-db

Now, open prisma/schema.prisma and update your generator block and models. Replace the generator block with the following, and add a User and a Post model:

title="prisma/schema.prisma"
generator client {

  provider = "prisma-client"

  output = "../src/generated/prisma"

}

datasource db {

  provider = "postgresql"

}

model User { 

  id    Int     @id @default(autoincrement()) 

  email String  @unique

  name  String?

  posts Post[]

} 

model Post { 

  id        Int     @id @default(autoincrement()) 

  title     String

  content   String?

  published Boolean @default(false) 

  authorId  Int

  author    User    @relation(fields: [authorId], references: [id]) 

} 

Create a prisma.config.ts file to configure Prisma:

title="prisma.config.ts"
import "dotenv/config"; 

import { defineConfig, env } from "prisma/config"; 

export default defineConfig({

  schema: "prisma/schema.prisma", 

  migrations: {

    path: "prisma/migrations", 

  }, 

  datasource: {

    url: env("DATABASE_URL"), 

  }, 

}); 

Generate the Prisma Client and apply your schema to your database:

bunx prisma generate
Bash
pnpm prisma migrate dev --name "init"
Bash
yarn prisma migrate dev --name "init"
Bash
npx prisma migrate dev --name "init"

This creates the tables according to your schema in the Postgres database and generates a client for you to interact with the database.

bunx prisma migrate dev --name "init"
Bash
pnpm add @prisma/instrumentation \
  dd-trace
Bash
yarn add @prisma/instrumentation \
  dd-trace
Bash
npm install @prisma/instrumentation \
  dd-trace

Also ensure you have development dependencies for TypeScript:

This creates the tables according to your schema in the Postgres database and generates a client for you to interact with the database.

In addition to Prisma, you will need the following packages for Datadog tracing:

bun add @prisma/instrumentation \

  dd-trace
Bash
pnpm add -D typescript
Bash
yarn add --dev typescript
Bash
npm install -D typescript

Here's a quick overview:

  • @prisma/instrumentation: Instruments Prisma queries so they appear as spans in your tracer.
  • dd-trace: Official Node.js tracing library from Datadog.

Also ensure you have development dependencies for TypeScript:

bun add --dev typescript
Bash
pnpm dlx tsx src/index.ts
Bash
yarn dlx tsx src/index.ts
Bash
npx tsx src/index.ts

This executes your script, which:

  • Registers the Datadog tracer.
  • Performs multiple Prisma queries.
  • Logs the result of each operation.

Then, confirm the traces in Datadog:

  • Open your Datadog APM page.
  • Navigate to APM > Traces > Explorer in the side panel.
  • Explore the list of traces and spans, each representing a Prisma query (e.g. prisma:query).

[!NOTE] Depending on your Datadog setup, it may take a minute or two for new data to appear. Refresh or wait briefly if you do not see traces right away.

Here's a quick overview:

  • @prisma/instrumentation: Instruments Prisma queries so they appear as spans in your tracer.
  • dd-trace: Official Node.js tracing library from Datadog.

Create a tracer.ts file in the src folder to instantiate your tracing logic:

touch src/tracer.ts

Open src/tracer.ts and add the following code:

title="src/tracer.ts"
import tracer from "dd-trace";

tracer.init({

  profiling: true,

  logInjection: true,

  runtimeMetrics: true,

  dbmPropagationMode: "full",

  env: "dev",

  sampleRate: 1,

  service: "prisma-datadog-tracing",

  version: "1.0.0",

});

export { tracer };
  • tracer.init configures dd-trace with a service name. This name appears in Datadog under your APM > Services list.
  • @prisma/instrumentation automatically logs each Prisma query as a Datadog span.

Create a src/client.ts to hold your Prisma Client instantiation:

title="src/client.ts"
import { tracer } from "./tracer";

import { PrismaClient } from "./generated/prisma/client";

import { PrismaPg } from "@prisma/adapter-pg";

const adapter = new PrismaPg({

  connectionString: process.env.DATABASE_URL!,

});

const prisma = new PrismaClient({

  adapter,

  log: [{ emit: "event", level: "query" }],

})

  .$on("query", (e) => {

    const span = tracer.startSpan(`prisma_raw_query`, {

      childOf: tracer.scope().active() || undefined,

      tags: {

        "prisma.rawquery": e.query,

      },

    });

    span.finish();

  })

  .$extends({

    query: {

      async $allOperations({ operation, model, args, query }) {

        const span = tracer.startSpan(`prisma_query_${model?.toLowerCase()}_${operation}`, {

          tags: {

            "prisma.operation": operation,

            "prisma.model": model,

            "prisma.args": JSON.stringify(args),

            "prisma.rawQuery": query,

          },

          childOf: tracer.scope().active() || undefined,

        });

        try {

          const result = await query(args);

          span.finish();

          return result;

        } catch (error) {

          span.setTag("error", error);

          span.finish();

          throw error;

        }

      },

    },

  });

export { prisma };

The setup above gives you more control over how queries are traced:

  • Tracing is initialized as early as possible by importing the tracer before creating the Prisma Client.
  • The $on("query") hook captures raw SQL queries and sends them as standalone spans.
  • The $allOperations extension wraps all Prisma operations in custom spans, allowing you to tag them with metadata like the model, operation type, and arguments.

Unlike the @prisma/instrumentation package, which offers automatic tracing out of the box, this manual setup gives you full control over how each span is structured and tagged. It's helpful when you need custom span names, additional metadata, a simpler setup, or when working around limitations or compatibility issues in the OpenTelemetry ecosystem. It also allows you to adapt tracing behavior based on query context, which can be especially useful in complex applications.

Create a src/index.ts file and add code to perform queries to your database and send traces to Datadog:

title="src/index.ts"
import { tracer } from "./tracer";

import { PrismaInstrumentation, registerInstrumentations } from "@prisma/instrumentation";

import { prisma } from "./client";

const provider = new tracer.TracerProvider();

registerInstrumentations({

  instrumentations: [new PrismaInstrumentation()],

  tracerProvider: provider,

});

provider.register();

async function main() {

  const user1Email = `alice${Date.now()}@prisma.io`;

  const user2Email = `bob${Date.now()}@prisma.io`;

  let alice, bob;

  // 1. Create users concurrently

  try {

    [alice, bob] = await Promise.all([

      prisma.user.create({

        data: {

          email: user1Email,

          name: "Alice",

          posts: {

            create: {

              title: "Join the Prisma community on Discord",

              content: "https://pris.ly/discord",

              published: true,

            },

          },

        },

        include: { posts: true },

      }),

      prisma.user.create({

        data: {

          email: user2Email,

          name: "Bob",

          posts: {

            create: [

              {

                title: "Check out Prisma on YouTube",

                content: "https://pris.ly/youtube",

                published: true,

              },

              {

                title: "Follow Prisma on Twitter",

                content: "https://twitter.com/prisma/",

                published: false,

              },

            ],

          },

        },

        include: { posts: true },

      }),

    ]);

    console.log(

      `✅ Created users: ${alice.name} (${alice.posts.length} post) and ${bob.name} (${bob.posts.length} posts)`,

    );

  } catch (err) {

    console.error("❌ Error creating users:", err);

    return;

  }

  // 2. Fetch all published posts

  try {

    const publishedPosts = await prisma.post.findMany({

      where: { published: true },

    });

    console.log(`✅ Retrieved ${publishedPosts.length} published post(s).`);

  } catch (err) {

    console.error("❌ Error fetching published posts:", err);

  }

  // 3. Create & publish a post for Alice

  let post;

  try {

    post = await prisma.post.create({

      data: {

        title: "Join the Prisma Discord community",

        content: "https://pris.ly/discord",

        published: false,

        author: { connect: { email: user1Email } },

      },

    });

    console.log(`✅ Created draft post for Alice (ID: ${post.id})`);

  } catch (err) {

    console.error("❌ Error creating draft post for Alice:", err);

    return;

  }

  try {

    post = await prisma.post.update({

      where: { id: post.id },

      data: { published: true },

    });

    console.log("✅ Published Alice’s post:", post);

  } catch (err) {

    console.error("❌ Error publishing Alice's post:", err);

  }

  // 4. Fetch all posts by Alice

  try {

    const alicePosts = await prisma.post.findMany({

      where: { author: { email: user1Email } },

    });

    console.log(`✅ Retrieved ${alicePosts.length} post(s) by Alice.`, alicePosts);

  } catch (err) {

    console.error("❌ Error fetching Alice's posts:", err);

  }

}

// Entrypoint

main()

  .catch((err) => {

    console.error("❌ Unexpected error:", err);

    process.exit(1);

  })

  .finally(async () => {

    await prisma.$disconnect();

    console.log("🔌 Disconnected from database.");

  });

Run the queries:

bunx tsx src/index.ts

This executes your script, which:

  • Registers the Datadog tracer.
  • Performs multiple Prisma queries.
  • Logs the result of each operation.

Then, confirm the traces in Datadog:

  • Open your Datadog APM page.
  • Navigate to APM > Traces > Explorer in the side panel.
  • Explore the list of traces and spans, each representing a Prisma query (e.g. prisma:query).

You have successfully:

  • Created a Prisma ORM project with Prisma Postgres.
  • Set up Datadog tracing using @prisma/instrumentation and dd-trace.
  • Verified that database operations show up as spans in Datadog.

To improve your observability further:

  • Add more instrumentation for your HTTP server or other services (e.g., Express, Fastify).
  • Create Dashboards to view key metrics from your data.

For additional guidance, check out:

Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu