简体   繁体   English

是否可以在 next.js api 中运行 Mongoose?

[英]Is it possible to run Mongoose inside next.js api?

I'm building a website for my sister so that she can sell her art.我正在为我姐姐建立一个网站,以便她可以出售她的艺术品。 I am using Next.js to set everything up.我正在使用 Next.js 来设置所有内容。 The website renders the artwork by grabbing an array from a database and mapping through it.该网站通过从数据库中抓取一个数组并通过它进行映射来呈现艺术品。

Two Example objects两个示例对象

  {
    id: 8,
    path: "images/IMG_0008.jpg",
    size: "9x12x.75",
    price: "55",
    sold: false
  }
  {
    id: 9,
    path: "images/IMG_0009.jpg",
    size: "9x12x.75",
    price: "55",
    sold: false
}

pages/Shop.js页面/Shop.js

import Card from "../Components/Card";
import fetch from 'node-fetch'
import Layout from "../components/Layout";

function createCard(work) {
  return (
    <Card
      key={work.id}
      id={work.id}
      path={work.path}
      size={work.size}
      price={work.price}
      sold={work.sold}
    />
  );
}

export default function Shop({artwork}) {
  return (
    <Layout title="Shop">
      <p>This is the Shop page</p>

        {artwork.map(createCard)}
    </Layout>
  );
}

export async function getStaticProps() {
  const res = await fetch('http://localhost:3000/api/get-artwork')
  const artwork = await res.json()


  return {
    props: {
      artwork,
    },
  }
}

The problem I am running into is that when I try to use mongoose in the api/get-artwork.我遇到的问题是当我尝试在 api/get-artwork 中使用 mongoose 时。 It will only render the page once and once it is refreshed it will break I believe do to the fact the Schema and Model get redone.它只会呈现页面一次,一旦刷新,它就会中断我相信会重做 Schema 和 Model 的事实。

pages/api/get-artwork.js/页面/api/get-artwork.js/

const mongoose = require("mongoose");



mongoose.connect('mongodb://localhost:27017/ArtDB', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: false
});

const itemsSchema = {
  id: String,
  description: String,
  path: String,
  size: String,
  price: Number,
  sold: Boolean
};
const Art = mongoose.model("Art", itemsSchema);

export default (req, res) => {
  Art.find({sold: false}, (err, foundItems)=> {
  if (err) {
    console.log(err);
  } else {
    console.log(foundItems);
    res.status(200).json(foundItems);
  }
});

};

So to try to fix this I decided to use the native MongoDB driver.因此,为了解决这个问题,我决定使用本机 MongoDB 驱动程序。 Like this.像这样。

/pages/api/get-artwork/ /页面/API/获取艺术品/

const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');

// Connection URL
const url = 'mongodb://localhost:27017';

// Database Name
const dbName = 'ArtDB';

// Create a new MongoClient
const client = new MongoClient(url, {useUnifiedTopology: true});

let foundDocuments = ["Please Refresh"];

const findDocuments = function(db, callback) {
  // Get the documents collection
  const collection = db.collection('arts');
  // Find some documents
  collection.find({}).toArray(function(err, arts) {
    assert.equal(err, null);
    foundDocuments = arts;
    callback(arts);
  });
}

// Use connect method to connect to the Server
client.connect(function(err) {
  assert.equal(null, err);

  const db = client.db(dbName);

  findDocuments(db, function() {
     client.close();
   });

});
export default (req, res) => {
  res.send(foundDocuments);
};

This works for the most part but occasionally the array will not be returned.这在大多数情况下都有效,但偶尔不会返回数组。 I think this is because the page is loading before the mongodb part finishes?我认为这是因为页面在 mongodb 部分完成之前加载? So I guess my question is how do I make 100% sure that it loads the art correctly every time whether that be using mongoose or the native driver.所以我想我的问题是,无论是使用 mongoose 还是本机驱动程序,我如何才能 100% 确保每次都能正确加载艺术作品。

Thanks!谢谢!

The Next.js team has a good set of example code, which they add to regularly, one of them being Next.js with MongoDB and Mongoose. Next.js 团队有一组很好的示例代码,他们定期添加,其中之一是 Next.js 与 MongoDB 和 Mongoose。 Check it out, https://github.com/vercel/next.js/tree/canary/examples/with-mongodb-mongoose , and hope this helps if you're still searching for solutions.检查一下, https://github.com/vercel/next.js/tree/canary/examples/with-mongodb-mongoose ,如果您仍在寻找解决方案,希望这会有所帮助。

In pages/api/get-artwork.js/pages/api/get-artwork.js/

const mongoose = require("mongoose");

mongoose.connect("mongodb://localhost:27017/ArtDB", {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useFindAndModify: false,
});

const itemsSchema = {
  id: String,
  description: String,
  path: String,
  size: String,
  price: Number,
  sold: Boolean,
};

let Art;

try {
  // Trying to get the existing model to avoid OverwriteModelError
  Art = mongoose.model("Art");
} catch {
  Art = mongoose.model("Art", itemsSchema);
}

export default (req, res) => {
  Art.find({ sold: false }, (err, foundItems) => {
    if (err) {
      console.log(err);
    } else {
      console.log(foundItems);
      res.status(200).json(foundItems);
    }
  });
};

It works just fine for me.它对我来说很好用。

A little more complete answer might be helpful here.更完整的答案在这里可能会有所帮助。 Here's what's working for us.这就是对我们有用的东西。

I would suggest using the next-connect library to make this a little easier and not so redundant.我建议使用next-connect库使这更容易一点,而不是那么多余。

Noticed unnecessary reconnects in dev so I bind to the global this property in Node.注意到 dev 中不必要的重新连接,所以我绑定到 Node.js 中的全局 this 属性。 Perhaps this isn't required but that's what I've noticed.也许这不是必需的,但这就是我注意到的。 Likely tied to hot reloads during development.可能与开发过程中的热重载有关。

This is a lengthy post but not nearly as complicated as it seems, comment if you have questions.这是一篇冗长的文章,但并不像看起来那么复杂,如果您有任何问题,请发表评论。

Create Middleware Helper:创建中间件助手:

import mongoose from 'mongoose';

// Get your connection string from .env.local

const MONGODB_CONN_STR = process.env.MONGODB_CONN_STR;

const databaseMiddleware = async (req, res, next) => {

  try {

    if (!global.mongoose) {

      global.mongoose = await mongoose.connect(MONGODB_CONN_STR, {
        useNewUrlParser: true,
        useUnifiedTopology: true,
        useFindAndModify: false,
      });

    }

  }
  catch (ex) {
    console.error(ex);
  }

  // You could extend the NextRequest interface 
  // with the mongoose instance as well if you wish.
  // req.mongoose = global.mongoose;

  return next();

};

export default databaseMiddleware;

Create Model:创建模型:

Typically the path here might be src/models/app.ts .通常,这里的路径可能是src/models/app.ts

import mongoose, { Schema } from 'mongoose';

const MODEL_NAME = 'App';

const schema = new Schema({
  name: String
});

const Model = mongoose.models[MODEL_NAME] || mongoose.model(MODEL_NAME, schema);

export default Model;

Implement Next Connect:实现下一个连接:

Typically I'll put this in a path like src/middleware/index.ts (or index.js if not using Typescript).通常我会把它放在像src/middleware/index.ts这样的路径中(如果不使用 Typescript,或者 index.js)。

Note: the ...middleware here just allows you, see below, to pass in additional middleware on the fly when the handler here is created.注意:这里的...middleware只允许您(见下文)在创建处理程序时即时传递额外的中间件。

This is quite useful as our handler creator here can have things like logging and other useful middleware so it's not so redundant in each page/api file.这非常有用,因为我们的处理程序创建者可以拥有诸如日志记录和其他有用的中间件之类的东西,因此它在每个页面/api 文件中都不是那么多余。

export function createHandler(...middleware) {

  return  nextConnect().use(databaseMiddleware, ...middleware);

}

Use in Api Route:在 Api 路由中使用:

Putting it together, we can now use our App Model with ease放在一起,我们现在可以轻松使用我们的应用程序模型

import createHandler from 'path/to/above/createHandler';
import App from 'path/to/above/model/app';

// again you can pass in middleware here
// maybe you have some permissions middleware???
const handler = createHandler(); 

handler.get(async (req, res) => {
  
  // Do something with App
  const apps = await App.find().exec();

  res.json(apps);

});

export default handler;

When we use mongoose with express.js , we connect to mongodb and run the code once.当我们将 mongoose 与express.js一起使用时,我们连接到 mongodb 并运行一次代码。 But in next.js everytime we need to connect to mongodb, we have to run connection code.但是在 next.js 每次我们需要连接到 mongodb 时,我们都必须运行连接代码。

Connection to mongodb连接到 mongodb

import mongoose from "mongoose";

const dbConnect = () => {
  if (mongoose.connection.readyState >= 1) {
    // if it is not ready yet return
    return;
  }

  mongoose
    .connect(process.env.DB_LOCAL_URI, {
      //****** since  mongoose 6, we dont need those******
      //   useNewUrlParser: true,
      //   useUnifiedTopology: true,
      //   useFindAndModify: false,
      //   useCreateIndex: true,
    })
    .catch((err) => console.log(err))
    .then((con) => console.log("connected to db"));
};

export default dbConnect;

Use it inside next api接下来在里面使用 api

Before running handler code, you need to connect first在运行处理程序代码之前,您需要先连接

import dbConnect from "../../../config/dbConnect";

dbConnect();

... then write handler code

A concise OO approach简洁的 OO 方法

Inspired by @Blujedis answer this is what I ended up with.受@Blujedis 的启发,这就是我最终得到的答案。

/* /api/util/handler.js */

import nextConnect from "next-connect";
import mongoose from "mongoose";

export class Handler {

    static dbConnection = null

    static dbMiddleware = async (req, res, next) => {
        try {
            Handler.dbConnection ||= await mongoose.connect(process.env.MONGO_URL)
            next()
        } catch (err) {
            console.error(err);
            next(err)
        }
    }

    constructor(...middleware) {
        return nextConnect().use(Handler.dbMiddleware, ...middleware);
    }
}

Example handler:示例处理程序:

/* /api/people.js */

import { Handler } from "./util/handler"
import { Person } from "./models/person"

export default
    new Handler()
        .get(async (req, res) => {
            const people = await Person.find({})
            return res.status(200).json(people)
        })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM