I'm working on a Next.js app and looking at the best way to structure database access. With Next.js and web based apps in general, there are lots of different "server actions", aka, API calls, there are lots of potential entry points into the app, so what's the best way to structure this. I feel like the usual patterns are all frowned upon these days.
The logical solution in my head is what I guess you might call static dependency injection. So something like this, where there's a static call to initialise the interface with the database. A default is set for normal service, but we can replace that default when testing if we wish. However, this feels far from perfect.
export class User {
private static db: DbInterface;
private userData: any;
constructor(userData: any) {
this.userData = userData;
}
public static initialise(db: DbInterface) {
this.db = db;
}
public static loadFromId(id: number): User {
const userData: any = db.loadUserFromId(id);
if (userData) return new User(userData);
return null;
}
}
User.initialise(new DefaultDbInterface());
So user loading would be
const user = User.loadFromId(1234);
if (!user){
// handle user not found
}
Another option would be to create a User instance that we pass an instance of the DbInterface to, but that seems a bit of a faff to me, and therefore not perfect either. So something like:
export class User {
private db: DbInterface;
private userData: any;
constructor(db: DbInterface) {
this.db = db;
}
public loadFromId(id: number): User {
this.userData = db.loadUserFromId(id);
if (!this.userData) throw new Error("User not found");
return this;
}
}
So user loading would be
try {
const user = new User(new DefaultDbInterface()).loadFromId(1234);
} catch (error) {
// handle user not found
}
What's are my other options here? What's easiest to work with, maintain and debug?
.initialize
approach). \$\endgroup\$