DEV Community

Cover image for Analyzing API Call Trends with Prisma and Next.js: Grouping by Week, Month, or Year
Muhammed Vengalath
Muhammed Vengalath

Posted on

Analyzing API Call Trends with Prisma and Next.js: Grouping by Week, Month, or Year

Hey, this post is exactly what I needed when I was trying to figure out how to use Prisma to group data by day, month, or year. I had to go through various documentation to understand how to implement this. So let's get started. We'll explore how to query by grouping data based on the DateTime type. We'll use an example to analyze an API call trend using Prisma, Next.js, and MongoDB. Our focus will be on querying data to track API call metrics such as success rates and call frequency over time.

Simplified API Call Data Model

Grouping API calls by time periods such as week, month, or year is crucial for implementing dashboard. To illustrate this, consider the following minimal Prisma schema:

model ApiCall {
  id        String    @id @default(auto()) @map("_id") @db.ObjectId
  timestamp DateTime  @default(now())
  status    ApiCallStatus // Enum for success or failure.
}

enum ApiCallStatus {
  SUCCESS
  FAILURE
}
Enter fullscreen mode Exit fullscreen mode

This model captures the timestamp of each API call and its status, sufficient for trend analysis.

Querying API Call Trends

Here’s an implementation of an API endpoint in Next.js that provides insights into API call trends by grouping the data by time periods such as week, month, or year. This endpoint helps monitor API usage patterns and identify potential system issues efficiently:

import { NextRequest, NextResponse } from 'next/server';
import { startOfYear, endOfYear, startOfMonth, endOfMonth } from 'date-fns';

export async function GET(req: NextRequest) {
    const range = req.nextUrl.searchParams.get("range"); // 'year' or 'month'
    const groupBy = req.nextUrl.searchParams.get("groupby"); // 'yearly', 'monthly', 'daily'

    if (!range || (range !== 'year' && range !== 'month')) {
        return NextResponse.json({ error: "Range must be 'year' or 'month'" }, { status: 400 });
    }

    if (!groupBy || (groupBy !== 'yearly' && groupBy !== 'monthly' && groupBy !== 'daily')) {
        return NextResponse.json({ error: "Group by must be 'yearly', 'monthly', or 'daily'" }, { status: 400 });
    }

    try {
        let start: Date, end: Date;
        if (range === 'year') {
            start = startOfYear(new Date());
            end = endOfYear(new Date());
        } else { // range === 'month'
            start = startOfMonth(new Date());
            end = endOfMonth(new Date());
        }

        let groupByFormat: string;
        switch (groupBy) {
            case 'yearly':
                groupByFormat = "%Y";
                break;
            case 'monthly':
                groupByFormat = "%Y-%m";
                break;
            case 'daily':
                groupByFormat = "%Y-%m-%d";
                break;
        }

        const apiCallTrends = await db.apiCall.aggregateRaw({
            pipeline: [
                {
                    $match: {
                        timestamp: { $gte: { $date: start }, $lte: { $date: end } }
                    }
                },
                {
                    $group: {
                        _id: { $dateToString: { format: groupByFormat, date: '$timestamp' } },
                        SUCCESS: { $sum: { $cond: [{ $eq: ['$status', 'SUCCESS'] }, 1, 0] } },
                        FAILURE: { $sum: { $cond: [{ $eq: ['$status', 'FAILURE'] }, 1, 0] } },
                        TOTAL: { $sum: 1 }
                    }
                },
                {
                    $sort: {
                        _id: 1
                    }
                }
            ]
        });

        return NextResponse.json({ apiCallTrends });
    } catch (error) {
        console.error(error);
        return NextResponse.json({ error: "An error occurred while fetching data." }, { status: 500 });
    }
}
Enter fullscreen mode Exit fullscreen mode

Possible Response

GET /api/your-endpoint?range=year&groupby=monthly
Enter fullscreen mode Exit fullscreen mode
{
  "apiCallTrends": [
    {
      "_id": "2025-01", // Grouped by month (January 2025)
      "SUCCESS": 120,
      "FAILURE": 15,
      "TOTAL": 135
    },
    {
      "_id": "2025-02", // Grouped by month (February 2025)
      "SUCCESS": 110,
      "FAILURE": 10,
      "TOTAL": 120
    },
    {
      "_id": "2025-03", // Grouped by month (March 2025)
      "SUCCESS": 130,
      "FAILURE": 20,
      "TOTAL": 150
    }
    // ... more grouped results for each month in the range
  ]
}
Enter fullscreen mode Exit fullscreen mode

Key Highlights

  1. Dynamic Grouping by Date: The aggregation pipeline groups API calls by year, month or day, depending on the user’s selection.
  2. Trend Analysis: Success and failure counts, along with total calls, are calculated for each time period.
  3. Error Handling: User-friendly error responses ensure a smooth API experience.
  4. Efficiency: MongoDB's aggregation pipeline minimizes server load and optimizes performance.

Conclusion

With this setup ,you can query timestamp groupBy various time range from MongoDB using Prisma ORM.

Thank you for reading! If you found this post helpful, please consider subscribing and liking the post. Feel free to connect with me on GitHub and LinkedIn. Your support is greatly appreciated!

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs