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

Цели

В этой статье мы узнаем, как подключить Strapi к PostgreSQL. По умолчанию Strapi использует SQLite для хранения контента, но Strapi не ограничивается только использованием SQLite в качестве базы данных. Его можно настроить для использования других баз данных, таких как MongoDB, MySQL, MariaDB, PostgreSQL и т. д.

Мы также научимся:

  • Создавайте коллекции на Strapi,
  • Настройте базу данных Postgres на нашей машине,
  • Как вручную добавить конечные точки API на Strapi и
  • Создайте приложение администратора банка в React, чтобы использовать конечные точки API Strapi.

Введение в Страпи

Strapi — это безголовая CMS с открытым исходным кодом, основанная на Nodejs и используемая для разработки APIS и управления контентом. Strapi помогает нам быстро формировать наш бэкенд, создавать API и использовать API со стороны клиента. Клиент может быть мобильным, веб, настольным, cURL и т. д.

API создаются из административной панели пользовательского интерфейса Strapi. Мы создаем коллекции как однотипные; коллекция в Strapi сопоставляется с конечными точками:

  • POST /YOUR_COLLECTION_s: Создает новый контент.
  • GET /YOUR_COLLECTION_s: получает все содержимое.
  • GET /YOUR_COLLECTION_s/:ID: получает один контент на основе его идентификатора.
  • PUT /YOUR_COLLECTION_s/:ID: редактирует содержимое.
  • УДАЛИТЬ /YOUR_COLLECTION_s/:ID: удаляет содержимое.

По умолчанию Strapi предоставляет нам API-интерфейсы RESTful, но мы также можем создавать API-интерфейсы GraphQL в Strapi. Затем мы можем использовать игровую площадку GraphQL в браузере для запуска запросов и мутаций. Настроить Strapi очень просто; выполните команду ниже:

npx create-strapi-app strapi-api
     # OR
yarn create strapi-app strapi-api

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

npm install --save @strapi/utils
     # OR
yarn add @strapi/utils

Затем мы запускаем команду yarn develop, чтобы запустить сервер в localhost:1337. Конечные точки API используются из URL-адреса localhost:1337. Кроме того, мы можем загрузить пользовательский интерфейс администратора с того же URL-адреса по адресу localhost:1337/admin.

Strapi содержит как сервер, так и базу данных. На сервере размещаются API, а база данных используется для хранения содержимого приложения. Strapi использует для своего сервера фреймворк Koajs.

Чтобы убедиться в этом, перейдите в папку strapi-API/config/. Вы должны увидеть следующее:

|---- config
    |     |- api.js
    |     |- admin.js
    |     |- database.js
    |     |- middleware.js
    |     |- server.js
    |     |- cron-tasks.js
    |     |- plugins.js

Здесь хранятся конфигурации API Strapi. Файл api.js содержит общие настройки вызовов API.

// path: ./config/api.js
    module.exports = ({ env }) => ({
      responses: {
        privateAttributes: ['_v', 'id', 'created_at'],
      },
      rest: {
        prefix: '/v1',
        defaultLimit: 100,
        maxLimit: 250,
      },
    });

Файл admin.js содержит конфигурацию панели администратора для вашего приложения Strapi.

// path: ./config/admin.js
    module.exports = ({ env }) => ({
      apiToken: {
        salt: env('API_TOKEN_SALT', 'someRandomLongString'),
      },
      auth: {
        secret: env('ADMIN_JWT_SECRET', 'someSecretKey'),
      },
    });

Файл cron-tasks.js — это место, где мы можем установить наши задания cron на Strapi. Эти задания запланированы для периодического запуска в зависимости от введенного нами формата: \[SECOND (optional)\] [MINUTE] \[HOUR\] [DAY OF MONTH] \[MONTH OF YEAR\] [DAY OF WEEK]. Чтобы включить задания cron, установите cron.enable в true в файле ./config/server.js.

// path: ./config/cron-tasks.js
    module.exports = {
      /**
       * Simple example.
       * Every monday at 1am.
       */
    
      '0 0 1 * * 1': ({ strapi }) => {
        // Add your own logic here (e.g. send a queue of email, create a database backup, etc.).
      },
    };

В файле server.js мы настраиваем сервер Strapi. Мы можем установить наш хост, порт и сеансовые ключи. Strapi по умолчанию обслуживает 0.0.0.0 на порту 1337. Мы можем изменить их в этом файле.

// path: ./config/server.js
    module.exports = ({ env }) => ({
      host: env('HOST', '0.0.0.0'),
      port: env.int('PORT', 1337),
      app: {
        keys: env.array('APP_KEYS'),
      },
    });

В файле database.js настраивается база данных, которая будет использоваться для хранения содержимого приложения. Здесь задаются клиент базы данных, имя хоста, порт и т. д.

// path: ./config/database.js
    module.exports = ({ env }) => ({
      connection: {
        client: 'sqlite',
        connection: {
          filename: env('DATABASE_FILENAME', '.tmp/data.db'),
        },
        useNullAsDefault: true,
        debug: false,
      },
    });

Здесь вы видите, что это настройки базы данных по умолчанию для Strapi. Как мы уже говорили ранее, он использует базу данных SQLite.

  • Поле connection содержит параметры конфигурации базы данных.
  • Поле settings содержит настройки базы данных, относящиеся к Strapi.
  • Поле connection.client указывает клиент базы данных для создания соединения.
  • Поле connection.connection используется для настройки информации о соединении с базой данных.
  • Поле connection.connection.filename определяет путь к файлу базы данных для sqlite.

В следующем разделе мы установим двоичный файл PostgreSQL.

Настройка PostgresDB

Нам нужно настроить и установить PostgreSQL. Если на вашем компьютере не установлен PostgresSQL, перейдите к загрузкам PostgresSQL и загрузите двоичные файлы для вашего компьютера.

После установки запустите сервер Postgres. Убедитесь, что вы помните порт Postgres, имя пользователя и пароль, потому что мы будем использовать их при подключении Strapi к Postgres. Создайте базу данных в PostgreSQL, назовите ее bank, потому что мы будем создавать банковское приложение, чтобы продемонстрировать, как использовать базу данных PostgreSQL с Страпи.

Кроме того, если вы хотите собрать PostgreSQL из исходного кода, скачайте отсюда исходный код и скомпилируйте его.

Настройка PostgreSQL в Strapi

Чтобы настроить наш Strapi для использования нашего PostgreSQL, мы добавим некоторые конфигурации в наш файл strapi-api/config/database.js.

Откройте strapi-api/config/database.js и вставьте в файл приведенный ниже код:

// strapi-api/config/database.js
    module.exports = ({ env }) => ({
      connection: {
        client: 'postgres',
        connection: {
          host: env('DATABASE_HOST', 'localhost'),
          port: env.int('DATABASE_PORT', 5432),
          database: env('DATABASE_NAME', 'bank'),
          user: env('DATABASE_USERNAME', 'postgres'),
          password: env('DATABASE_PASSWORD', '0000'),
          schema: env('DATABASE_SCHEMA', 'public'), // Not required
          ssl: {
            rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false),
          },
        },
        debug: false,
      },
    });
  • В объекте connection мы устанавливаем client на postgres. Этот клиент является клиентом базы данных PostgreSQL для создания соединения с БД.
  • host — это имя хоста сервера PostgreSQL, для которого мы установили localhost.
  • Для port установлено значение 5432, и это порт по умолчанию для сервера PostgreSQL.
  • database установлено на bank, и это имя базы данных, которую мы создали на сервере PostgreSQL.
  • password — это пароль нашего сервера PostgreSQL.
  • username — это имя пользователя нашего PostgreSQL. Он установлен на Postgres, потому что это имя пользователя нашего сервера PostgreSQL.
  • schema — это схема базы данных, и здесь она установлена ​​на public. Эта схема используется для публичного доступа к базам данных.

При этом наш Strapi использует PostgreSQL для сохранения содержимого нашего API. Теперь запустите Strapi.

yarn develop

Strapi загрузит localhost:1337/admin в наш браузер. Теперь зарегистрируйтесь и нажмите на кнопку LET'S START, это приведет вас к панели администратора.

Если вы получаете сообщение об ошибке «не удается найти модуль ‘pg’», установите его, запустив yarn add pgили npm install --``save pg

Создание наших коллекций

Все готово к прокату. Мы подключили наше приложение Strapi к PostgreSQL. Теперь приступаем к сбору наших коллекций. Мы создаем банковское приложение; это приложение для администратора банка, которое банкиры будут использовать для управления учетными записями в Strapi, а постоянство БД будет PostgreSQL. Давайте распишем основные функции нашего банковского приложения.

  • В приложении можно создавать новые учетные записи.
  • Могут осуществляться транзакции, т. е. деньги могут быть отправлены от пользователя к другому пользователю.

У нас будет две модели: Account и Transact.

Счет содержит счета в банке, а Транзакция содержит выполненные транзакции.

Модель учетной записи

Account {
      name
      balance
    }

Поле name будет содержать имя владельца счета, а поле balance будет содержать баланс владельца счета в долларах.

Транзакционная модель

Transact {
      sender
      receiver
      amount
    }
  • Поле sender содержит имя владельца счета, который переводит деньги.
  • receiver является бенефициаром.
  • amount — это сумма, которую отправитель отправляет получателю.

Давайте начнем создавать коллекции в нашей панели администратора Strapi. Мы начнем с модели Account.

  • Нажмите кнопку Create First Content Type и введите «учетная запись» в качестве имени коллекции. Теперь мы добавим поля для коллекции account.
  • Нажмите кнопку + Add another field, выберите Text и введите name, а затем нажмите кнопку + Add another field, чтобы добавить еще одно поле.
  • Выберите Number и на Number format выберите float (ex. 3.3333333), затем введите balance и нажмите кнопку Finish.
  • На появившейся странице Account нажмите кнопку Save в правом верхнем углу страницы.

Генерируем коллекцию Transact:

  • Нажмите на ссылку + Create new collection type, появится модальное окно, введите transact. Нажмите на кнопку + Add another field.
  • Добавьте поля: sender, receiver и amount. Поля sender и receiver будут полями Text, а amount будут полями Number с числовым форматом float (ex. 3.333333).
  • После их добавления нажмите на кнопку Finish и кнопку Save.

Теперь мы создали наши коллекции.

Бизнес-логика

Нам нужно реализовать нашу бизнес-логику. Эта бизнес-логика будет конечной точкой API для перевода денег от отправителя к получателю.

Логика будет такая:

  • списать сумму с отправителя
  • добавить сумму получателю
  • добавить транзакцию в коллекцию транзакций

Я хочу, чтобы это было сделано в /transfer API, методом POST. HTTP-запрос на передачу будет выглядеть так:

http://localhost:1337/transfer
    Method: POST
    Body:
    {
      sender: nnamdi
      receiver: chidme
      amount:  10
    }

Итак, мы видим, что коллекции не справляются с этим. Это единая конечная точка. Один тип не делает работу для меня. Обычно мне сложно создать единую конечную точку API из панели администратора Strapi, поэтому я перехожу к исходному коду проекта, чтобы добавить ее.

API в проекте Strapi хранятся в папке api. Итак, мы идем в нашу папку src/api, мы увидим папки, созданные для наших API: transact и account.

|--- src
    |    |--- api
    |    |    |--- account
    |    |    |    |--- content-types
    |    |    |    |    |--- account
    |    |    |    |    |    |- schema.json
    |    |    |    |--- controllers
    |    |    |    |    |- account.js
    |    |    |    |--- routes
    |    |    |    |--- services
    |    |    |--- transact
    ...

Файл routes/[apiName].js содержит конечные точки, содержащиеся в API. Strapi предоставляет две разные файловые структуры маршрутизатора (основные маршрутизаторы и настраиваемые маршрутизаторы). Основные маршрутизаторы — это стандартные операции CRUD, автоматически создаваемые Strapi.

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

  • Эти две вещи нам нужны для создания нашего transfer API. Итак, мы создаем папку transfer в нашей папке api:
mkdir src/api/transfer
  • Далее мы создаем папки routes и controllers внутри папки transfer.
mkdir src/api/transfer/routes src/transfer/controllers
  • Создайте файл transfer.js в папке routes:
touch src/api/transfer/routes/transfer.js

Внутри него мы настраиваем собственный маршрутизатор с конечной точкой /transfer. Затем мы заставим обработчик указывать на функцию index, которая будет экспортироваться из controllers.

module.exports = {
        routes: [
          {
            method: "POST",
            path: "/transfer",
            handler: "transfer.index",
            config: {
              policies: [],
              middlewares: [],
            },
          },
        ],
      };
  • Создайте файл transfer.js в папке controllers.
touch src/api/transfer/controllers/transfer.js

Здесь мы экспортируем функцию index. Эта функция будет вызываться при выполнении HTTP-запроса localhost:1337/transfer. Функция обработает этот запрос. Здесь мы применим нашу бизнес-логику отправки денег со счета на счет другого получателя.

См. код ниже:

"use strict";
      const { sanitize } = require("@strapi/utils");
      /**
       * A set of functions called "actions" for `transfer`
       */
      module.exports = {
        index: async (ctx) => {
          const {
            data: { sender, receiver, amount },
          } = ctx.request.body;
          let entity;
          // deduct amount from sender
          // add amount to reciver
          // add the transaction to transact
          const [senderAcc] = await strapi.entityService.findMany(
            "api::account.account",
            {
              filters: { name: { $eq: sender } },
            }
          );
          const [receiverAcc] = await strapi.entityService.findMany(
            "api::account.account",
            {
              filters: { name: { $eq: receiver } },
            }
          );
          senderAcc.balance = parseFloat(senderAcc.balance) - parseFloat(amount);
          receiverAcc.balance = parseFloat(receiverAcc.balance) + parseFloat(amount);
          await strapi.entityService.update("api::account.account", senderAcc.id, {
            data: senderAcc,
          });
          await strapi.entityService.update("api::account.account", receiverAcc.id, {
            data: receiverAcc,
          });
          entity = await strapi.entityService.create("api::transact.transact", {
            data: { sender, receiver, amount },
          });
          const sanitizedEntity = await sanitize.contentAPI.output(entity);
          return sanitizedEntity;
        },
      };

ctx содержит res и req, как в Expressjs или Koajs. ctx — это объект, который содержит свойства и методы для доступа к входящему сообщению и для ответа клиенту.

Мы получили sender, receiver и amount из поля data в файле ctx.request.body. Обратите внимание, что у нас есть объект strapi. Да, это объект Strapi, который является глобальным в проекте Strapi. Мы используем объект для доступа к различным свойствам и методам.

Здесь мы используем его для использования объекта API entityService, который содержит методы для доступа к базе данных. Посмотрите в нем функции: create, update, find, findOne и т.д. Они используются для создания данных в БД, обновления БД, извлечения значений из БД.

Итак, мы получили данные учетной записи отправителя, а также данные учетной записи получателя. Затем мы совершили транзакцию, вычли amount из баланса отправителя и добавили баланс получателя.

Далее мы обновили балансы отправителя и получателя в базе данных с их новыми значениями.

Затем мы создали новую транзакцию в таблице transact и, наконец, вернули результат новой транзакции.

Функция sanitizeOutput удаляет все частные поля из модели и ее отношений. Сохраните файл, и это перезапустит наш сервер Strapi. Вы не увидите transfer API на панели администратора, и это автономный API, а не тип коллекции.

Разрешить доступ

Теперь мы разрешим доступ ко всем нашим API.

Нажмите на элемент Settings в меню боковой панели, затем на элемент Roles во втором появившемся боковом меню. В правом разделе нажмите на элемент Public и прокрутите вниз. Вы увидите все API с их обработчиками. Установите флажок Select allв каждом API и нажмите кнопку Save вверху. Это обеспечит публичный доступ ко всем API в нашем проекте Strapi:

  • счет
  • сделка
  • передача

Исходные данные

Теперь мы заполняем наши данные.

  1. Нажмите Content Manager на левой боковой панели и нажмите Account на второй левой боковой панели под Collection Types. Нажмите на кнопку + Create new entry.
  2. Добавьте данные:
name -> nnamdi
balance -> 2000000
  1. Нажмите на кнопку Save и кнопку Publish.
  2. Добавьте другие данные:
name -> chidume
balance -> 1000000
  1. Нажмите кнопку Save и кнопку Publish.

Посмотрите наш пользовательский интерфейс PostgreSQL, содержимое было сохранено в PostgreSQL:

Создание внешнего интерфейса: администратор банка

Наш интерфейс будет приложением администратора банка. Мы будем использовать Nextjs для создания приложения. Итак, мы строим наш проект.

yarn create next-app strapi-bank

Наше приложение будет иметь два маршрута страницы:

  • /
  • /account/[id]

Маршрут index / будет отображать все учетные записи в системе.

Маршрут /account/[id] будет отображать данные конкретной учетной записи. Это динамический маршрут, id может содержать любое значение, его динамическое значение, и это будет уникальный идентификатор учетной записи.

У нас будут компоненты:

  • Header: это отобразит заголовок.
  • AccountCard: этот компонент отображает некоторые сведения об учетной записи в файле /route.
  • AddAccountDialog: Это диалоговое окно, отображающее пользовательский интерфейс, который мы будем использовать для добавления новых учетных записей в систему.
  • TransactionDialog: В этом диалоговом окне отображается пользовательский интерфейс, в котором будут совершаться транзакции, отправляя деньги с одного счета на другой.
  • TransactionCard: Этот компонент будет отображать транзакции пользователя.
  • Accounts: это компонент страницы для страницы /. В нем отображаются все счета в банке.
  • Account: это компонент страницы для страницы /account/[id].

Наше финальное приложение будет выглядеть так:

Итак, мы приступаем к созданию компонентов.

mkdir components
        
     mkdir components/TransactionCard
     touch components/TransactionCard/index.js
     touch components/TransactionCard/TransactionCard.module.css
        
     mkdir components/TransactionDialog
     touch components/TransactionDialog/index.js
        
     mkdir components/AddAccountDialog
     touch components/AddAccountDialog/index.js
        
     mkdir components/AccountCard
     touch components/AccountCard/index.js
     touch components/AccountCard/AccountCard.module.css
        
     mkdir components/Header
     touch components/Header/index.js
     touch components/Header/Header.module.css
        
     touch styles/AccountView.module.css
     mkdir pages/account
     touch pages/account/[id].js

Заголовок

Это будет простой пользовательский интерфейс, он будет отображать текст Bank Admin. Вставьте приведенный ниже код в components/Header/index.js:

import { header, headerName } from "./Header.module.css";
        
        export default function Header() {
          return (
            <section className={header}>
              <div className={headerName}>Bank Admin</div>
            </section>
          );
        }

AccountCard

Этот компонент будет отображаться компонентом Accounts. Он будет отображать мини-детали учетной записи.

Вставьте приведенный ниже код в components/AccountCard/index.js:

import styles from "./AccountCard.module.css";
      import Link from "next/link";
      export default function AccountCard({ account }) {
        const {
          id,
          attributes: { name, balance, createdAt },
        } = account;
        return (
          <Link href={`account/${id}`}>
            <div className={styles.account}>
              <div className={styles.accountdetails}>
                <div className={styles.accountname}>
                  <h3>
                    <span style={{ fontWeight: "100" }}>Account: </span>
                    {name}
                  </h3>
                </div>
                <div className={styles.accountbalance}>
                  <span>
                    <span style={{ fontWeight: "100" }}>Balance($): </span>
                    {balance}
                  </span>
                </div>
                <div className={styles.accountcreated_at}>
                  <span>Created: {createdAt}</span>
                </div>
              </div>
            </div>
          </Link>
        );
      }

Он получает объект account в своем аргументе props. Далее мы деструктурируем id, name, balance, createdAt из объекта account.attributes. Да, id и createdAt — это поля, установленные Strapi в каждом содержимом модели.

Итак, компонент AccountCard отрисовывает детали.

ТранзакцияКарта

Этот компонент будет отображать переданную ему конкретную транзакцию. Он будет отображать отправленные sender, receiver и amount. Компонент страницы «Учетная запись» отображает этот компонент для отображения транзакций, выполненных пользователем учетной записи, — дебета и кредита.

Вставьте приведенный ниже код в components/TransactionCard/index.js:

import styles from "./TransactionCard.module.css";
      export default function TransactionCard({ transaction }) {
        const {
          attributes: { sender, receiver, amount, createdAt },
        } = transaction;
        return (
          <div className={styles.transactionCard}>
            <div className={styles.transactionCardDetails}>
              <div className={styles.transactionCardName}>
                <h4>
                  <span>Sender: </span>
                  <span style={{ fontWeight: "bold" }}>{sender}</span>
                </h4>
              </div>
              <div className={styles.transactionCardName}>
                <h4>
                  <span>Receiver: </span>
                  <span style={{ fontWeight: "bold" }}>{receiver}</span>
                </h4>
              </div>
              <div className={styles.transactionCardName}>
                <h4>
                  <span>Amount($): </span>
                  <span style={{ fontWeight: "bold" }}>{amount}</span>
                </h4>
              </div>
              <div className={styles.transactionCardName}>
                <h4>
                  <span>Created At: </span>
                  <span style={{ fontWeight: "bold" }}>{createdAt}</span>
                </h4>
              </div>
            </div>
          </div>
        );
      }

Он получает объект transaction в свой реквизит. Поля sender, receiver, amount, createdAt деструктурированы из объекта transaction.attributes. Затем они отображаются компонентом.

Счета

Этот компонент отображается при переходе по маршруту страницы индекса /. Этот компонент отправит HTTP-запрос к серверной части Strapi, чтобы получить список учетных записей и отобразить их.

Вставьте приведенный ниже код в pages/index.js:

import Head from "next/head";
      import styles from "../styles/Home.module.css";
      import Header from "../components/Header";
      import AccountCard from "../components/AccountCard";
      import { useEffect, useState } from "react";
      import axios from "axios";
      import TransactionDialog from "../components/TransactionDialog";
      import AddAccountDialog from "../components/AddAccountDialog";
      export default function Home() {
        const [accounts, setAccounts] = useState([]);
        const [showTransactModal, setShowTransactModal] = useState(false);
        const [showAddAccountModal, setShowAddAccountModal] = useState(false);
        useEffect(() => {
          async function fetchData() {
            const { data } = await axios.get("http://localhost:1337/api/accounts");
            setAccounts(data?.data);
          }
          fetchData();
        }, []);
        return (
          <div className={styles.container}>
            <Head>
              <title>Bank Admin</title>
              <link rel="icon" href="/favicon.ico" />
            </Head>
            <main className={styles.main}>
              <Header />
              <div className={styles.breadcrumb}>
                <div>
                  <span style={{ margin: "1px" }}>
                    <button onClick={() => setShowTransactModal(true)}>
                      Transact
                    </button>
                  </span>
                  <span style={{ margin: "1px" }}>
                    <button onClick={() => setShowAddAccountModal(true)}>
                      Add Account
                    </button>
                  </span>
                </div>
              </div>
              <div className={styles.accountcontainer}>
                <div className={styles.youraccounts}>
                  <h3>Accounts</h3>
                </div>
                <div>
                  {accounts.map((account, i) => (
                    <AccountCard key={i} account={account} />
                  ))}
                </div>
              </div>
              {showAddAccountModal ? (
                <AddAccountDialog
                  closeModal={() => setShowAddAccountModal((pV) => !pV)}
                />
              ) : null}
              {showTransactModal ? (
                <TransactionDialog
                  closeModal={() => setShowTransactModal((pV) => !pV)}
                />
              ) : null}
            </main>
          </div>
        );
      }

У нас есть три состояния:

  • accounts: это состояние, в котором хранятся учетные записи, полученные из конечной точки /accounts.
  • showTransactModal: это логическое состояние, которое переключает видимость TransactionModal.
  • showAddAccountModal: это также логическое состояние, используемое для отображения и удаления AddAccountModal.

Обратный вызов useEffect вызывает конечную точку /api/accounts, и результат устанавливается в состояние accounts.

Массив accounts визуализируется, и каждая учетная запись визуализируется компонентом AccountCard, каждая учетная запись передается в AccountCard через его реквизиты account.

Обратите внимание, что мы условно отображаем компоненты диалога AddAccountDialog и TransactDialog. Кнопка Transact переключает TransactDialog, а кнопка Add Account переключает AddAccountDialog.

Обратите внимание, что мы передаем функцию каждому диалогу через closeModal props. Функция позволит диалогам закрыться из их компонентов.

Счет

Это компонент страницы, который отображается при переходе по /api/account/[id]route. Этот компонент отображает сведения об учетной записи и ее транзакциях. Мы также можем удалить учетную запись оттуда.

Вставьте приведенный ниже код в pages/account/[id].js:

import styles from "../../styles/AccountView.module.css";
      import { useRouter } from "next/router";
      import TransactionCard from "../../components/TransactionCard";
      import axios from "axios";
      import { useEffect, useState } from "react";
      export default function Account() {
        const router = useRouter();
        const {
          query: { id },
        } = router;
        const [account, setAccount] = useState();
        const [transactions, setTransactions] = useState([]);
        useEffect(() => {
          async function fetchData() {
            const { data: AccountData } = await axios.get(
              "http://localhost:1337/api/accounts/" + id
            );
            var { data: transactsData } = await axios.get(
              "http://localhost:1337/api/transacts"
            );
            transactsData = transactsData?.data?.filter(
              (tD) =>
                tD.attributes?.sender == AccountData?.data?.attributes?.name ||
                tD.attributes?.receiver == AccountData?.data?.attributes?.name
            );
            setAccount(AccountData?.data);
            setTransactions(transactsData);
          }
          fetchData();
        }, [id]);
        async function deleteAccount() {
          if (confirm("Do you really want to delete this account?")) {
            await axios.delete("http://localhost:1337/api/accounts/" + id);
            router.push("/");
          }
        }
        return (
          <div className={styles.accountviewcontainer}>
            <div className={styles.accountviewmain}>
              <div style={{ width: "100%" }}>
                <div className={styles.accountviewname}>
                  <h1>{account?.attributes?.name}</h1>
                </div>
                <div className={styles.accountviewminidet}>
                  <div>
                    <span style={{ marginRight: "4px", color: "rgb(142 142 142)" }}>
                      Balance($):
                    </span>
                    <span style={{ fontWeight: "600" }}>
                      {account?.attributes?.balance}
                    </span>
                  </div>
                  <div style={{ padding: "14px 0" }}>
                    <span>
                      <button onClick={deleteAccount} className="btn-danger">
                        Delete
                      </button>
                    </span>
                  </div>
                </div>
                <div className={styles.accountviewtransactionscont}>
                  <div className={styles.accountviewtransactions}>
                    <h2>Transactions</h2>
                  </div>
                  <div className={styles.accountviewtransactionslist}>
                    {!transactions || transactions?.length <= 0
                      ? "No transactions yet."
                      : transactions?.map((transaction, i) => (
                          <TransactionCard key={i} transaction={transaction} />
                        ))}
                  </div>
                </div>
              </div>
            </div>
          </div>
        );
      }

Компонент извлекает id из URL-адреса. У нас есть состояния account и transactions, которые содержат учетную запись и ее транзакции соответственно.

Обратный вызов ловушки useEffect вызывает конечную точку /api/accounts/" + id со значением id, чтобы получить учетную запись через ее идентификатор. Затем он вызывает конечную точку /api/transacts для извлечения транзакций и фильтрации транзакций, сделанных или полученных пользователем текущей учетной записи. Результат устанавливается в состоянии transactions, а данные учетной записи устанавливаются в состоянии account.

Затем пользовательский интерфейс отображает данные учетной записи и их транзакции.

Есть кнопка Delete, которая при нажатии удаляет текущего пользователя учетной записи. Для этого он вызывает конечную точку /api/accounts/" + id с помощью HTTP-метода DELETE с идентификатором учетной записи. Это заставляет Strapi удалить учетную запись.

диалоговое окно добавления учетной записи

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

Вставьте приведенный ниже код в components/AddAccountDialog/index.js:

import { useState } from "react";
      import axios from "axios";
      export default function AddAccountDialog({ closeModal }) {
        const [disable, setDisable] = useState(false);
        async function addAccount(e) {
          setDisable(true);
          const accountName = e.target.accountName.value;
          const accountBalance = e.target.accountBalance.value;
          // add account
          await axios.post("http://localhost:1337/api/accounts", {
            data: {
              name: accountName,
              balance: parseFloat(accountBalance),
            },
          });
          setDisable(false);
          closeModal();
          location.reload();
        }
        return (
          <div className="modal">
            <div className="modal-backdrop" onClick={closeModal}></div>
            <div className="modal-content">
              <div className="modal-header">
                <h3>Add New Account</h3>
                <span
                  style={{ padding: "10px", cursor: "pointer" }}
                  onClick={closeModal}
                >
                  X
                </span>
              </div>
              <form onSubmit={addAccount}>
                <div className="modal-body content">
                  <div style={{ display: "flex", flexWrap: "wrap" }}>
                    <div className="inputField">
                      <div className="label">
                        <label>Name</label>
                      </div>
                      <div>
                        <input id="accountName" name="accountName" type="text" />
                      </div>
                    </div>
                    <div className="inputField">
                      <div className="label">
                        <label>Balance($):</label>
                      </div>
                      <div>
                        <input
                          id="accountBalance"
                          name="accountBalance"
                          type="text"
                        />
                      </div>
                    </div>
                  </div>
                </div>
                <div className="modal-footer">
                  <button
                    disabled={disable}
                    className="btn-danger"
                    onClick={closeModal}
                  >
                    Cancel
                  </button>
                  <button disabled={disable} className="btn" type="submit">
                    Add Account
                  </button>
                </div>
              </form>
            </div>
          </div>
        );
      }

У нас есть поля ввода для ввода имени учетной записи и ее начального баланса. Кнопка Add Account при нажатии вызывает функцию addAccount. Эта функция извлекает имя учетной записи и баланс и вызывает конечную точку /api/accounts через POST HTTP с полезной нагрузкой: имя учетной записи и баланс. Это создает новую учетную запись с этой полезной нагрузкой.

диалоговое окно транзакции

В этом компоненте мы отправляем деньги с одного счета на другой.

Вставьте приведенный ниже код в components/TransactionDialog/index.js:

import { useState } from "react";
      import axios from "axios";
    
      export default function TransactionDialog({ closeModal }) {
        const [disable, setDisable] = useState(false);
        async function transact(e) {
          setDisable(true);
          const sender = e.target.sender.value;
          const receiver = e.target.receiver.value;
          const amount = e.target.amount.value;
          await axios.post("http://localhost:1337/api/transfer", {
            data: { sender, receiver, amount },
          });
          setDisable(false);
          closeModal();
          location.reload();
        }
        return (
          <div className="modal">
            <div className="modal-backdrop" onClick={closeModal}></div>
            <div className="modal-content">
              <div className="modal-header">
                <h3>Transaction</h3>
                <span
                  style={{ padding: "10px", cursor: "pointer" }}
                  onClick={closeModal}
                >
                  X
                </span>
              </div>
              <form onSubmit={transact}>
                <div className="modal-body content">
                  <div style={{ display: "flex", flexWrap: "wrap" }}>
                    <div className="inputField">
                      <div className="label">
                        <label>Sender</label>
                      </div>
                      <div>
                        <input id="sender" type="text" />
                      </div>
                    </div>
                    <div className="inputField">
                      <div className="label">
                        <label>Receiver</label>
                      </div>
                      <div>
                        <input id="receiver" type="text" />
                      </div>
                    </div>
                    <div className="inputField">
                      <div className="label">
                        <label>Amount($)</label>
                      </div>
                      <div>
                        <input id="number" name="amount" type="text" />
                      </div>
                    </div>
                  </div>
                </div>
                <div className="modal-footer">
                  <button
                    disabled={disable}
                    className="btn-danger"
                    onClick={closeModal}
                  >
                    Cancel
                  </button>
                  <button disabled={disable} className="btn" type="submit">
                    Transact
                  </button>
                </div>
              </form>
            </div>
          </div>
        );
      }

В поля ввода заносятся имена отправителя и получателя, а также сумма перевода.

Функция transact выполняет свою работу. Он извлекает значения отправителя, получателя и суммы из полей ввода, а затем вызывает конечную точку /api/transfer через HTTP POST, передавая отправителя, получателя и сумму в качестве полезной нагрузки. Затем конечная точка /api/transfer передаст amount с sender на receiver.

Мы закончили создание наших компонентов, давайте протестируем их.

Тест

Добавить новую учетную запись

Выполнить транзакцию

Удалить аккаунт

Исходный код

Заключение

Страпи великолепен! Это потрясающе! Вы видите, как нам удалось легко интегрировать PostgreSQL в наш проект Strapi.

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

Далее мы представили PostgreSQL и показали, где его скачать и установить. Мы узнали, как настроить проект Strapi для использования PostgreSQL в качестве базы данных для хранения содержимого приложения.

Мы пошли еще дальше и создали приложение для банка, чтобы продемонстрировать, как использовать PostgreSQL в Strapi в полной мере.

Всегда рассматривайте возможность использования Strapi в своих проектах. Это просто и легко настраивается.