Как получить безопасный макет API доступа к данным, не кодируя его

Ландшафт веб-разработки постоянно меняется. Мы начали с голых машин, обслуживающих статические HTML-страницы, до появления стека LAMP, затем стека MEAN, а теперь и стека JAM.

На другом уровне прослеживаются две тенденции:

Консолидация архитектуры

Разработчики устают от объединения множества разных частей в приложении. Они начали отдавать предпочтение меньшему вместо большего: унифицированному языку программирования, монорепозиториям, мета-фреймворкам (например, Next.js) и даже созданию веб-приложений без явного кодирования серверной части.

Возрождение баз данных SQL

Базы данных SQL, вероятно, никогда не уступали позиции NoSQL; это просто стало менее крутым для использования на некоторое время с точки зрения моды. Поскольку все больше и больше людей осознают, что немногие веб-приложения действительно «веб-масштабируются», а реляционные базы данных по-прежнему являются лучшим выбором для большинства случаев использования, базы данных SQL возвращаются.

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

Эта статья является частью серии статей о библиотеках и сервисах, которые упрощают создание серверной части веб-приложений. Другие статьи вы можете найти здесь:



Современная веб-архитектура без серверной части — использование «PostgREST
Как заставить браузер напрямую взаимодействовать с базой данныхbetterprogramming.pub»





Современная веб-архитектура без серверной части — использование «Supabase
Давайте поговорим об облачном сервисе, который может значительно сократить время выхода на рынокbetterprogramming.pub»



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

Prisma — набор инструментов ORM для Javascript/TypeScript. ORM — это своего рода библиотека, которая позволяет вам обращаться к базам данных без написания SQL. Вместо этого вы пишете код на том же языке, что и остальная часть вашего приложения, для управления базой данных, а библиотека ORM преобразует его в SQL-запросы.

В частности, Prisma — это ORM, основанный на схеме, что означает, что вы сначала определяете свою модель данных в файле схемы, а затем Prisma генерирует для вас схему базы данных и соответствующие операции CRUD. Напротив, другая категория ORM — это код сначала: вы определяете свою модель данных на языках программирования, таких как Typescript, и ORM выводит из нее схему базы данных.

Выбор «сначала схема» или «сначала код» является вопросом предпочтения, хотя ORM «сначала схема» имеет то преимущество, что он более связный, лаконичный и более легкий для чтения.

Что такое ZenStack

ZenStack — это расширение Prisma, добавляющее уровень безопасности. Он позволяет вам определять правила контроля доступа внутри вашего файла схемы и внедрять их в запросы Prisma во время выполнения.

Кроме того, он предоставляет RESTful API для доступа к базе данных и создает клиентские библиотеки для вашего внешнего кода. Комбинируя управление доступом и генерацию API, ZenStack позволяет иметь полностью безопасный сервер CRUD без его ручной реализации.

Как они работают вместе

Prisma и ZenStack идеально подходят для создания защищенной серверной части с реляционной базой данных. Вот общие шаги для его достижения:

1. Определите свою модель данных

Используйте язык ZModel (надмножество схемы Prisma) для определения ваших моделей, отношений и политик доступа. При желании, если вы используете Next.js в качестве полнофункциональной платформы, включите подключаемый модуль реагирования для создания клиентских перехватчиков доступа к данным.

// schema.zmodel

model Post {
  id        String @id @default(cuid())
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title     String
  published Boolean @default(false)
  author    User @relation(fields: [authorId], references: [id])
  authorId  String

  // author has full access
  @@allow('all', auth() == author)

  // logged-in users can view published posts
  @@allow('read', auth() != null && published)
}

// generate react hooks under ./src/lib/hook
plugin reactHooks {
  provider = '@zenstackhq/react'
  output = "./src/lib/hook
}

2. Запустите генерацию кода

Используйте zenstack CLI для создания нескольких фрагментов кода:

  • Схема призмы (schema.prisma)
  • Призма клиент
  • Политики доступа для внедрения запросов Prisma во время выполнения
  • Реагировать на хуки для доступа к данным

Вы можете использовать сгенерированный schema.prisma для переноса схемы вашей базы данных и клиента Prisma для доступа к базе данных без какого-либо контроля доступа. Далее вы увидите, как помогают политики доступа и хуки React.

3. Установка API доступа к данным

Используйте пакет для конкретной платформы, чтобы установить обработчик запросов API доступа к данным. Обычно вы будете использовать «расширенный» клиент Prisma для инициализации обработчика запросов для защиты конечных точек. «Улучшение» работает путем загрузки политик доступа, созданных на предыдущем шаге, перехвата операций клиента Prisma и введения дополнительных условий запроса/мутации.

Вот пример для Next.js:

// pages/api/model/[...path].ts

// the standard Prisma client
const prisma = new PrismaClient();

// create a Next.js API endpoint handler
export default requestHandler({
    // set up a callback to get a database instance for handling the request
    getPrisma: async (req, res) => {
        // get current user in the session
        const user = await getSessionUser(req, res);

        // return an enhanced Prisma client that enforces access policies
        return withPresets(prisma, { user });
    },
});

4. Используйте сгенерированные хуки для доступа к данным

Используйте сгенерированные хуки, чтобы с легкостью создавать части пользовательского интерфейса. Перехватчики взаимодействуют с API доступа к данным, установленным на предыдущем шаге. Поскольку политики доступа уже защищают API, перехватчики будут возвращать только читаемые объекты текущему пользователю и отклонять любые несанкционированные изменения.

// /src/components/posts.tsx

import { usePost } from '../lib/hooks';

const Posts: FC = () => {
    // "usePost" is a generated hooks method
    const { findMany } = usePost();

    // list unpublished posts together with their author's data,
    // the result "posts" only include entities that are readable
    // to the current user
    const posts = findMany({
        where: { published: false },
        include: { author: true },
        orderBy: { updatedAt: 'desc' },
    });

    // entities are accurately typed based on the query structure
    // posts: Array<Post & { author: user }>
    return (
        <ul>
            {posts.map((post) => (
                <li key={post.id}>
                    {post.title} by {post.author.e}
                </li>
            ))}
        </ul>
    );
};

Почему они улучшают вашу производительность?

Сочетание Prisma и ZenStack повышает продуктивность вашей разработки несколькими способами:

1. Четкое представление о вашей модели данных

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

2. Защита рядом с базой данных

Безопасность сложна отчасти потому, что правила должны последовательно применяться ко всем связанным API. Декларативно моделируя безопасность, вы создаете защиту рядом с базой данных и можете избежать риска забыть добавить необходимые проверки при добавлении или обновлении API. Также намного проще корректировать правила при изменении требований, потому что схема — ваш единственный источник правды.

3. Мощная и типобезопасная клиентская библиотека бесплатно

API-интерфейс Typescript клиента Prisma (бэкэнд) обеспечивает превосходную безопасность типов. Благодаря генерации клиентских библиотек ZenStack теперь вы можете наслаждаться тем же опытом программирования прямо в коде внешнего интерфейса.

Это хороший выбор для меня?

Prisma + ZenStack может хорошо подойти для вашего проекта, если применима одна из следующих ситуаций:

  • Вам нужна простая архитектура и вы хотите создать полнофункциональное веб-приложение полностью с мета-фреймворком (например, Next.js или Remix.run) без отдельного бэкенда.
  • Ваше приложение имеет нетривиальные требования безопасности.
  • Вы не гуру SQL и хотите по возможности избегать сложных задач SQL. В противном случае лучшим выбором может быть Supabase или PostgREST.
  • Вы хотите избежать привязки к определенному типу базы данных или хостеру.

Заворачивать

Prisma и ZenStack — отличная комбинация для создания защищенной серверной части с реляционной базой данных. Я надеюсь, что вы найдете его полезным для вашего следующего проекта.