Prisma — ORM нового поколения

Требования:

  • Базовые знания REST API
  • Базовые знания SQL
  • Node.js
  • клиент npm по вашему выбору.

В этом руководстве мы рассмотрим, как сделать REST API TODO App с помощью TypeScript, Prisma и esbuild.

Вам не нужны предварительные знания о какой-либо из этих технологий.

Что такое Призма?

Это ORM нового поколения с поддержкой TypeScript и инструментами, облегчающими нашу жизнь:

  • Prisma Client: безопасный с точки зрения типов, автоматически генерируемый конструктор запросов, который адаптируется к вашим данным.
  • Миграции: Инструмент для создания и управления миграциями баз данных.
  • Prisma Studio: GUI-клиент для вашей базы данных.

👀 Чтобы вы не заблудились в том, куда все идет, или если вы просто зашли посмотреть, я оставляю вам код в этом репозитории.

Давайте начнем 🧑💻

Если вы используете VSCode, вы можете использовать официальный плагин Prisma.

Давайте установим наши зависимости

# yarn
$ yarn add -D prisma esbuild @types/node
$ yarn add @prisma/client

# npm
$ npm i -D prisma esbuild @types/node
$ npm i @prisma/client 
Войдите в полноэкранный режим Выход из полноэкранного режима

Давайте поднимем нашу рабочую среду

# yarn
$ yarn prisma init

# npm
$ npx prisma init
Войдите в полноэкранный режим Выход из полноэкранного режима

Давайте обновим наши переменные окружения. В этом учебнике мы будем использовать SQLite, но вы можете проверить, какие из них поддерживаются Prisma.

# .env
DATABASE_URL="file:./database.sqlite"
Войдите в полноэкранный режим Выход из полноэкранного режима

Давайте обновим наш файл schema.prisma

// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite" // <- usemos SQLite
  url      = env("DATABASE_URL")
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Мы создаем наш конструктор с помощью esbuild для работы с TypeScript.

Если вы не знаете, как это работает, возможно, вам стоит потратить время на чтение моего блога esbuild — Безболезненная разработка.

// esbuild.dev.js
import { spawn } from 'child_process'
import esbuild from 'esbuild'

let server;

const startServer = (message) => {
    if (server) server.kill('SIGINT')
    server = spawn('node', ['./dist/index.mjs'], { stdio: 'inherit' })
    console.log(`n${message}n`)
}

esbuild.build({
    entryPoints: ['./src/index.ts'],
    watch: { onRebuild: (err) => !err && startServer('Rebuilded') },
    bundle: true,
    minify: true,
    platform: 'node',
    format: 'esm',
    target: ['esnext'],
    external: ['/node_modules/*'],
    outfile: './dist/index.mjs',
})
    .then(() => startServer('Done 🚀'))
    .catch(() => process.exit(1))
Войдите в полноэкранный режим Выход из полноэкранного режима

Давайте обновим наш package.json.

// package.json
{
  "name": "prisma",
  "version": "1.0.0",
  "main": "index.js",
  "author": "Ushieru Kokoran (https://ushieru.com/)",
  "license": "MIT",
  "type": "module", // <- soporte a mjs
  "scripts": {
    "dev": "node esbuild.dev.js" // <- script para desarrollo
  },
  "dependencies": {
    "@prisma/client": "^4.1.0",
    "@types/koa": "^2.13.5",
    "@types/node": "^18.0.6",
    "koa": "^2.13.4"
  },
  "devDependencies": {
    "esbuild": "^0.14.49",
    "prisma": "^4.1.0"
  }
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Давайте создадим нашу модель задачи. Если вы используете БД, отличную от SQLite, вы можете использовать перечисления в статусе (defining-enums).

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

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

model Task {
  id          String   @id @default(cuid())
  name        String
  description String
  status      String   @default("OPEN") // OPEN, IN_PROGRESS, DONE
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Теперь нам нужно загрузить наши модели в базу данных.

# yarn
$ yarn prisma db push

# npm
$ npx prisma db push
Войдите в полноэкранный режим Выход из полноэкранного режима

Создать клиента Prisma…? Именно так, prisma генерирует типы объектов из таблиц базы данных «на лету». Возможно, что VSCode не замечает этого изменения, поэтому нам нужно перезапустить сервер TypeScript.
Для этого откройте палитру команд и выполните команду: TypeScript: Перезагрузите сервер TS.

Давайте создадим клиента prism для доступа к нашему ORM и его соответствующим CRUD (Create, Read, Update, Delete).

// src/crudTask.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Tenemos acceso a nuestro modelo task que es un mapeo exacto
// a nuestra tabla en la base de datos. Y con TypeScript tenemos
// el autocompletado para no cometer errores.

export const createTask = async (name: string, description: string) => {
    return await prisma.task.create({
        data: { name, description }
    })
}

export const readTasks = async () => {
    return await prisma.task.findMany()
}

export const readOneTask = async (id: string) => {
    return await prisma.task.findUnique({ where: { id } })
}

export const updateTask = async (id: string, name: string, description: string, status: 'OPEN' | 'IN_PROGRESS' | 'DONE') => {
    return await prisma.task.update({
        data: { name, description, status },
        where: { id }
    })
}

export const deleteTask = async (id: string) => {
    return await prisma.task.delete({ where: { id } })
}
Войдите в полноэкранный режим Выход из полноэкранного режима

И это все 🎉🎉 Вы можете обернуть его в любой бэкенд, который вы хотите, в src/index.ts.

Если вы используете Next.js, вам придется внести некоторые изменения в объявление клиента. Вот док.

Я показываю вам этот маленький пример с Коа:

// src/index.ts
import Koa from 'koa';
import Router from 'koa-router';
import koaBody from 'koa-body';
import { createTask, readTasks, readOneTask, updateTask, deleteTask } from './crudTask'

const app = new Koa();

app.use(koaBody({ jsonLimit: '5kb' }));

const router = new Router();

router.get('/', (ctx) => ctx.body = 'Hello World');

router.post('/tasks', async (ctx) =>
    ctx.body = await createTask(ctx.request.body.name, ctx.request.body.description));

router.get('/tasks', async (ctx) =>
    ctx.body = await readTasks());

router.get('/tasks/:id', async (ctx) =>
    ctx.body = await readOneTask(ctx.params.id));

router.put('/tasks/:id', async (ctx) =>
    ctx.body = await updateTask(
        ctx.params.id,
        ctx.request.body.name,
        ctx.request.body.description,
        ctx.request.body.status
    ));

router.delete('/tasks/:id', async (ctx) =>
    ctx.body = await deleteTask(ctx.params.id));

app.use(router.routes());

app.listen(3000);
Войдите в полноэкранный режим Выход из полноэкранного режима

И это все, давайте посмотрим пример с Отношениями? расскажите мне в комментариях.

И счастливого хакинга 🎉👨💻

Оцените статью
devanswers.ru
Добавить комментарий