首页 > 数据库 >Next.js + Mongodb CURD

Next.js + Mongodb CURD

时间:2024-05-20 12:07:51浏览次数:27  
标签:const Mongodb topics await js Topic CURD http id

环境

  • Next.js 14
  • React 18
  • Mongodb

前言

花了两周时间学习了Next.js, 自己做了个demo,尝试了下服务器端渲染,客户端渲染,给人的感觉就是又像回到了asp.net MVC时代, 需要在页面初次加载时显示的数据可以使用ViewModel来解决,需要在页面上有交互、异步刷新的业务可以使用ajax来解决。
最主要的是整理了使用Next.js 项目结构,一些文件、目录应该怎么放。

项目结构(目录结构)

  1. 需要区分服务器端渲染和客户端渲染的页面,Next.js 14版本推荐服务器端渲染页面是放在app目录下,按照目录约定方式配置路由。 比如这里app/addTopic, app/editTopic就对应两个路由地址

http://localhost:3000/addTopic
http://localhost:300/editTopic.
如果需要带路由参数,是把文件夹名称使用中括号包装起来

  1. 需要通过客户端渲染的页面建议创建Views or Pages目录,主要是和服务器渲染组件区分开,并且使用'use client' 指令描述
  2. API Endpoint - Next.js 可以创建服务器端接口, 就像asp.net MVC里创建Controller/Action 可以在Razor 视图里异步调用。 默认放在app/api目录下,如上图,根据约定就会暴露出如下几个api endpoint

http://localhost:3000/api/topics
http://localhost:3000/api/topics/{id}
然后具体的http协议可以在代码里指定,可以是POST OR GET OR PUT. 后面会贴上参考代码。 这一点我个人发现如果是复杂一些的路由,可能会在api目录先出现嵌套很多的目录结构,比如说URL上包含很多查询参数,按照Restful URL设计的话, 此时项目结构可能就有点凌乱,体验不是很好。

API Endpoint 代码片段

app/topics/route.ts

/**
 * POST
 * http://localhost:3000/api/topics
 * **/
export async function POST(request: any) {
  const { title, description } = await request.json();
  await connectMongoDb();

  await Topic.create({ title, description });
  return NextResponse.json({ message: "Topic Created" }, { status: 201 });
}

/**
 * GET
 * http://localhost:3000/api/topics
 * **/
export async function GET() {
  await connectMongoDb();
  const topics = await Topic.find();
  return NextResponse.json(topics);
}

/**
 * DELETE
 * http://localhost:3000/api/topics?id=123
 * **/
export async function DELETE(request: any) {
  const id = request.nextUrl.searchParams.get("id");
  await connectMongoDb();
  await Topic.findByIdAndDelete(id);
  return NextResponse.json({ message: "Topic Deleted" }, { status: 200 });
}

app/topics/[id]/route.ts

import connectMongoDb from "@/libs/mongodb";
import Topic from "@/models/topic";
import { NextResponse } from "next/server";

/**
 * http get
 * http://localhost:3000/api/topics/[id]
 */
export async function GET(request: any, { params }: any) {
    const { id } = params;
    await connectMongoDb();
    const topic = await Topic.findOne({ _id: id });
    return NextResponse.json({ topic }, { status: 200 });
}

/**
 * http put
 * http://localhost:3000/api/topics/[id]
 */
export async function PUT(request: any, { params }: any) {
    const { id } = params;
    //从request body 中解析参数newTitle, newDescription 给title, description 赋值
    const { newTitle: title, newDescription: description } = await request.json();
    await connectMongoDb();
    await Topic.findByIdAndUpdate(id, { title, description });
    return NextResponse.json({ message: "Topic Updated" }, { status: 200 });
}

使用postman测试接口, 在mongodb里查看数据

[POST]插入一条数据
QQ截图20240520104332.png
[GET]获取所有数据QQ截图20240520104346.png
[PUT]修改数据
QQ截图20240520104505.png

列表页面

const getTopics = async () => {
    const res = await fetch('http://localhost:3000/api/topics', {
        method: 'GET',
        cache: 'no-cache'//不使用缓存
    });

    return res.json();
}

export default async function TopicList() {
    const topics = await getTopics();

    return (
        <>
            {topics.map((t: Topic) => (
                <div
                    key={t._id}
                    className="p-4 border border-slate-300 my-3 flex justify-between gap-5 items-start">
                    <div>
                        <h2 className="font-bold text-2xl">{t.title}</h2>
                        <div>{t.description}</div>
                    </div>
                    <div className="flex gap-2">
                        <RemoveBtn id={t._id} />
                        <Link href={`/editTopic/${t._id}`}>
                            <HiPencilAlt size={24} />
                        </Link>
                    </div>
                </div>
            ))}
        </>
    )
}

列表页面使用服务器端渲染, 页面初始化时就去调用接口加载数据,这里会发现【删除】按钮就单独封装出来,需要客户端交互就需要使用客户端组件,也就是普通的React组件。

删除组件

删除组件顶部需要使用'use client'指令

'use client'

import { HiOutlineTrash } from "react-icons/hi";
import { useRouter } from "next/navigation";

export default function RemoveBtn({ id }: any) {
    const router = useRouter();
    const removeTopic = async () => {
        const confirmed = confirm("Are you sure?");
        if (confirmed) {
            const res = await fetch(`http://localhost:3000/api/topics?id=${id}`, {
                method: "DELETE"
            })

            if (res.ok) {
                router.refresh();
            }
        }
    }

    return (
        <button onClick={removeTopic} className="text-red-400">
            <HiOutlineTrash size={24} />
        </button>
    )
}

连接Mongodb

connect

import mongoose from "mongoose";

const connectMongoDb = async () => {
    try {
        await mongoose.connect(process.env.MONGODB_URI!);
        console.log("Connected to MongoDB");
    } catch (error) {
        console.log(error);
    }
}

export default connectMongoDb;

Schema

import mongoose, { Schema } from "mongoose";

const topicSchema = new Schema(
    {
        title: { type: String, required: true },
        description: { type: String, required: true }
    },
    {
        timestamps: true
    }
)

const Topic = mongoose.models.Topic || mongoose.model('Topic', topicSchema);

export default Topic;

参考

https://gitee.com/garfieldzf/next-topiclist-app

标签:const,Mongodb,topics,await,js,Topic,CURD,http,id
From: https://www.cnblogs.com/sword-successful/p/18201623

相关文章

  • etcd 和 MongoDB 的混沌(故障注入)测试方法
    最近在对一些自建的数据库driver/client基础库的健壮性做混沌(故障)测试,去验证了解业务的故障处理机制和恢复时长.主要涉及到了MongoDB和etcd这两个基础组件.本文会介绍下相关的测试方法.MongoDB中的故障测试MongoDB是比较世界上热门的文档型数据库,支持ACID事务......
  • mongo replicaset=rs0 com.mongodb.MongoSocketException: centosc
    1、描述虚拟机搭建mongo副本集虚拟机的设定的hostname为: centosc。虚拟机IP为192.168.25.129搭建三个副本集端口分别为,28017、28018、28019,运行mongo副本集报错,报错信息如下:2024-05-2010:22:39:235[main]INFOorg.apache.coyote.http11.Http11NioProtocol-StartingProtocol......
  • threejs饼图
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metaname="viewport"content="width=device-width,initial-scale=1.0"/><title>Cake</title>......
  • net.sf.jsqlparser.schema.Column.withColumnName(Ljava/lang/String;)Lnet/sf/jsqlpar
    https://blog.csdn.net/yuanzhugen/article/details/133648431 SpringBoot整合mybatisplus报错:net.sf.jsqlparser.schema.Column,isavailablefromthefollowinglocationsAnattemptwasmadetocallthemethodnet.sf.jsqlparser.schema.Column.withColumnName(Ljava/l......
  • P4171 [JSOI2010] 满汉全席 2-SAT
    P4171[JSOI2010]满汉全席2-SAT题目链接思路:2-SAT模板题,我们将满席定为1,汉席定为0.那么建边即可。判断同一道菜满汉是否在强联通分量中即可。注意多测清空!!!代码:vector<int>e[N];stack<int>stk;intdfn[N],low[N],tot;intinstk[N],scc[N],siz[N],cnt;intn,m;void......
  • JS测试文章
    目录1.HOOK1.1.直接替换函数1.2.为对象的属性赋值的方式2.chrome控制台3.函数3.1.箭头函数3.2.arguments对象4.对象4.1.原型继承4.2.访问对象内的方法:4.3.对象构造器函数4.4.内建构造器4.5.内建构造器的省略写法4.6.JSON对象4.7.JSONVSXML1.HOOK原理:JS是一......
  • react中的jsx语法
    JSX是JavaScriptXML的缩写,它是一种JavaScript的语法扩展。JSX允许在JavaScript代码中编写类似于XML或HTML的标记结构,用来描述用户界面的结构。 在React应用中,开发者通常使用JSX来定义组件的结构。这样做的好处是,JSX让代码更加直观易读,并且可以轻松地在JavaS......
  • 微信小程序使用crypto-js进行AES加密
    1.首先npmi crypto-js2.找到node_modules下的crypto-js文件夹3.在crypto-js文件下找到crypto-js.js,将它复制到你存放工具类js的文件夹下,例如我放到了utils文件夹下4.使用var CryptoJS = require('./crypto-js/crypto-js');写一个加密函数并导出exportconst encry......
  • 微信小程序使用JSEncrypt进行RSA加密
    1.首先npm一下JSEncrypt2.找到node_modules下的jsencrypt文件夹3.在jsencrypt文件下找到jsencrypt.min.js,将它复制到你存放工具类js的文件夹下,例如我放到了utils文件夹下4.使用var JSEncrypt = require('./jsencrypt/jsencrypt.min.js');写一个加密函数并导出exportcon......
  • MongoDB基础知识梳理笔记
    1、mongodb是什么?MongoDB是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB旨在给WEB应用提供可扩展的高性能数据存储解决方案。MongoDB将数据存储给一个文档,数据结构由键值(key=>value)对组成......