Создание эффективного Node.js API, обеспечивающего безопасность данных и типобезопасность, может быть сложной задачей. Это руководство демонстрирует процесс с использованием Prisma, TypeGraphQL и graphql-query-purifier.

Полный пример доступен в репозитории

Настройка проекта Node.js и интеграция с Prisma

Для начала инициализируйте проект Node.js и интегрируйте Prisma для управления базой данных:

mkdir nodejs-api && cd nodejs-api
npm init -y
npm i --save-dev @types/cors@2.8.16 @types/graphql-fields@1.3.9 @types/node@20.9.2 body-parser@1.20.2 cors@2.8.5 express@4.18.2 graphql-query-purifier prisma@5.6.0 ts-node@10.9.1 type-graphql@2.0.0-beta.1 typegraphql-prisma@0.27.1 typescript@5.2.2
npm i @apollo/server@4.9.5 @prisma/client@5.6.0 graphql@16.8.1 graphql-fields@2.0.3 graphql-scalars@1.22.4 reflect-metadata@0.1.13
npx tsc --init
npx prisma init --datasource-provider sqlite

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

Создайте схему Prisma для представления структуры компании:

// schema.prisma

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

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

generator typegraphql {
  provider = "typegraphql-prisma"
  output   = "generated"
}

model Employee {
  id           Int        @id @default(autoincrement())
  name         String
  departmentId Int
  department   Department @relation(fields: [departmentId], references: [id])
  salary       Salary?
  salaryId     Int?
}

model Department {
  id        Int        @id @default(autoincrement())
  name      String
  employees Employee[]
}

model Salary {
  id         Int      @id @default(autoincrement())
  amount     Float
  employeeId Int      @unique
  employee   Employee @relation(fields: [employeeId], references: [id])
}

Запустите миграции:

npx prisma migrate dev --name init

Настройка Apollo Server с Express

Интегрируйте Apollo Server с Express:

import "reflect-metadata";
import express from "express";
import { ApolloServer } from "@apollo/server";
import { GraphQLQueryPurifier } from "graphql-query-purifier";
import { resolvers } from "../prisma/generated";
import { PrismaClient } from "@prisma/client";

import cors from "cors";
import path from "path";
import { json, urlencoded } from "body-parser";
import { expressMiddleware } from "@apollo/server/express4";
import { buildSchema } from "type-graphql";

const startServer = async () => {
  const app = express();
  const prisma = new PrismaClient();

  app.use(cors(), json(), urlencoded({ extended: true }));

  const gqlPath = path.resolve(__dirname, "../frontend");
  const queryPurifier = new GraphQLQueryPurifier({
    gqlPath,
    allowStudio: true,
    // allowAll: false,
  });
  app.use(queryPurifier.filter);

  const server = new ApolloServer({
    schema: await buildSchema({
      resolvers,
      validate: false,
    }),
  });

  await server.start();

  const context = expressMiddleware(server, {
    context: async (_ctx) => ({
      prisma,
    }),
  });

  app.use("/graphql", context);

  const PORT = process.env.PORT || 3000;
  app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}/graphql`);
  });
};

startServer();

Тестирование API

Проверьте функциональность API с помощью GraphQL запросов:

Разрешенный запрос:

query {
  departments {
    id
    name
    employees {
      id
      name
    }
  }
}

Запрос курильщика:

query {
  departments {
    id
    name
    employees {
      id
      name
      salary {
        amount
      }
    }
  }
}

Заключение

Это руководство представляет метод создания Node.js API с использованием архитектурных решений TypeGraphQL и Prisma. Основные моменты:

  • TypeGraphQL для схемы и резолверов: Автоматическая генерация из моделей Prisma ускоряет создание API с полными возможностями CRUD. Сильная типизация с TypeScript повышает качество API.

  • Фильтрация запросов с graphql-query-purifier: Предотвращает чрезмерную выборку и неавторизованный доступ к чувствительным данным, что критично для API с генерацией кода.

  • Практические результаты: Сочетание этих инструментов приводит к созданию безопасного, масштабируемого API. Это здорово сокращает время разработки.

Метод предлагает эффективный подход к разработке современных веб-приложений, обеспечивая быструю разработку, безопасность и типизацию.

P.S. для тех кому кажется, что этого не будет достаточно для базового набора CRUD. Ниже набор резолверов созданный генератором, каждый резолвер поддерживает бесконечную вложенность и генерируется автоматически на каждое изменение модели данных.

Комментарии (11)


  1. volatilization
    19.11.2023 01:37
    +1

    не к содержанию статьи, а к ее теме.
    какой ужас! вообще сомнительная полезность всяких библиотек доступа к данным по типу graphql и jsonrpc. буквально убийство приложения. связывает по рукам и ногам. даже для mvp сомнительная цена.


    1. thousandsofraccoons Автор
      19.11.2023 01:37
      -1

      У всего свои плюсы и минусы. У этого подхода плюс в том, что API полностью сгенерировано.

      Это позволяет как получать/фильтровать/сортировать ответы с любой глубиной вложенности, так и производить манипуляции с данными на любой глубине.

      Скажем, хотите добавить департамент, в него сотрудника, а ему сразу добавить выгрузку зарплат за один запрос? Не проблема, авторезолверы полностью поддерживают API Призмы.

      Плюс, не придется писать инструкцию, достаточно дать ссылку на сайт с доками по призме, чтобы фронты разобрались как этим пользоваться.

      А написали мы всего два файла. Сложно сходу это представить, я тоже не сразу понял прикола


      1. 19Zb84
        19.11.2023 01:37

        Согласен с первым комментарием.

        А без этих надстроек это разве невозможно сделать ?



        1. thousandsofraccoons Автор
          19.11.2023 01:37
          -1

          Можно конечно. Но придется это писать и делать. ???????? Лично я бы потратил время на что-нибудь другое кроме очередного CRUD.

          Или вы имеете ввиду, что существует способ сгенерировать типизированные REST эндпоинты с валидацией, поддержкой вложенности данных и автогенерацией Open API спек?


          1. 19Zb84
            19.11.2023 01:37
            +1

            придется это писать и делать. 

            А статью не лень писать ? Такое чувство вы не соираетесь поддерживать и развивать код.


            1. thousandsofraccoons Автор
              19.11.2023 01:37
              -2

              Не собираюсь, всё верно. А зачем? Что именно там надо развить, когда у вас в распоряжении полный доступ к любым операциям с базой из коробки?

              Всё что вам остаётся сделать - это убедиться, что клиент не выполнит операцию, которую не должен. Но это тема следующей статьи.


            1. thousandsofraccoons Автор
              19.11.2023 01:37
              -1

              Вы что-то пропали. Расскажете как поддерживать и развивать сгенерированный код?


  1. MinskLeo
    19.11.2023 01:37

    Мне статья понравилась, спасибо. Думаю если цель стоит в том чтобы получить авто генерацию GraphQL API исходя из сущностей есть гораздо более удобный способ это сделать - keystone js.


    1. thousandsofraccoons Автор
      19.11.2023 01:37

      Спасибо! Да, в целом, почти одно и тоже - directus, keystone, strapi и еще штук сто похожих.

      Из того с чем сталкивался лично - Strapi, но она не умеет в edge и в строгую типизацию. Ну и медленно очень запросы обрабатывает. Иногда до минуты может доходить время ответа сервера.

      А Keystone?


    1. thousandsofraccoons Автор
      19.11.2023 01:37

      Погуглил, keystone хорош


      1. MinskLeo
        19.11.2023 01:37

        Я пока только для своих пет проектиков поюзал немного keystone. Годная тема, но есть свои ньюансы, порой довольно неприятные. Тем не менее позволяет очень быстро поднять GraphQL API и легко его конфигурировать. Поддержка TS тоже огонь. Не знаю насколько покатит для высоконагруженных систем... Но юзать приятно)