I am using Next.js with MongoDB, Currently I am using the MongoDB client itself in TypeScript. I was wondering if later on I have to change my database, I need to customize the api/route.ts file.
Instead of directly customizing the route.ts file can we inject some kind of dependency that just deals with data operation and abstracts out this database part in a separate file?
Currently, my api/route.ts file looks like below, as you can see it's tightly coupled with vendor-specific database API.
import clientPromise from '@/app/mongodb';
import { NextResponse } from 'next/server';
import * as crypto from 'crypto';
import { v4 as uuidv4 } from "uuid";
export async function POST(request: Request) {
const ip = // get the IP
const sha256 = crypto.createHmac("sha256", process.env.SALT!);
const ipHash = sha256.update(ip!);
const ipHashDigest = ipHash.digest("hex");
const client = await clientPromise;
const db = client.db("survey-db");
const creatorHistory = await db.collection("creator-ip-hash").findOne({ ipHash: ipHashDigest });
const uuid = uuidv4();
if (creatorHistory === null) {
await db.collection("creator-ip-hash").insertOne({ ipHash: ipHashDigest, count: 1 });
} else {
const prevCount = creatorHistory.count;
if (prevCount >= 3) {
return NextResponse.json({ "error": "API Limit is reached" }, { status: 429 });
}
await db.collection("creator-ip-hash").updateOne({ ipHash: ipHashDigest }, { $set: { count: prevCount + 1 } });
}
const survey = await request.json();
survey.creatorIpHash = ipHashDigest;
survey.uuid = uuid;
await db.collection("survey-templates").insertOne(survey);
return NextResponse.json({ "survey-id": uuid }, { status: 201 });
}
I know POST is not a class but a function, but still there is a way to inject dependency somehow. I was looking at this guide: https://himynameistim.com/blog/dependency-injection-with-nextjs-and-typescript but it seems like it for maybe an older version and also doesn't show how to incorporate this in API routes. They have shown the use of inject and injectable but it's for class.
I found this discussion on the next.js GitHub community: https://github.com/vercel/next.js/discussions/46805
Seems like we have to work around with package.json or webpack, Still I can't able to find any proper guide on how to use this in API routes. They are using tsyringe for the dependency injection container.
However, I am open to any solution just decoupling this data access layer from API routes, so in the future, if I have to change the database backend I can do it efficiently, if anyone has done it before please let me know your idea.
if your question is related to setting up a database connection similar to express.js we cannot do this in next.js. I explained it here why. In Express.js, database connections are typically long-lived, meaning they are established when the application starts and remain active throughout the lifetime of the application. But in next.js due to the nature of serverless functions, we have to implement per-request connection.
But if you want to implement dependency injection, you can try tsyringe developed by Microsoft. To apply this in next.js, you can follow this. In case link gets broken in the future:
Modify your tsconfig.json file to include the following settings, which will allow the use of decorators in TypeScript.
Add a .bablerc file to your project with the following settings.
Finally add this import to your _app.tsx file or in
appdirectory to layout.We're now ready to convert the code I had before into something more maintainable.
Example code creates a graphql client:
mport { DocumentNode } from "graphql";
note that you need to use class components to inject into constuctor