Реализуйте Google OAuth в своем приложении Next.js с помощью NextAuth.js
В этой статье мы узнаем следующее:
- Использование NextAuth.js с Next.js.
- Сторонняя аутентификация Google с помощью NextAuth.js.
- Поддержание связи между пользователями и таблицами Notes.
- Выполнение операций CRUD с нашей базой данных для чтения и записи заметок.
Вступление
Итак, вы создали потрясающий блог с Next.js, который ежедневно привлекает миллионы пользователей. Чтобы получать доход от написания статей, вам пришла в голову идея заблокировать статьи за платный доступ. Так читатели смогут вас поддержать. Чтобы реализовать такую идею, вам потребуется выполнить следующие шаги:
- Создайте систему входа в систему.
- Убедитесь, что зарегистрированный пользователь подписался на ваш сайт.
- Если они являются платными участниками, позвольте им продолжить наслаждаться вашим контентом.
- С другой стороны, если посетитель не вошел в систему, предложите ему создать учетную запись или войти в систему.
Однако давайте посмотрим правде в глаза: аутентификация - это боль. Для этого требуется тонна кода, а в некоторых случаях обслуживание затруднено.
Здесь на помощь приходит Next-Auth.js. Он не только упрощает процесс создания аутентификации пользователей, но также имеет следующие преимущества:
- Файлы cookie: в паспорте, если вы перезапустите свой сервер, вы автоматически выйдете из системы. В Next-Auth.js это не проблема, поскольку он уже использует файлы cookie для хранения данных сеанса.
- Чрезвычайно безопасный: Next-Auth.js активно продвигает варианты входа без пароля. Кроме того, он использует токены CSRF в своем коде, чтобы гарантировать, что ваш проект не будет содержать эксплойтов и других лазеек.
Пришло время писать код!
Настройка проекта
Инициализация репозитория
Этот шаг довольно прост. Чтобы создать экземпляр проекта Next.js, выполните следующую команду терминала:
Затем создайте файл .env.local
в корне вашего проекта следующим образом:
Здесь мы будем хранить переменные среды.
Теперь приступим к установке наших зависимостей.
Установка модулей
В этом проекте мы будем использовать следующие модули:
next-auth
: Суть нашего проекта. Эта библиотека будет обрабатывать все наши процессы, основанные на аутентификации.mongodb
: Поскольку мы будем использовать сервер MongoDB, Next-Auth.js будет использовать пакетmongodb
как одну из своих зависимостей.mongoose
: Для взаимодействия с нашей базой данных и выполнения операций CRUD с нашими коллекциями.
Чтобы установить эти пакеты, выполните следующую команду терминала:
Давайте теперь инициализируем наш сервер MongoDB.
Настройка MongoDB Atlas
Рекомендую использовать MongoDB Atlas. Он настраивает для вас сервер базы данных в облаке без какой-либо дополнительной настройки.
Сначала войдите на страницу Атласа и выполните следующие действия, чтобы создать свой первый кластер. Когда это будет сделано, нажмите Подключиться:
Затем выберите «Разрешить доступ из любого места»:
Теперь создайте свое имя пользователя и пароль и нажмите «Создать пользователя базы данных», чтобы зарегистрироваться в качестве пользователя. Когда это будет сделано, нажмите «Подключить ваше приложение»:
Затем вы получите строку подключения. Скопируйте это и вставьте в свой .env.local
файл:
Теперь займемся созданием учетных данных Google OAuth.
OAuth с Google
Перейдите в Консоль разработчика Google и нажмите Выбрать проект. Затем нажмите Новый проект:
Когда это будет сделано, перейдите на боковую панель и нажмите «API и службы». Затем нажмите «Панель управления»:
На верхней панели нажмите «Включить API и службы». Вы попадете на страницу библиотеки API. Найдите «Google+» и включите его.
Теперь веб-сайт приведет вас на страницу «Включенные API». Сначала выберите «Учетные данные», а затем щелкните ссылку «Учетные данные в API и службах»:
Это перенаправит вас на домашнюю страницу. Нажмите «Создать учетные данные», а затем «Идентификатор клиента OAuth»:
Выберите «Веб-приложение»:
Теперь дайте вашему приложению имя. Когда это будет сделано, прокрутите вниз до «URI авторизованного перенаправления» и настройте его следующим образом:
Теперь нажмите «Создать», чтобы сообщить Google о создании нашего клиента OAuth. Вы получите свой идентификатор клиента и секрет:
Скопируйте их в свой .env.local
файл следующим образом:
И мы, наконец, закончили! В следующем разделе мы обсудим нашу файловую структуру.
Файловая структура проекта
В корневом каталоге вашего проекта создайте следующие папки:
models
: Будем хранить наши модели Mongoose.helpers
: будет содержать все наши функции промежуточного программного обеспечения.components
: хранит наши пользовательские компоненты, которые можно повторно использовать в других файлах.
Теперь войдите в свою папку pages
и создайте эти папки:
notes
в каталоге/api
: будет обрабатывать/api/notes/
маршруты.auth
в каталоге/api
: будет содержать логику аутентификации пользователя.notes
: будет обрабатывать маршрут/notes
.
В итоге структура вашего проекта должна выглядеть так:
Готово! А теперь давайте попробуем построить внутреннюю часть нашей системы входа в систему.
Создание нашей системы входа в систему
Настройка бэкэнда
В своем проекте перейдите в каталог /pages/api/auth
и создайте файл с именем [...nextauth.js]
. Это означает, что все маршруты, начинающиеся с /api/auth
, будут обрабатываться файлом [...nextauth].js
.
В /pages/api/[...nextauth].js
напишите следующий фрагмент кода:
import NextAuth from "next-auth"; | |
import Providers from "next-auth/providers"; | |
export default NextAuth({ | |
// Configure one or more authentication providers | |
providers: [ | |
Providers.Google({ | |
clientId: process.env.GOOGLE_ID, | |
clientSecret: process.env.GOOGLE_SECRET, | |
}), | |
], | |
database: process.env.MONGODB_URI, | |
callbacks: { | |
session: async (session, user) => { | |
session.id = user.id; | |
return Promise.resolve(session); | |
}, | |
}, | |
}); |
- Строки 1-2: Импортируйте модуль
NextAuth
для реализации аутентификации. Затем импортируйте пакетProviders
, чтобы сообщить Next.js, что мы будем использовать стороннюю аутентификацию. - Строки 6-11: Сообщите Next.js, что мы будем использовать только Google OAuth для этого проекта. Мы также передаем наш идентификатор Google и секретный ключ через переменные среды.
- Строка 13: используйте наш экземпляр базы данных MongoDB. Строка подключения передается через переменные среды.
- Строка 14: Наш обратный вызов
session
будет запущен, когда пользовательский сеанс будет проверен. - Строка 16. Раскройте свойство сеанса
id
, чтобы мы могли получить к нему доступ. Это будет полезно при создании заметок, связанных с каждым пользователем.
Давайте теперь создадим наш интерфейс.
Создание нашего интерфейса
В /pages/index.js
замените весь свой код следующим:
import { signIn, signOut, useSession } from "next-auth/client"; | |
export default function Home() { | |
const [session, loading] = useSession(); | |
return ( | |
<div> | |
{loading && <p>Loading..</p>} | |
{!session && ( | |
<> | |
Not signed in <br /> | |
<button | |
onClick={() => | |
signIn("google", { callbackUrl: "http://localhost:3000/" }) | |
} | |
> | |
Sign in | |
</button> | |
</> | |
)} | |
{session && ( | |
<> | |
Signed in as {session.user.name} <br /> | |
<button onClick={() => signOut()}>Sign out</button> | |
</> | |
)} | |
</div> | |
); | |
} |
- Строка 3. Используйте
useSession
Hook, чтобы получить данные вошедшего в систему пользователя. - Строка 7: проверьте, не вошел ли пользователь в систему (если
session
не имеет значения). Если да, предложите пользователю войти в систему. МетодsignIn
обрабатывает функции входа. - Строка 19: Если пользователь вошел в систему (если
session
имеет значение), то показать возможность выхода. МетодsignOut
сделает это за нас. - Строка 21: отображение имени пользователя на главной странице.
Последний шаг - изменить наш _app.js
файл. В /pages/_app.js
сначала импортируйте модуль Provider
следующим образом:
Теперь найдите в /pages/_app.js
следующий фрагмент кода:
Измените это так:
- Строки 2–4: оберните наш
Component
тегамиProvider
. Это сделает наши данные сеанса постоянными на всех маршрутах.
Запустите код. Это будет результат:
Большой! Наш код работает как задумано. Теперь создадим наш Note
компонент.
В итоге ваш /pages/_app.js
файл должен выглядеть так:
import "../styles/globals.css"; | |
import { Provider } from "next-auth/client"; | |
function MyApp({ Component, pageProps }) { | |
return ( | |
<Provider session={pageProps.session}> | |
<Component {...pageProps} /> | |
</Provider> | |
); | |
} | |
export default MyApp; |
Компонент Note
В папке components
создайте файл с именем Note.js
. В этом файле напишите следующий фрагмент кода:
import Link from "next/link"; | |
import { useRouter } from "next/router"; | |
export default function Note({ id, title }) { | |
const router = useRouter(); | |
const sendDelete = async () => { | |
const res = await fetch(`/api/notes/delete/${id}`, { | |
method: "DELETE", | |
}); | |
router.push("/dashboard"); | |
}; | |
return ( | |
<div> | |
<Link href={`/notes/${id}`}> | |
<a> | |
{" "} | |
<h1>{title}</h1> | |
</a> | |
</Link> | |
<button> | |
<Link href={`/notes/update/${id}`}>Update</Link> | |
</button> | |
<button onClick={() => sendDelete()}>Delete</button> | |
</div> | |
); | |
} |
- Строка 4: Наш
Note
компонент будет иметь два свойства:id
иtitle
. - Строка 5: этот
useRouter
экземпляр поможет нам выполнять перенаправление на стороне клиента. - Строка 7: Создайте функцию
sendDelete
. - Строки 8–10: выполнение запроса
DELETE
к маршрутуapi/notes/delete/{id}
. Это скажет серверу идентифицировать нашу запись поid
, а затем удалить ее. - Строка 11: После запроса
DELETE
перенаправьте пользователя на/dashboard
. - Строки 16-21: отобразите опору
title
. Если щелкнуть, перенаправить пользователя на маршрут/notes/{id}
. Это отобразит полную информацию о заметке. - Строки 22-24: перенаправляют пользователя на страницу
/notes/update/${id}
. Это позволит клиенту обновить указанный документ. - Строка 25: Если щелкнуть, вызвать функцию
sendDelete
.
В следующем разделе мы создадим нашу страницу панели инструментов.
Страница панели инструментов
В папке /pages
создайте файл с именем dashboard.js
. В этом файле будут показаны все сохраненные заметки пользователя.
В pages/dashboard.js
напишите следующий код:
import { useSession } from "next-auth/client"; | |
import Link from "next/link"; | |
import Note from "../components/Note"; | |
export default function Dashboard() { | |
const [session, loading] = useSession(); | |
return ( | |
<div> | |
{!session && <h1>You're not logged in yet</h1>} | |
{session && <h1>Hello, {session.user.name}!</h1>} | |
<button> | |
<Link href={`/notes/add`}>Add</Link>{" "} | |
</button> | |
<button onClick={() => signOut({ callbackUrl: "/" })}>Sign out</button> | |
</div> | |
); | |
} |
- Строка 6:
useSession
Hook сообщит нам, вошел ли пользователь в систему. - Строка 11: отображение приветственного сообщения, если пользователь вошел в систему.
- Строки 13–14: при нажатии пользователь будет перенаправлен на маршрут
notes/add
. - Строка 15: Эта кнопка позволяет пользователю выйти из системы. В случае успеха наше приложение перенаправит их на страницу
/
.
Запустите код. Должен быть результат:
Однако заставлять пользователя вручную переходить на страницу /dashboard
- не очень удобно. Нам нужно это исправить.
В pages/index.js
найдите следующий фрагмент кода:
Измените это так:
- Строка 3: Когда пользователь входит в систему, перенаправьте его на страницу
dashboard
.
Запустите код. Должен быть результат:
Готово! Пришло время создать на его основе наше приложение для создания заметок.
В итоге /pages/index.js
должен выглядеть так:
import { signIn, signOut, useSession } from "next-auth/client"; | |
export default function Home() { | |
const [session, loading] = useSession(); | |
return ( | |
<div> | |
{loading && <p>Loading..</p>} | |
{!session && ( | |
<> | |
Not signed in <br /> | |
<button | |
onClick={() => | |
signIn("google", { | |
callbackUrl: "http://localhost:3000/dashboard", | |
}) | |
} | |
> | |
Sign in | |
</button> | |
</> | |
)} | |
{session && ( | |
<> | |
Signed in as {session.user.name} <br /> | |
<button onClick={() => signOut()}>Sign out</button> | |
</> | |
)} | |
</div> | |
); | |
} |
Приложение "Заметки"
Модель Notes
В папке /models
создайте файл с именем Note.js
. В /models/Note.js
напишите следующий код:
import mongoose, { Schema } from "mongoose"; | |
const noteSchema = new Schema({ | |
user: { | |
type: Schema.Types.ObjectId, | |
ref: "user", | |
}, | |
title: String, | |
body: String, | |
}); | |
mongoose.models = {}; | |
export default mongoose.models.Note || mongoose.model("Note", noteSchema); |
- Строка 3: Определите наш
noteSchema
. - Строка 4: В нашей коллекции будет поле
user
, которое будет связано с вошедшим в систему пользователем. Это позволяет нам создать реляционную базу данных. - Строки 8–9: Наша модель будет иметь поля
title
иbody
, которые будут содержать строковые значения. - Строка 13: Экспортируйте нашу
Note
модель, которая будет производным отnoteSchema
.
Давайте теперь поработаем над связыванием нашей базы данных с этим проектом.
Подключение к базе данных
В папке helpers
создайте файл с именем dbConnect.js
. Как следует из названия, этот файл содержит конфигурацию нашей базы данных.
В helpers/dbConnect.js
напишите следующий код:
import mongoose from "mongoose"; | |
const MONGODB_URI = process.env.MONGODB_URI; | |
if (!MONGODB_URI) { | |
throw new Error( | |
"Please define the MONGODB_URI environment variable inside .env.local" | |
); | |
} | |
let cached = global.mongoose; | |
if (!cached) { | |
cached = global.mongoose = { conn: null, promise: null }; | |
} | |
async function dbConnect() { | |
if (cached.conn) { | |
return cached.conn; | |
} | |
if (!cached.promise) { | |
const opts = { | |
useNewUrlParser: true, | |
useUnifiedTopology: true, | |
bufferCommands: false, | |
bufferMaxEntries: 0, | |
useFindAndModify: false, | |
useCreateIndex: true, | |
}; | |
cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => { | |
return mongoose; | |
}); | |
} | |
cached.conn = await cached.promise; | |
return cached.conn; | |
} | |
export default dbConnect; |
- Строки 3–9: Получите нашу строку подключения MongoDB от
.env.local
. Если его нет, то выдает ошибку. - Строка 16: Здесь мы используем Globals для поддержания кэшированного соединения. Это предотвратит рост количества подключений во время использования.
- Строка 28-34: Укажите конфигурацию нашей базы данных.
- Строка 37: подключение к базе данных.
- Строка 45: Наконец, экспортируем нашу
dbConnect
функцию. Теперь мы можем использовать его в нашем проекте.
Мы сделали! Давайте теперь поработаем с CRUD-операциями с нашими заметками.
Добавление заметки
Перейдите в папку /pages/api/notes
и создайте файл с именем add.js
. Этот файл скажет нашей базе данных вставить запись.
В /pages/api/notes/add.js
напишите следующий код:
import { getSession } from "next-auth/client"; | |
import Note from "../../../models/Note"; | |
import dbConnect from "../../../helpers/dbConnect"; | |
export default async (req, res) => { | |
const user = await getSession({ req }); | |
await dbConnect(); | |
if (!user) { | |
return res.json({ error: "not logged in" }); | |
} | |
if (req.method === "POST") { | |
const note = new Note({ | |
user: user.id, | |
title: req.body.title, | |
body: req.body.body, | |
}); | |
note.save(async function (err, doc) { | |
if (err) console.log(err); | |
console.log(doc); | |
}); | |
} | |
res.end(); | |
}; |
- Строка 6: Метод
getSession
вернет данные пользователя. - Строка 8: Подключитесь к базе данных перед выполнением любых критически важных для базы данных функций.
- Строка 10: Если клиент не вошел в систему, отправьте сообщение об ошибке.
- Строка 14: Проверить, был ли выполнен запрос
POST
. - Строки 15–16. Если верно, то создайте
Note
документ с полемuser
, указывающим наid
вошедшего в систему пользователя. Это позволяет нам строить отношения. - Строки 17-18: поля
title
иbody
будут содержать введенные пользователем данные. - Строки 21-24: Сохраните документ и выйдите из сохраненной записи.
- Строка 26: Завершить ответ.
Давайте теперь закодируем наш интерфейс для этой функции. В /pages/notes
создайте файл с именем add.js
.
В pages/notes/add.js
напишите следующий код:
import { useSession } from "next-auth/client"; | |
import { useRouter } from "next/router"; | |
export default function Add() { | |
const router = useRouter(); | |
const [session, loading] = useSession(); | |
const addNote = async (event) => { | |
event.preventDefault(); | |
const res = await fetch("/api/notes/add", { | |
body: JSON.stringify({ | |
title: event.target.title.value, | |
body: event.target.body.value, | |
}), | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
method: "POST", | |
}); | |
router.push("/dashboard"); | |
}; | |
return ( | |
<div> | |
{loading && <p>Loading..</p>} | |
{session ? ( | |
<form onSubmit={addNote}> | |
<label htmlFor="name">Name</label> | |
<input id="title" name="title" type="text" autoComplete="name" /> | |
<input id="body" name="body" type="text" autoComplete="false" /> | |
<button type="submit">Add</button> | |
</form> | |
) : ( | |
<p>Not logged in to view this resource</p> | |
)} | |
</div> | |
); | |
} |
- Строка 7: Создайте нашу функцию
addNote
. - Строки 10-19: отправьте
POST
запрос на/api/notes/add
. Это скажет нашему серверу добавить данные в базу данных. Наши поляbody
иtitle
будут иметь значенияinput
. - Строка 20: Когда запрос завершен, перенаправьте пользователя на
/dashboard
. - Строка 26: Когда форма отправлена, вызовите функцию
addNote
.
Запустите код. Должен быть результат:
Посмотрите на консоль:
Это доказывает, что наш документ был сохранен в базе данных. Наш код работает так, как ожидалось.
Теперь мы будем работать над отображением всех наших заметок.
Отображение наших заметок
Давайте сначала разработаем внутренний код этого раздела. В папке /pages/api/notes
создайте файл с именем list.js
. В этом файле напишите следующий код:
import { getSession } from "next-auth/client"; | |
import Note from "../../../models/Note"; | |
import dbConnect from "../../../helpers/dbConnect"; | |
export default async (req, res) => { | |
const user = await getSession({ req }); | |
await dbConnect(); | |
if (user) { | |
console.log("user id: " + user.id); | |
const notes = await Note.find({ user: user.id }).lean(); | |
return res.json({ notes }); | |
} else res.json({ error: "Not logged in" }); | |
}; |
- Строка 11: Найдите все заметки пользователя. Это делается путем фильтрации заметок, содержащих поле
user
поляid
текущего пользователя. Наконец, используйте методlean
, чтобы вернуть документы в формате JSON. - Строка 12: отправьте результаты клиенту в формате JSON.
- Строка 13: Если пользователь не вошел в систему, вернуть ошибку.
Теперь поработаем с нашим интерфейсом. В /pages/dashboard.js
добавьте следующий фрагмент кода:
export async function getServerSideProps(context) { | |
const hostname = "http://localhost:3000"; | |
const options = { headers: { cookie: context.req.headers.cookie } }; | |
const res = await fetch(`${hostname}/api/notes/list`, options); | |
const json = await res.json(); | |
if (json.error) { | |
return { | |
redirect: { | |
destination: "/", | |
permanent: false, | |
}, | |
}; | |
} | |
return { | |
props: { | |
data: json, | |
}, | |
}; | |
} |
- Строка 1: функция
getServerSideProps
получит данные с сервера. - Строка 3: Создайте наши заголовки, чтобы сообщить NextAuth.js, что мы уполномочены просматривать наши документы.
- Строка 4: Выполните
FETCH
запрос к нашему серверу и отправьте эти заголовки. - Строка 6: Если возвращенные данные содержат ошибку, вместо этого перенаправьте пользователя на маршрут
/
. - Строка 14: отправьте возвращенные данные как реквизиты.
Затем в /pages/dashboard.js
найдите следующий фрагмент кода:
Измените это так:
Теперь мы можем использовать данные, возвращаемые сервером, в нашем компоненте.
В этом же файле найдите свой return
блок:
Замени его так:
- Строка 2: Используйте функцию
map
для отображения наших данных. - Строка 4: Каждый объект будет отображаться с компонентом
Note
.
Запустите код. Должен быть результат:
Наш код работает! Давайте теперь поработаем над чтением конкретного документа.
В итоге /pages/dashboard.js
должно выглядеть так:
import { signIn, signOut, useSession, getSession } from "next-auth/client"; | |
import Link from "next/link"; | |
import Note from "../components/Note"; | |
export default function Dashboard({ data }) { | |
const [session, loading] = useSession(); | |
return ( | |
<div> | |
{data.notes.map((item) => ( | |
<Note id={item._id} title={item.title} body={item.body} /> | |
))} | |
<button> | |
<Link href="/notes/add">Add</Link>{" "} | |
</button> | |
<button onClick={() => signOut({ callbackUrl: "/" })}>Sign out</button> | |
</div> | |
); | |
} | |
export async function getServerSideProps(context) { | |
const hostname = "http://localhost:3000"; | |
const options = { headers: { cookie: context.req.headers.cookie } }; | |
const res = await fetch(`${hostname}/api/notes/list`, options); | |
const json = await res.json(); | |
if (json.error) { | |
return { | |
redirect: { | |
destination: "/", | |
permanent: false, | |
}, | |
}; | |
} | |
return { | |
props: { | |
data: json, | |
}, | |
}; | |
} |
Чтение заметки
В качестве первого шага нам нужно закодировать бэкэнд. В каталоге /pages/api/notes
создайте файл с именем [id].js
. Здесь напишите следующий код:
import { getSession } from "next-auth/client"; | |
import Note from "../../../models/Note"; | |
import dbConnect from "../../../helpers/dbConnect"; | |
export default async (req, res) => { | |
const user = await getSession({ req }); | |
await dbConnect(); | |
if (!user) return res.json({ error: "Not logged in" }); | |
const note = await Note.findOne({ _id: req.query.id, user: user.id }); | |
return res.json({ note }); | |
}; |
- Строка 10. Найдите заметку пользователя с конкретным
id
. - Строка 12: вернуть документ в формате JSON.
Теперь нам нужно создать наш интерфейс. В папке /pages/notes
создайте файл с именем [id].js
. Это означает, что этот файл будет обрабатывать маршрут /pages/notes/{id}
. Здесь {id}
- это id
нашей заметки.
В /pages/notes/[id].js
напишите следующий код:
export default function Specific({ data }) { | |
return ( | |
<> | |
{data.note && ( | |
<div> | |
<h1>{data.note.title}</h1> | |
<p>{data.note.body}</p> | |
</div> | |
)} | |
</> | |
); | |
} | |
export async function getServerSideProps(context) { | |
const hostname = "http://localhost:3000"; | |
const options = { headers: { cookie: context.req.headers.cookie } }; | |
const res = await fetch( | |
`${hostname}/api/notes/${context.params.id}`, | |
options | |
); | |
const json = await res.json(); | |
if (json.error) { | |
return { | |
redirect: { | |
destination: "/", | |
permanent: false, | |
}, | |
}; | |
} | |
return { | |
props: { | |
data: json, | |
}, | |
}; | |
} |
- Строка 1: свойство
data
содержит наши данные, которые мы хотим отобразить. - Строки 6-7: отображают поля
title
иbody
нашего объекта. - Строка 14: Создайте функцию
getServerSideProps
для получения данных с сервера. - Строки 18–22: выборка данных из базы данных и их возврат.
- Строки 24-31: Если была ошибка, вместо этого перенаправьте пользователя на
/
. - Строка 33: вернуть данные в компонент
Specific
в качестве свойств.
Запустите код. Должен быть результат:
Большой! Наш код работает так, как ожидалось.
На следующем шаге мы узнаем, как обновлять наши записи.
Обновление заметки
В каталоге /pages/api/note
создайте папку с именем update
. В этой папке создайте файл с именем [id].js
.
В /pages/api/note/update/[id].js
напишите следующий код:
import { getSession } from "next-auth/client"; | |
import Note from "../../../../models/Note"; | |
import dbConnect from "../../../../helpers/dbConnect"; | |
export default async (req, res) => { | |
const user = await getSession({ req }); | |
if (!user) return res.json({ error: "user not logged in" }); | |
await dbConnect(); | |
if (req.method === "GET") { | |
let note = await Note.findOne({ user: user.id, _id: req.query.id }).lean(); | |
return res.json({ note }); | |
} | |
if (req.method === "PUT") { | |
let note = await Note.findOneAndUpdate( | |
{ | |
user: user.id, | |
_id: req.query.id, | |
}, | |
{ title: req.body.title, body: req.body.body }, | |
(err, doc) => console.log(doc) | |
); | |
} | |
res.end(); | |
}; | |
- Строка 11: Проверить, был ли выполнен запрос
GET
. - Строка 12: Если
true
, найдите примечание с совпадающимid
, которое находится в параметре URL-адреса. - Строка 13: Наконец, верните данные в формате JSON.
- Строка 16: Проверить, был ли выполнен запрос
PUT
. - Строка 17: Найдите документ с совпадающим
id
и затем обновите его. - Строка 22: Обновите поля
title
иbody
значениями, введенными пользователем. - Строка 26: Завершить ответ.
Теперь напишем наш интерфейс. В папке /pages/notes
создайте еще один каталог с именем update
. Здесь создайте файл с именем [id].js
.
В /pages/notes/update/[id].js
напишите следующий код:
import { useRouter } from "next/router"; | |
export default function Update({ data }) { | |
const router = useRouter(); | |
const { id } = router.query; | |
const updateNote = async (event) => { | |
event.preventDefault(); | |
const res = await fetch(`/api/notes/update/${id}`, { | |
body: JSON.stringify({ | |
title: event.target.title.value, | |
body: event.target.body.value, | |
}), | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
method: "PUT", | |
}); | |
router.push("/dashboard"); | |
}; | |
} |
- Строка 3: Используйте опору
data
в нашем компоненте. Он будет содержать данные с сервера. - Строка 5: извлеките параметр
id
из URL-адреса. - Строка 7: Создайте функцию
updateNote
. - Строка 10: Выполните
PUT
запрос кapi/notes/update
маршруту и отправьте наши входные поляtitle
иbody
в качестве полезной нагрузки. - Строка 20: По завершении запроса перенаправьте пользователя на страницу
/dashboard
.
Когда это будет сделано, добавьте в тот же файл следующий код:
return ( | |
<div> | |
{data.error ? ( | |
<p>{data.error}</p> | |
) : ( | |
<form onSubmit={updateNote}> | |
<label htmlFor="name">Name</label> | |
<input | |
id="title" | |
name="title" | |
type="text" | |
defaultValue={data.note.title} | |
autoComplete="name" | |
/> | |
<input | |
id="body" | |
name="body" | |
type="text" | |
defaultValue={data.note.body} | |
autoComplete="false" | |
/> | |
<button type="submit">Update</button> | |
</form> | |
)} | |
</div> | |
); | |
} |
- Строка 6: После отправки формы запустите функцию
updateNote
. - Строки 8-13: Создаем наш
input
элемент. Его начальным значением будет полеtitle
нашей заметки. - Строки 15-20: создайте наш
input
элемент, который будет содержать полеbody
примечания в качестве значения по умолчанию.
В качестве последнего шага нам нужно указать Next.js получить данные. Добавьте в этот файл следующий код:
export async function getServerSideProps(context) { | |
const hostname = "http://localhost:3000"; | |
const options = { headers: { cookie: context.req.headers.cookie } }; | |
const res = await fetch( | |
`${hostname}/api/notes/update/${context.params.id}`, | |
options | |
); | |
const json = await res.json(); | |
return { | |
props: { | |
data: json, | |
}, | |
}; | |
} |
Этот код функции идентичен коду файла /pages/notes/[id].js
.
Запустите код. Это будет результат:
Вуаля! Результат оказался ожидаемым. Давайте теперь поработаем над удалением нашей записи.
В итоге /pages/notes/update/[id].js
должен выглядеть так:
import { useRouter } from "next/router"; | |
export default function Update({ data }) { | |
const router = useRouter(); | |
const { id } = router.query; | |
const updateNote = async (event) => { | |
event.preventDefault(); | |
const res = await fetch(`/api/notes/update/${id}`, { | |
body: JSON.stringify({ | |
title: event.target.title.value, | |
body: event.target.body.value, | |
}), | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
method: "PUT", | |
}); | |
router.push("/dashboard"); | |
}; | |
return ( | |
<div> | |
{data.error ? ( | |
<p>{data.error}</p> | |
) : ( | |
<form onSubmit={updateNote}> | |
<label htmlFor="name">Name</label> | |
<input | |
id="title" | |
name="title" | |
type="text" | |
defaultValue={data.note.title} | |
autoComplete="name" | |
/> | |
<input | |
id="body" | |
name="body" | |
type="text" | |
defaultValue={data.note.body} | |
autoComplete="false" | |
/> | |
<button type="submit">Update</button> | |
</form> | |
)} | |
</div> | |
); | |
} | |
export async function getServerSideProps(context) { | |
const hostname = "http://localhost:3000"; | |
const options = { headers: { cookie: context.req.headers.cookie } }; | |
const res = await fetch( | |
`${hostname}/api/notes/update/${context.params.id}`, | |
options | |
); | |
const json = await res.json(); | |
return { | |
props: { | |
data: json, | |
}, | |
}; | |
} |
Удаление записи
В папке /pages/api/notes
создайте каталог с именем delete
. Здесь создайте файл с именем [id].js
.
В /pages/api/notes/delete/[id].js
напишите следующий код:
import { getSession } from "next-auth/client"; | |
import Note from "../../../../models/Note"; | |
import dbConnect from "../../../../helpers/dbConnect"; | |
export default async (req, res) => { | |
await dbConnect(); | |
const session = await getSession({ req }); | |
if (!session) { | |
return res.json({ error: "not logged in" }); | |
} else { | |
await Note.findOneAndDelete( | |
{ | |
_id: req.query.id, | |
user: session.id, | |
}, | |
function (err, docs) { | |
if (err) console.log(err); | |
} | |
); | |
res.end(); | |
} | |
}; |
- Строка 11: Найдите заметку с совпадающими полями
id
иuser
. Если нашел, то удалите. - Строка 20: Завершить ответ.
Запустите код. Должен быть результат:
Готово! Результат оказался ожидаемым.
Дополнительные ресурсы
Репозиторий GitHub
Другой учебный материал
- Аутентификация с помощью NextJS-LogRocket
- Аутентификация NextJS с NextAuth - Джеймс Перкинс
- Создание клона Netflix с помощью Next.Js и Next Auth - Ли Робинсон
Заключение
NextAuth.js упростил разработчикам создание функций аутентификации пользователей в своих приложениях. Как следствие, это улучшает опыт разработчика. Более того, нам не нужно было создавать внешний сервер Express для построения нашей системы входа в систему. Все было сделано в Next.js, что обещало более быстрое и производительное приложение.
Если вы столкнулись с какими-либо проблемами, советую разобрать программу и поиграться с кодом. Спасибо, что дожили до конца!