Йоу! Как дела? Помните прошлую статью, где мы использовали Prisma с Next.js, сделали CLI и добавили функцию аутентификации? Я заметил, что многие из вас узнали много нового! Так вот! Вы узнаете еще больше, включая создание приложения для списка дел в виде CLI с Prisma и Next.js API для обеспечения наилучшей функциональности приложения!
Подожди, Омар? Зачем нам нужен Prisma/Next.js? Причина в том, что это приложение позволяет синхронизировать ваши задачи из любой точки мира!
Не теряя времени, давайте приступим.
Создание нашего API
Прежде всего, нам нужна база данных, поэтому давайте воспользуемся PlanetScale! Зайдите на сайт planetscale.com и нажмите Get Started, создайте свою базу данных и подождите, пока она будет готова к работе!
Теперь запустите терминал и начните новый проект Next.js с помощью npx
, после чего смените директорию:
npx create-next-app
cd to-do-list-app
Теперь, после того как мы создали наш проект, давайте добавим Prisma и подготовим его, поэтому выполните следующую команду в терминале:
yarn add prisma @prisma/client
yarn prisma init
После этого мы получим файл schema.prisma
в папке prisma
, замените его на следующий:
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
referentialIntegrity = "prisma"
}
Итак, самое главное, что мы только что изменили поставщика базы данных на mysql
, поскольку мы используем базу данных MySQL.
Теперь на приборной панели PlanetScale нажмите на Connect и выберите Prisma в качестве опции для подключения, скопируйте предоставленный файл .env
и создайте файл .env
в вашем проекте и вставьте то, что вы только что скопировали.
Теперь мы создадим 2 модели, User
и Task
, поэтому добавьте эти 2 модели в файл схемы Prisma:
model User {
id Int @id @default(autoincrement())
username String @unique
password String
token String @unique
}
model Task {
id Int @id @default(autoincrement())
title String
userToken String
}
Теперь ваш файл схемы должен выглядеть следующим образом:
generator client {
provider = "prisma-client-js"
previewFeatures = ["referentialIntegrity"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
referentialIntegrity = "prisma"
}
model User {
id Int @id @default(autoincrement())
username String @unique
password String
token String @unique
}
model Task {
id Int @id @default(autoincrement())
title String
userToken String
}
Теперь запустите yarn prisma db push
, чтобы синхронизировать вашу базу данных MySQL PlanetScale.
Теперь нам нужно создать наши API файлы, сначала удалите файл pages/api/hello.js
и создайте файл register.js
в папке pages/api
и вставьте следующий код:
import { PrismaClient } from '@prisma/client';
export default function handler(req, res) {
const prisma = new PrismaClient();
const { username, password, token } = req.body;
if (!username || !password || !token) {
res.status(400).json({
message: 'Missing username, password or token'
});
return;
}
prisma.user.findUnique({
where: {
username: username
}
}).then(user => {
if (user) {
res.status(400).json({
message: 'Username already taken'
});
} else {
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
res.status(400).json({
message: 'Token already taken'
});
} else {
prisma.user.create({
data: {
username: username,
password: password,
token: token
}
}).then(user => {
res.json({
message: "User created successfully",
user: user
});
});
}
}
);
}
});
}
Здесь мы определяем клиента Prisma и создаем переменные для username
, password
и token
. Если один из параметров не указан, мы возвращаем ошибку:
const prisma = new PrismaClient();
const { username, password, token } = req.body;
if (!username || !password || !token) {
res.status(400).json({
message: 'Missing username, password or token'
});
return;
}
А для регистрации мы сначала проверяем, используется ли имя пользователя, если да, то возвращаем ошибку, если нет, то проверяем, используется ли токен, если да, то возвращаем ошибку, как обычно, если нет, то переходим к регистрации:
prisma.user.findUnique({
where: {
username: username
}
}).then(user => {
if (user) {
res.status(400).json({
message: 'Username already taken'
});
} else {
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
res.status(400).json({
message: 'Token already taken'
});
} else {
prisma.user.create({
data: {
username: username,
password: password,
token: token
}
}).then(user => {
res.json({
message: "User created successfully",
user: user
});
});
}
}
);
}
});
Теперь у нас есть пользователь, который должен иметь возможность аутентификации/логина, создайте файл login.js
и вставьте следующее:
import { PrismaClient } from '@prisma/client';
export default function handler(req, res) {
const prisma = new PrismaClient();
const { username, password } = req.body;
if (!username || !password) {
res.status(400).json({
message: 'Missing username or password'
});
return;
}
prisma.user.findUnique({
where: {
username: username
}
}).then(user => {
if (user) {
if (user.password === password) {
res.json({
message: 'User authenticated successfully',
userToken: user.token,
user: username
});
} else {
res.status(400).json({
message: 'Incorrect password'
});
}
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
}
Как обычно, мы определяем клиента Prisma и проверяем, если один из параметров (username
или password
) отсутствует, и если да, то мы вернем ошибку:
const prisma = new PrismaClient();
const { username, password } = req.body;
if (!username || !password) {
res.status(400).json({
message: 'Missing username or password'
});
return;
}
Затем мы используем функцию findUnique
, чтобы использовать имя пользователя
, которое является уникальным для поиска записи/пользователя в таблице, поэтому если мы не нашли пользователя, мы возвращаем ошибку, если мы нашли пользователя с подходящим именем пользователя, мы проверяем, совпадает ли предоставленный пароль с именем пользователя, если пароль правильный, мы возвращаем токен, который является чем-то вроде случайного набора символов & цифр, который трудно угадать, что позволяет вам делать все, что угодно, включая работу с самим API (при отправке запросов вручную), если нет, мы возвращаем ошибку, что пароль неверен:
prisma.user.findUnique({
where: {
username: username
}
}).then(user => {
if (user) {
if (user.password === password) {
res.json({
message: 'User authenticated successfully',
userToken: user.token,
user: username
});
} else {
res.status(400).json({
message: 'Incorrect password'
});
}
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
Теперь давайте запустим fun…..! Создайте новый файл create.js
, который позволит пользователю создавать новые задания! Вставьте в файл следующий код:
import { PrismaClient } from '@prisma/client';
export default function handler(req, res) {
const prisma = new PrismaClient();
const { token, taskTitle } = req.body;
if (!token || !taskTitle) {
res.status(400).json({
message: 'Missing token or task title'
});
return;
}
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
prisma.task.create({
data: {
title: taskTitle,
userToken: token
}
}).then(task => {
res.json({
message: 'Task created successfully',
});
});
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
}
Мы просто пропускаем определение клиента Prisma, поэтому мы используем findUnique
, чтобы проверить действителен ли токен пользователя или нет, если нет, мы передаем ошибку, говоря, что такого пользователя нет, если пользователь существует, мы создаем задачу с заданным названием и с этим токеном, а затем возвращаем сообщение об успехе:
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
prisma.task.create({
data: {
title: taskTitle,
userToken: token
}
}).then(task => {
res.json({
message: 'Task created successfully',
});
});
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
Теперь, если мы предположим, что кто-то хочет удалить задачу? Создайте delete.js
и вставьте следующее:
import { PrismaClient } from '@prisma/client';
export default function handler(req, res) {
const prisma = new PrismaClient();
const { token, taskId } = req.body;
if (!token || !taskId) {
res.status(400).json({
message: 'Missing token or task id'
});
return;
}
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
prisma.task.findUnique({
where: {
id: taskId
}
}).then(task => {
if (task.userToken === token) {
prisma.task.delete({
where: {
id: taskId
}
}).then(task => {
res.json({
message: 'Task deleted successfully',
});
});
} else {
res.status(400).json({
message: 'Task does not belong to the user'
});
}
}
);
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
}
Итак, мы находим пользователя по токену, если мы не нашли пользователя, мы возвращаем ошибку, если мы нашли пользователя, мы находим задачу по ID, мы проверяем, совпадает ли владелец, если да, мы удаляем ее, если нет, мы возвращаем ошибку:
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
prisma.task.findUnique({
where: {
id: taskId
}
}).then(task => {
if (task.userToken === token) {
prisma.task.delete({
where: {
id: taskId
}
}).then(task => {
res.json({
message: 'Task deleted successfully',
});
});
} else {
res.status(400).json({
message: 'Task does not belong to the user'
});
}
}
);
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
И последний, но не менее важный файл view.js
, который позволяет пользователю просматривать задания:
import { PrismaClient } from '@prisma/client';
export default function handler(req, res) {
const prisma = new PrismaClient();
const { token } = req.body;
if (!token) {
res.status(400).json({
message: 'Missing token'
});
return;
}
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
prisma.task.findMany({
where: {
userToken: token
}
}).then(tasks => {
res.json({
message: 'Tasks retrieved successfully',
tasks: tasks
});
});
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
}
Итак, что мы делаем здесь: ищем пользователя, если он не найден, возвращаем ошибку, если пользователь найден, возвращаем все задачи, соответствующие пользователю:
prisma.user.findUnique({
where: {
token: token
}
}).then(user => {
if (user) {
prisma.task.findMany({
where: {
userToken: token
}
}).then(tasks => {
res.json({
message: 'Tasks retrieved successfully',
tasks: tasks
});
});
} else {
res.status(400).json({
message: 'No user found'
});
}
}
);
Теперь, после тяжелой работы мы получили готовый API 🥳!
Создание нашего CLI
Для начала работы выполните следующие команды:
mkdir todocli
cd todocli
npm init -y
Теперь создайте файл index.js
и вставьте в него следующее:
#!/usr/bin/env node
var args = process.argv.slice(2);
var fs = require('fs');
var request = require('request');
command = args[0];
if (command == 'login') {
options = {
url: 'http://localhost:3000/api/login',
method: 'POST',
json: {
username: args[1],
password: args[2]
}
};
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body.message);
fs.writeFile('./token.txt', body.userToken, function (err) {
if (err) {
return console.log(err);
}
console.log("Token saved successfully!");
}
);
} else {
console.log(body.message);
}
});
} else if (command == 'register') {
options = {
url: 'http://localhost:3000/api/register',
method: 'POST',
json: {
username: args[1],
password: args[2],
token: Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
}
};
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body.message);
// save token
fs.writeFile('./token.txt', body.user.token, function (err) {
if (err) {
return console.log(err);
}
console.log("Token saved successfully!");
}
);
} else if (body.message=="Token already taken") {
console.log("Please try registering again!")
} else {
console.log(body.message);
}
});
} else if (command == 'create') {
options = {
url: 'http://localhost:3000/api/create',
method: 'POST',
json: {
taskTitle: args.slice(1).join(' '),
token: fs.readFileSync('./token.txt', 'utf8')
}
};
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body.message);
} else {
console.log(body.message);
}
});
} else if (command == 'view') {
options = {
url: 'http://localhost:3000/api/view',
method: 'POST',
json: {
token: fs.readFileSync('./token.txt', 'utf8')
}
};
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
// save the title and id of each task in an array
var tasks = [];
for (var i = 0; i < body.tasks.length; i++) {
tasks.push({
id: body.tasks[i].id,
title: body.tasks[i].title
});
}
// print the tasks along with their ids
console.log("Your tasks are:");
for (var i = 0; i < tasks.length; i++) {
console.log(tasks[i].id + ": " + tasks[i].title);
}
} else {
console.log(body.message);
}
});
} else if (command == 'delete') {
options = {
url: 'http://localhost:3000/api/delete',
method: 'POST',
json: {
taskId: args[1],
token: fs.readFileSync('./token.txt', 'utf8')
}
};
request(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body.message);
} else {
console.log(body.message);
}
});
} else if (command == 'help') {
console.log("Usage: todocli <command> <arguments>");
console.log("Commands:");
console.log("login <username> <password>");
console.log("register <username> <password>");
console.log("create <task title>");
console.log("view");
console.log("delete <task id>");
console.log("help");
}
Просто то, что мы в основном делаем, это получение параметров и зависимости от API, а токен сохраняется в локальном файле txt
.
Теперь установите необходимый пакет с помощью npm install request
.
Теперь отредактируйте ваш package.json
и вместо scripts
вставьте следующее:
"scripts": {
"start": "node index.js"
},
"bin": {
"todocli": "index.js"
}
Теперь ваш package.json
должен выглядеть следующим образом:
{
"name": "todocli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"bin": {
"todocli": "index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"request": "^2.88.2"
}
}
Теперь мы закончили, выполните npm install . -g
и начните использовать приложение!
Протестируйте его — установка не требуется
Просто установите to-do-cli-prisma
с помощью команды:
npm i to-do-cli-prisma
И проверьте, как им пользоваться, выполнив команду: todocli help
.
Важные ссылки 🔗
- GitHub Repo — Omar8345/to-do-list-app-cli
- npm Package — to-do-cli-prisma
Спасибо за прочтение этой статьи и надеемся, что вы узнали что-то полезное!