برنامه نویسی

با استفاده از BunJs و Prisma – DEV یک سرور HTTP بسازید

توضیحات تصویر
در این راهنما، ما از دو ابزار قدرتمند استفاده خواهیم کرد: BunJs و Prisma. آنها با هم، پایه ای قوی برای ساخت وب سرورهای مدرن، مقیاس پذیر و کارآمد فراهم می کنند. اما قبل از اینکه به جزئیات فنی بپردازیم، اجازه دهید لحظه ای را دریابیم که BunJs و Prisma چه چیزی را به میز می آورند.چرا از BunJs استفاده کنیم؟

BunJs یک چارچوب HTTP بسیار سبک، سریع و بسیار قابل تنظیم برای Node.js است. این به ساده سازی فرآیند ایجاد وب سرور کمک می کند. از سوی دیگر، با Prisma، به لطف ویژگی‌های قدرتمند آن مانند تکمیل خودکار و بررسی نوع، تعامل با پایگاه‌های داده بصری‌تر و کمتر مستعد خطا می‌شود.

راه اندازی پروژه ما

ابتدا بیایید جعبه ابزار Bun را با دستور زیر نصب کنیم: –

npm install -g bun
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا بیایید دایرکتوری پروژه خود را ایجاد کنیم: –

├── controller/
│   ├── comments.controller.ts
│   ├── post.controller.ts
│   └── user.controller.ts
├── prisma/
│   └── schema.prisma
├── services/
│   ├── auth.service.ts
│   ├── comment.service.ts
│   ├── post.service.ts
│   └── user.service.ts
├── docker-compose.yml
├── index.ts
├── package*.json
└── tsconfig.json

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در پوشه کنترلرها، مسیرها و توابع خود را در پوشه خدمات تعریف می کنیم، schema.prisma شامل مدل ما و همچنین تنظیمات برای اجرای Prisma-ORM خواهد بود. Prisma انواع مختلفی از پایگاه های داده مبتنی بر SQL را پشتیبانی می کند و برای این وبلاگ ما از PostgresQL با نمونه docker استفاده می کنیم.

ساخت خدمات

ابتدا باید تمام بسته ها و وابستگی های مورد نیاز را نصب کنیم:

bun add -d prisma @types/jsonwebtoken bun-types
bun add pg jsonwebtoken elysia dotenv axios @prisma/client @elysia/cookie
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما prisma را نصب کرده ایم، باید فایل schema.prisma را ایجاد کنیم، جایی که مدل های طرح ما تعریف می شوند.

bunx init prisma
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

bux شبیه npx یا pnpx است. هدف اصلی bux تسهیل اجرای بسته‌هایی است که در بخش Dependencies یا devDependencies فایل package.json پروژه فهرست شده‌اند. به جای نصب دستی این بسته ها به صورت سراسری یا محلی، می توانید از bux برای اجرای مستقیم آنها استفاده کنید.

اکنون طرح کاربر را در داخل فایل prisma/schema.prisma ایجاد کنید.

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  password  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در ادامه قصد داریم auth.services.ts خود را تعریف کنیم که پس از احراز هویت کاربر اجازه انجام عملیات CRUD را می دهد.

//auth.service.ts
import jwt from "jsonwebtoken";

export const verifyToken = (token: string) => {
  let payload: any;

  //Verify the JWT token
  jwt.verify(token, process.env.JWT_SECRET as string, (error, decoded) => {
    if (error) {
      throw new Error("Invalid token");
    }

    payload = decoded;
  });

  return payload;
};

export const signUserToken = (data: { id: number; email: string }) => {
  //Sign the JWT token
  const token = jwt.sign(
    {
      id: data.id,
      email: data.email,
    },
    process.env.JWT_SECRET as string,
    { expiresIn: "1d" }
  );

  return token;
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تابع verifyToken()، که یک رشته توکن JWT را به عنوان آرگومان می گیرد. در داخل تابع، jwt.verify را برای تأیید توکن فراخوانی می کند. تابع jwt.verify رمزگشایی می کند و امضای آن را با استفاده از راز ارائه شده تأیید می کند.

اگر در حین تأیید خطایی وجود داشته باشد، خطایی نشان می دهد که نشانه نامعتبر است.

اگر تأیید موفقیت آمیز باشد، بار رمزگشایی شده را به متغیر payload اختصاص می دهد و آن را برمی گرداند.

تابع signUserToken() یک توکن JWT برای داده های کاربر معین تولید می کند. از jwt.sign برای تولید یک توکن جدید JWT استفاده می کند. بار توکن حاوی شناسه و ایمیل کاربر است. از رمز JWT ذخیره شده در متغیر محیط process.env.JWT_SECRET برای امضای توکن استفاده می کند، انقضای این توکن ها به طور پیش فرض روی 1D تنظیم شده است.

با مکانیزم احراز هویت ما آماده است، اکنون باید user.service.ts را ایجاد کنیم

//user.service.ts
import { prisma } from "../index";
import { signUserToken } from "./auth.service";

export const createNewUser = async (data: {
  name: string;
  email: string;
  password: string;
}) => {
  try {
    const { name, email, password } = data;

    //Hash the password using the Bun package and bcrypt algorithm
    const hashedPassword = await Bun.password.hash(password, {
      algorithm: "bcrypt",
    });

    //Create the user
    const user = await prisma.user.create({
      data: {
        name,
        email,
        password: hashedPassword,
      },
    });

    return user;
  } catch (error) {
    throw error;
  }
};

export const login = async (data: { email: string; password: string }) => {
  try {
    const { email, password } = data;

    //Find the user
    const user = await prisma.user.findUnique({
      where: {
        email,
      },
    });

    if (!user) {
      throw new Error("User not found");
    }

    //Verify the password
    const valid = await Bun.password.verify(password, user.password);

    if (!valid) {
      throw new Error("Invalid credentials");
    }

    // //Sign the JWT token
    const token = signUserToken({
      id: user.id,
      email: user.email,
    });

    return {
      message: "User logged in successfully",
      token,
    };
  } catch (error) {
    throw error;
  }
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا ما دو تابع داریم: – createNewUser و login. تابع createNewuser داده های کاربر از جمله نام، ایمیل و رمز عبور را به عنوان ورودی دریافت می کند و رمز عبور ارائه شده را با استفاده از بسته BunJs و الگوریتم bcrypt برای ذخیره سازی ایمن هش می کند.

سپس سعی می کند با استفاده از روش user.create در Prisma ORM یک کاربر جدید در پایگاه داده ایجاد کند و رمز عبور هش شده را به همراه نام و ایمیل ارائه دهد. اگر ایجاد کاربر با موفقیت انجام شود، شی کاربر ایجاد شده را برمی گرداند، در غیر این صورت خطا را پرتاب می کند.

در حالی که، تابع ورود سعی می کند کاربر را با ایمیل ارائه شده با استفاده از روش user.findUnique Prisma-ORM پیدا کند. اگر کاربر پیدا نشود، یک خطا نشان می دهد که کاربر وجود ندارد. اگر کاربر وجود داشته باشد، پسورد ارائه شده را در برابر رمز عبور هش شده ذخیره شده در پایگاه داده با استفاده از روش password.verify بسته BunJs تأیید می کند. پس از تأیید موفقیت آمیز، یک توکن JWT با استفاده از تابع signUserToken از auth.service تولید می کند و شناسه و ایمیل کاربر را ارسال می کند. و در نهایت، یک پیام موفقیت آمیز به همراه توکن JWT تولید شده برمی گرداند.

اکنون با ثبت نام کاربر ما و ورود به سیستم با مکانیزم احراز هویت یکپارچه شده و آماده است، ما به ایجاد فایل مسیر خود در controller/user.controller.ts ادامه خواهیم داد:-

//user.controller.ts
import Elysia from "elysia";
import { createNewUser, login } from "../services/user.service";
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما کتابخانه Elysia را برای راه‌اندازی مسیرها و رسیدگی به درخواست‌های HTTP وارد می‌کنیم. همچنین توابع createNewUser و login از ماژول user.service برای رسیدگی به عملکردهای ثبت نام و ورود کاربر به ترتیب.

app.post("/signup", async (context) => {
  try {
    const userData: any = context.body;

    const newUser = await createNewUser({
      name: userData.name,
      email: userData.email,
      password: userData.password,
    });

    return {
      user: newUser,
    };
  } catch (error: any) {
    return {
      error: error.message,
    };
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

/signup یک مسیر POST است که اطلاعات کاربر را در بدنه درخواست انتظار دارد. در داخل هدایتگر مسیر، داده های کاربر را از بدنه درخواست استخراج می کند. و سپس تابع createNewUser را از user.service.ts فراخوانی می کند و داده های کاربر استخراج شده را ارسال می کند.

اگر ایجاد کاربر موفقیت آمیز باشد، شی کاربر ایجاد شده را برمی گرداند.

اگر در حین فرآیند خطایی رخ دهد، خطا را می گیرد، پیام خطا را استخراج می کند و آن را برمی گرداند.

پس از ثبت نام، مسیر بعدی / ورود به سیستم می آید:-

app.post("/login", async (context) => {
  try {
    const userData: any = context.body;

    const loggedInUser = await login({
      email: userData.email,
      password: userData.password,
    });

    return loggedInUser;
  } catch (error: any) {
    console.log(error);
    return {
      error: error.message,
    };
  }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما تابع ورود از سرویس کاربر را فراخوانی می کنیم و داده های کاربر استخراج شده را ارسال می کنیم.

اگر ورود موفقیت آمیز باشد، شیء کاربر وارد شده را به همراه یک توکن JWT برمی گرداند.

اگر در حین فرآیند ورود خطایی رخ دهد، خطا را دریافت می کند، آن را در کنسول ثبت می کند، پیام خطا را استخراج می کند و آن را برمی گرداند.

اکنون همه چیز را در جای خود و آماده اجرا داریم، اما قبل از آن به فایل index.ts و docker-compose.yml نیاز داریم. فایل docker-compose برای ایجاد و اجرای نمونه postgres محتوا خواهد داشت:

version: '3.9'
services:
    postgres:
        image: postgres:latest
        restart: always
        environment:
          - POSTGRES_DB=postgres
          - POSTGRES_USER=postgres
          - POSTGRES_PASSWORD=password
        ports:
          - '5432:5432'
        volumes:
          - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql
        networks:
          - keploy-network

networks:
  keploy-network:
    external: true
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و فایل index.ts ما می‌خواهد:

//index.ts
import Elysia from "elysia";
import { PrismaClient } from "@prisma/client";
import { userController } from "./controllers/user.controller";

//Create instances of prisma and Elysia
const prisma = new PrismaClient();
const app = new Elysia();

//Use controllers as middleware
app.use(userController as any);

//Listen for traffic
app.listen(4040, () => {
  console.log("🦊 Elysia is running at localhost:4040");
});

export { app, prisma };
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فایل index.ts به عنوان نقطه ورودی عمل می کند که در آن نمونه های سرور و پایگاه داده مقداردهی اولیه می شوند، کنترلرها ثبت می شوند و سرور شروع به گوش دادن به درخواست های دریافتی می کند. تنظیم برنامه را هماهنگ می کند و نمونه های لازم را برای استفاده در ماژول های دیگر صادر می کند.

بیایید سرور را راه اندازی کنیم

bun run dev
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

🦊 Elysia is running at localhost:4040
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه

ما باید یاد بگیریم که چگونه BunJs ایجاد سرور را ساده می کند، در حالی که Prisma تعاملات پایگاه داده را ساده می کند. در این وبلاگ، نحوه ایجاد سرور BunJs ساده با احراز هویت با استفاده از توکن‌های JWT را یاد گرفتیم، مسیرهای ثبت نام و ورود کاربر را با استفاده از Elysia ایجاد کردیم.

در قسمت بعدی این وبلاگ، برنامه خود را با استفاده از bun-test، cucumber و keploy تست می کنیم و یاد می گیریم که از کدام یک بهتر است استفاده کنیم.

سوالات متداول

چرا از توکن های JWT برای احراز هویت استفاده کنیم؟

توکن‌های JWT راهی امن برای احراز هویت کاربران در برنامه‌های کاربردی وب فراهم می‌کنند. آنها بدون حالت هستند، به این معنی که نیازی به ذخیره هیچ جلسه ای در سرور نیست و می توان آنها را به راحتی در سرویس های مختلف به اشتراک گذاشت. علاوه بر این، آنها می‌توانند حاوی ادعاهای سفارشی باشند و به توسعه‌دهندگان این امکان را می‌دهند که اطلاعات بیشتری را به بار توکن اضافه کنند.

Bunx چیست و چه تفاوتی با سایر ابزارهای اجرای بسته دارد؟

Bunx یک ابزار اجرای بسته مشابه npx یا pnpx است. هدف اصلی آن تسهیل اجرای بسته های فهرست شده در بخش وابستگی ها یا devDependencies پروژه بدون نیاز به نصب دستی است. Bunx مدیریت وابستگی را با اجرای بسته ها به طور مستقیم از متن پروژه ساده می کند.

در قسمت بعدی وبلاگ به چه ابزارهای تستی پرداخته خواهد شد؟

در قسمت بعدی وبلاگ، روش‌های تست را با استفاده از Keploy، bun test و CucumberJs بررسی خواهیم کرد. این ابزارها رویکردهای مختلفی را برای آزمایش برنامه های کاربردی ارائه می دهند و ما در مورد نقاط قوت و موارد استفاده آنها صحبت خواهیم کرد.

آیا می توانم به BunJs و Prisma کمک کنم؟

هر دو BunJs و Prisma پروژه های منبع باز هستند و از مشارکت پذیرفته می شود! می‌توانید با ارسال گزارش‌های اشکال، درخواست‌های ویژگی، یا حتی مشارکت کد از طریق مخازن مربوطه GitHub مشارکت کنید. برای کسب اطلاعات بیشتر در مورد نحوه مشارکت، حتماً دستورالعمل‌های مشارکت آنها را دنبال کنید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا