Хранение данных в Deno: Prisma, Mongoose, Apollo и многое другое теперь доступно через npm +6


Хранимые данные (persistent data) являются неотъемлемой частью современных веб-приложений. Они нужны нам для сохранения информации о пользователях, предоставления платежной информации и многого-многого другого. Теперь вы можете подключать свои любимые технологии хранения данных (Prisma, Mongoose, MySQL и другие) через npm и в Deno!

Этот пост продемонстрирует вам, как быстро начать работу в Deno со следующими npm-модулями:

Больше руководств по началу работы с npm-модулями для хранения данных вы можете найти в нашем мануале.

Повышенная безопасность хранения данных с Deno

Атаки через цепочку поставок (supply chain attacks) — известная проблема безопасности в npm. Node устанавливает и запускает npm-модули с доступом ко всему по умолчанию, что позволяет всего одной вредоносной зависимости незаметно скомпрометировать миллионы конечных пользователей.

Хранилище данных подвержено еще большему риску атак через цепочку поставок, поскольку к конфиденциальным производственным данным, необходимым для ведения бизнеса, можно получить доступ через переменные окружения.

Модель явного предоставления разрешений Deno гарантирует, что вы точно знаете, к чему вашим зависимостям требуется доступ. Система разрешений достаточно детализирована, чтобы вы могли предоставить доступ к переменным окружения, файловой системе и даже FFI.

Prisma

Prisma, современная ORM, способная похвастаться непревзойденным опытом разработки, была одним из наших наиболее востребованных модулей. Ниже приведено краткое руководство по началу работы с Prisma в Deno.

В следующем разделе показано, как быстро подключить Prisma к Deno.

Вы можете изучить исходный код здесь или посмотреть видеоруководство на нашем YouTube-канале.

Настройка приложения

Давайте создадим папку deno-prisma и переместимся туда.

mkdir deno-prisma & cd deno-prisma

Затем запустим prisma init с помощью Deno:

deno run --allow-read --allow-env --allow-write npm:prisma@^4.5 init

Это сгенерирует prisma/schema.prisma. Давайте обновим его следующим образом:

generator client {
  provider = "prisma-client-js"
  previewFeatures = ["deno"]
  output = "../generated/client"
}

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

model Dinosaur {
  id          Int     @id @default(autoincrement())
  name        String  @unique
  description String
}

Prisma также должна была сгенерировать .env файл с DATABASE_URL. Давайте присвоим DATABASE_URL строку подключения PostreSQL. В этом примере мы будем использовать бесплатную базу данных PostgreSQL от Supabase.

После того, как мы обновили .env файл, нам нужно создадать схему базы данных в Prisma:

deno run npm:prisma@^4.5 db push

После этого нам нужно сгенерировать клиент Prisma для Data Proxy:

deno run npm:prisma@ ^4.5 generate --data-proxy

Настройка Prisma Data Platform

Чтобы использовать Prisma Data Platform, нам нужно создать и подключить репозиторий GitHub. Итак, давайте инициализируем локальный репозиторий, создадим новый репозиторий на GitHub, добавим удаленный источник и запушим локальный репозиторий.

После этого создайте бесплатную учетную запись Prisma Data Platform.

Кликните “New Project” и выберите “Import a Prisma Repository”.

Он запросит строку подключения PostgreSQL, которая должна быть в вашем .env файле. Скопируйте ее сюда. Затем кликните “Create Project”.

Вы получите новую строку подключения, начинающуюся с prisma://. Давайте присвоим ее DATABASE_URL в вашем .env файле, тем самым заменив нашу строку PostgreSQL из Supabase.

Использование Prisma и PrismaClient

Теперь вы сможете импортировать Prisma и PrismaClient в свой Deno-проект:

import { Prisma, PrismaClient } from "../generated/client/deno/edge.ts";

Далее вы можете заполнить свою базу данных, запросить данные и так далее.

Ознакомьтесь с нашим более подробным руководством по использованию Prisma или узнайте, как развернуть Prisma с помощью Deno Deploy.

Mongoose

Mongoose — популярная использующая схему библиотека, которая моделирует данные для MongoDB. Ниже приведено краткое руководство по началу работы с ней.

Вы можете изучить исходный код здесь или посмотреть видеоруководство на нашем YouTube-канале.

Определение модели

Давайте определим модель в Mongoose. В новый файл Dinosaur.ts добавим:

import { model, Schema } from "npm:mongoose@^6.7";

// Определение схемы.
const dinosaurSchema = new Schema({
  name: { type: String, unique: true },
  description: String,
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now },
});

// Проверки
dinosaurSchema.path("name").required(true, "Dinosaur name cannot be blank.");
dinosaurSchema.path("description").required(
  true,
  "Dinosaur description cannot be blank.",
);

// Экспорт модели.
export default model("Dinosaur", dinosaurSchema);

Создание нового динозавтра

Давайте создадим новый файл main.ts, который будет:

  • подключаться к MongoDB

  • создавать нового Dinosaur

  • извлекать динозавра с именем "Deno" 

  • console.log полученного динозавра

import mongoose from "npm:mongoose@^6.7";
import Dinosaur from "./model/Dinosaur.ts";

await mongoose.connect("mongodb://localhost:27017");

// Создание нового динозавра.
const deno = new Dinosaur({
  name: "Deno",
  description: "The fastest dinosaur ever lived.",
});

// Внедрение deno.
await deno.save();

// Поиск по имени Deno.
const denoFromMongoDb = await Dinosaur.findOne({ name: "Deno" });
console.log(
  `Finding Deno in MongoDB -- \n  ${denoFromMongoDb.name}: ${denoFromMongoDb.description}`,
);

Вывод должен быть следующим:

Finding Deno in MongoDB --

  Deno: The fastest dinosaur ever lived.

Отлично!

Ознакомьтесь с нашим более подробным руководством по работе с Mongoose.

Apollo

Apollo — это GraphQL-сервер, который можно настроить за считанные минуты и использовать с существующим источником данных (или REST API). Вы можете подключить к нему любой GraphQL-клиент, чтобы получить доступ к данным и воспользоваться всеми преимуществами проверки типов и эффективного фетчинга.

Давайте настроим и запустим простой сервер Apollo, который позволит нам запрашивать некоторые локальные данные. Для этого нам понадобятся только три файла:

  1. schema.ts, что настроить нашу модель данных,

  2. resolvers.ts для настройки того, как мы собираемся заполнять поля данных в нашей схеме

  3. и наш main.ts  где будет запускаться сервер.

И начнем мы с их создания:

touch schema.ts resolvers.ts main.ts

Давайте разберемся с настройкой каждого из них.

Посмотреть код можно здесь.

Определяем наши данные с помощью schema.ts

Файл schema.ts описывает наши данные. В данном случае наши данные представляют собой список динозавров. Мы хотим, чтобы наши пользователи могли получить имя и краткое описание каждого динозавра. На языке GraphQL это означает, что Dinosaur — это наш тип, а name (имя) и description (описание) — это наши поля. Мы также можем определить тип данных для каждого поля. В данном случае оба поля будут строками.

Здесь мы также описываем запросы, которые мы разрешаем для наших данных, используя специальный тип Query GraphQL. У нас будут два запроса:

  • dinosaurs, который получает список всех динозавров

  • dinosaur, который принимает имя динозавра в качестве аргумента и возвращает информацию об этом типе динозавра

Мы собираемся экспортировать все это в наши typeDefs переменные:

export const typeDefs = `
  type Dinosaur {
    name: String
    description: String
  }

  type Query {
    dinosaurs: [Dinosaur]
        dinosaur(name: String): Dinosaur
  }
`;

Если бы мы хотели записать данные, нам бы также нужно было описать мутацию для этого. Мутации (Mutation) — это то, как вы записываете данные с помощью GraphQL. Поскольку мы используем здесь статический набор данных, мы не будем писать ничего такого.

Заполнение данными в resolvers.ts

Резолвер отвечает за заполнение данных для каждого запроса. Здесь у нас есть наш список динозавров, и все, что будет делать резолвер, это либо а) передавать весь этот список клиенту, если пользователь формирует запрос dinosaurs, либо б) передавать только одного из них, если пользователь запрашивает dinosaur.

const dinosaurs = [
  {
    name: "Aardonyx",
    description: "An early stage in the evolution of sauropods.",
  },
  {
    name: "Abelisaurus",
    description: '"Abel\'s lizard" has been reconstructed from a single skull.',
  },
];

export const resolvers = {
  Query: {
    dinosaurs: () => dinosaurs,
    dinosaur: (_: any, args: any) => {
      return dinosaurs.find((dinosaur) => dinosaur.name === args.name);
    },
  },
};

В последнем случае мы передаем аргументы от клиента в функцию, чтобы сопоставить полученное имя с именем в нашем наборе данных.

Настройка main.ts

В наш main.ts мы будем импортировать ApolloServer, graphql и наши typeDef из схемы и наших резолверов:

import { ApolloServer } from "npm:@apollo/server@^4.1";
import { startStandaloneServer } from "npm:@apollo/server@^4.1/standalone";
import { graphql } from "npm:graphql@^16.6";
import { typeDefs } from "./schema.ts";
import { resolvers } from "./resolvers.ts";

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 8000 },
});

console.log(`Server running on: ${url}`);

Мы передаем наши typeDefs и resolvers в ApolloServer, необходимые для запуска нашего нового сервера. Наконец, startStandaloneServer — это вспомогательная функция, позволяющая быстро запустить сервер.

Запуск сервера

Все, что осталось сделать, это запустить сервер:

deno run --allow-net --allow-read --allow-env main.ts

Вы должны увидеть Server running on: 127.0.0.1:8000 в вашем терминале. Если вы перейдете по этому адресу, вы увидите сэндбокс Apollo, где мы можем ввести наш запрос dinosaurs:

query {
  dinosaurs {
    name
    description
  }
}

Это вернет наш набор данных:

{
  "data": {
    "dinosaurs": [
      {
        "name": "Aardonyx",
        "description": "An early stage in the evolution of sauropods."
      },
      {
        "name": "Abelisaurus",
        "description": "\"Abel's lizard\" has been reconstructed from a single skull."
      }
    ]
  }
}

Или, если нам нужен только один dinosaur:

query {
  dinosaur(name:"Aardonyx") {
    name
    description
  }
}

Что возвращает:

{
  "data": {
    "dinosaur": {
      "name": "Aardonyx",
      "description": "An early stage in the evolution of sauropods."
    }
  }
}

Потрясающе!

Ознакомьтесь с нашим более подробным руководством по работе с Apollo.

Что дальше?

В этом посте мы продемонстрировали, как начать работу с Prisma, Mongoose и Apollo. У нас также есть руководства по другим npm-модулям хранения данных, таким как PlanetScale, Redis и MySQL2, и мы продолжим добавлять новые.

Что-то не получается? Обращайтесь за помощью в наш Discord.


Приглашаем всех желающих на открытое занятие «Хуки и мемоизация», на котором участники научатся эффективно использовать возможности React.js и избегать лишних рендеров. Регистрация открыта на странице курса "React.js Developer".




Комментарии (0):