ในบทความนี้ เราจะสร้างแอปพลิเคชัน TODO แบบ Serverless Next.js เราจะพยายามทำให้ดีที่สุดเพื่อให้เป็นแบบมินิมอล มันจะไม่มีการเชื่อมต่อฐานข้อมูลใด ๆ มันจะไม่มีการพึ่งพาพิเศษอื่นใดนอกจาก Next.js มันจะไม่มีปุ่มใดๆ นอกจากนี้ minimalism
เจ๋งและสะอาด ฉันชอบมันเพราะฉันเป็นนักพัฒนาที่ขี้เกียจ :)
เหตุใดเราจึงหลีกเลี่ยงการเชื่อมต่อฐานข้อมูล
Next.js เป็นเฟรมเวิร์กที่ทันสมัยซึ่งช่วยให้นักพัฒนาส่วนหน้าสามารถพัฒนาแอปพลิเคชันแบบสแต็กแบบเต็มได้ ฟังก์ชันแบบไร้เซิร์ฟเวอร์มีบทบาทสำคัญในการทำให้การพัฒนาแบ็กเอนด์สำหรับนักพัฒนา Next.js ง่ายขึ้น อย่างที่คุณคงทราบ ฟังก์ชันไร้เซิร์ฟเวอร์ไม่ชอบการเชื่อมต่อฐานข้อมูลเนื่องจากลักษณะไร้สัญชาติ ดูที่นี่และที่นี่เป็นตัวอย่างของปัญหาการเชื่อมต่อฐานข้อมูลภายในฟังก์ชันแบบไร้เซิร์ฟเวอร์
REST คือคำตอบ
REST อนุญาตให้ไคลเอ็นต์และเซิร์ฟเวอร์สื่อสารโดยไม่มีข้อมูลเซสชัน การไร้สัญชาติและลักษณะที่เรียบง่ายทำให้ REST เป็นโปรโตคอลการสื่อสารที่สมบูรณ์แบบสำหรับสภาพแวดล้อมแบบไร้เซิร์ฟเวอร์ เราจะเข้าถึง Upstash Redis ด้วย REST
กองโครงการ
- ส่วนหน้า:Next.js
- แบ็กเอนด์:ฟังก์ชัน Vercel
- ฐานข้อมูล:เพิ่ม Redis ด้วย REST API
ดูการสาธิต:https://nextjs-todo-zeta.vercel.app/
ดูรหัส:https://github.com/upstash/examples/tree/master/nextjs-todo
การตั้งค่าโครงการ
สร้างแอป Next.js:npx create-next-app
สร้างฐานข้อมูล Upstash Redis ในภูมิภาค AWS-US-EAST-1 และคัดลอก REST URL และโทเค็น
โปรเจ็กต์จะเป็นแอปพลิเคชันหน้าเดียวที่มี 3 จุดปลาย API:
- pages/api/list.js:แสดงรายการสิ่งที่ต้องทำ
- pages/api/add.js:เพิ่มรายการสิ่งที่ต้องทำ
- pages/api/remove.js:ลบรายการสิ่งที่ต้องทำ
เดอะโค้ด
เพิ่มเพจ/api/list.js ดังนี้:
export default async (req, res) => {
const token = "REPLACE_YOUR_TOKEN";
const url = "https://REPLACE_YOUR_ENDPOINT/lrange/todo/0/100?_token=" + token;
return fetch(url)
.then((r) => r.json())
.then((data) => {
let result = JSON.stringify(data.result);
return res.status(200).json(result);
});
};
เพิ่มเพจ/api/add.js ดังนี้:
export default async (req, res) => {
if (!req.query.todo) {
return res.status(400).send("todo parameter required.");
}
let todo = encodeURI(req.query.todo);
const token = "REPLACE_YOUR_TOKEN";
const url =
"https://REPLACE_YOUR_ENDPOINT/lpush/todo/" + todo + "?_token=" + token;
return fetch(url)
.then((r) => r.json())
.then((data) => {
let result = JSON.stringify(data.result);
return res.status(200).json(result);
});
};
เพิ่ม pages/api/remove.js ดังนี้:
export default async (req, res) => {
if (!req.query.todo) {
return res.status(400).send("todo parameter required.");
}
let todo = encodeURI(req.query.todo);
const token = "REPLACE_YOUR_TOKEN";
const url =
"https://REPLACE_YOUR_ENDPOINT/lrem/todo/1/" + todo + "?_token=" + token;
return fetch(url)
.then((r) => r.json())
.then((data) => {
let result = JSON.stringify(data.result);
return res.status(200).json(result);
});
};
อัปเดตเพจ/index.js ดังนี้:
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import { useEffect, useState } from "react";
export default function Home() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [todo, setTodo] = useState("");
let changeHandler = (event) => {
setTodo(event.target.value);
};
let addTodo = (event) => {
setLoading(true);
event.preventDefault();
fetch("/api/add?todo=" + todo)
.then((res) => res.json())
.then((data) => {
loadTodos();
});
};
let removeTodo = (rtodo) => {
setLoading(true);
fetch("/api/remove?todo=" + rtodo)
.then((res) => res.json())
.then((data) => {
loadTodos();
});
};
let loadTodos = () => {
console.log("load todos");
fetch("/api/list")
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
});
};
useEffect(() => {
setLoading(true);
loadTodos();
}, []);
if (!data) return "Loading...";
return (
<div className={styles.container}>
<Head>
<title>Next.js TODO APP</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<h1 className={styles.title}>
TODO App with{" "}
<a href="https://blog.upstash.com/nextjs-todo">Next.js!</a>
<br />
<br />
</h1>
{loading ? (
<a href="#" className={styles.card}>
<img src="/loader.gif" />
</a>
) : (
<form className={styles.cardForm} onSubmit={addTodo}>
<input
className={styles.cardInput}
type="text"
name="todo"
onChange={changeHandler}
placeholder="Enter your exciting TODO item!"
/>
</form>
)}
{data.map((item) => (
<a
href="#"
onClick={() => removeTodo(item)}
className={styles.card}
>
<p>{item}</p>
</a>
))}
</div>
</main>
<footer className={styles.footer}>
<a
href="https://blog.upstash.com/nextjs-todo"
target="_blank"
rel="noopener noreferrer"
>
Powered by{" "}
<span className={styles.logo}>
<Image src="/logo.png" alt="Upstash Logo" width={87} height={25} />
</span>
</a>
</footer>
</div>
);
}
อย่างที่คุณเห็น มันเป็นแอปพลิเคชั่น React พื้นฐานที่ใช้ตะขอ เรามี 3 วิธีซึ่งโต้ตอบกับ API:addTodo, removeTodo และ loadTodos
และสุดท้ายให้อัปเดตไฟล์ styles/Home.module.css ดังที่นี่
เรียกใช้และปรับใช้
เรียกใช้โครงการของคุณในเครื่องด้วย npm run dev
. หากทุกอย่างดูดี คุณสามารถทำให้โครงการใช้งานได้โดยเรียกใช้ vercel
ในโฟลเดอร์โครงการ Vercel จะสร้างฟังก์ชันแบบไร้เซิร์ฟเวอร์สำหรับฟังก์ชัน API ของคุณ ขอบเขตเริ่มต้นสำหรับฟังก์ชัน Vercel คือ US-EAST-1 นั่นคือเหตุผลที่เราสร้างฐานข้อมูลของเราในภูมิภาคเดียวกัน
หมายเหตุ
- การเก็บโทเค็นฐานข้อมูลไว้ในตัวแปรสภาพแวดล้อม Vercel จะปลอดภัยกว่า
- เป็นการดีที่สุดสำหรับประสิทธิภาพที่จะรักษาฟังก์ชันแบบไร้เซิร์ฟเวอร์และฐานข้อมูล Redis ไว้ในภูมิภาคเดียวกัน
- เราสามารถใช้ไคลเอนต์ Redis แทน REST API แต่อย่างที่ฉันได้กล่าวไว้ก่อนหน้านี้ การเชื่อมต่อฐานข้อมูลอาจทำให้เกิดปัญหาภายในฟังก์ชันแบบไร้เซิร์ฟเวอร์ นอกจากนี้ โปรดทราบว่าเราไม่เห็นความแตกต่างด้านประสิทธิภาพที่สำคัญระหว่าง Upstash REST API และ API ดั้งเดิม