I’m working with a group to create an open source application using Next.js
with MongoDB
and Mongoose
, I found It difficult to find adequate documentation on being able to manipulate, query and update a user with next-auth
. I dug into issues, discussions, and documentation but only found myself halfway there.
Required packages:
mongodb
mongoose
next-auth
First following along with Next.js mongoose example
// utils/dbConnect.js
import mongoose from "mongoose";
async function dbConnect() {
if (mongoose.connection.readyState >= 1) {
// if connection is open return the instance of the databse for cleaner queries
return mongoose.connection.db;
}
return mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true,
poolSize: 10, //increase poolSize from default 5
});
}
export default dbConnect;
Then creating a custom User model to work with next-auth
’s TypeORM Adapter
// models/User.js
import Adapters from "next-auth/adapters";
// Extend the built-in models using class inheritance
export default class User extends Adapters.TypeORM.Models.User.model {
// You can extend the options in a model but you should not remove the base
// properties or change the order of the built-in options on the constructor
constructor(name, email, image, emailVerified) {
super(name, email, image, emailVerified);
}
}
export const UserSchema = {
name: "User",
target: User,
columns: {
...Adapters.TypeORM.Models.User.schema.columns,
// Add your own properties to the User schema
},
};
As an extra option add an index.js
for cleaner imports
// models/index.js
import User, { UserSchema } from "./User"
export default {
User: {
model: User,
schema: UserSchema,
},
}
Now to add the authentication routes for the custom adapter
// pages/api/auth/[...nextauth.js]
import NextAuth from "next-auth";
import Adapters from "next-auth/adapters";
import Providers from "next-auth/providers";
import User, { UserSchema } from "../../../models/User";
// import Models from '../../../models'
export default NextAuth({
site: process.env.NEXTAUTH_URL,
providers: [
Providers.GitHub({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
// any additional providers
],
adapter: Adapters.TypeORM.Adapter(
// The first argument should be a database connection string or TypeORM config object
process.env.MONGODB_URI,
// The second argument can be used to pass custom models and schemas
{
models: {
User: { model: User, schema: UserSchema },
// User: Model.User
},
}
),
// other options (pages, callbacks, session, ...etc)
});
Now we can query the MongoDB in the API routes
// pages/api/users/index.js
import dbConnect from "../../../utils/dbConnect";
export default async (req, res) => {
const db = await dbConnect(); // retrieve db instance
// you can also use switch/case on req.method for a clean structure
if (req.method === "GET") {
// query db for users collection
db.collection("users", (err, usersCollection) =>
// on retrieval of the users collection run any desired query methods to the collection
usersCollection.find({}).toArray((err, users) => {
// respond with users as json
res.status(200).json(users);
})
);
}
};
I hope this helps, I found the documentation to be suboptimal for mongo/mongoose however next-auth
supports MySQL, MariaDB, Postgres, SQL Server, and SQLite as well. If you don’t need to have custom user schemas it will work out of the box with MongoDB, I’ve also read some discussions of having a separate user-info
model for custom attributes then using callbacks to populate it although, I have not tested it myself.
// pages/api/auth/[...nextauth.js]
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
import dbConnect from '../../../utils/dbConnect'
export default NextAuth({
site: process.env.NEXTAUTH_URL,
providers: [
Providers.GitHub({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
// any additional providers
],
callback: {
async signIn(user, account, profile){
// something simular to
const db = await dbConnect()
db.collection("user-info", (err, userInfoCollection) =>
if(!userInfoCollection.exists({user})){
// note: you should add a ref to the user being stored by next-auth and look into the user object for any attributes you'd want to carry over
await userInfoCollection.create([{name: 'name', address: '555 random St'}], options, callback)
})
);
}
}
// other options
});
Resources:
Next-auth - supported DB’s - Docs
Next-auth - Custom Adapter - Docs
Next-auth - Custom User Model - Docs
Next.js - MongoDB + Mongoose - GitHub