How to Connect Strapi to PostgreSQL
Tags: strapi, postgres, javascript
Published: 2022-05-17 10:24:34


It is often confusing how to connect a Strapi instance to a Postgres database, but in this article I will demystify it with examples and images.

## Goals
In this article, we will learn how to connect Strapi to PostgreSQL. By default, Strapi uses the SQLite for content storage, but Strapi is not only limited to using SQLite as the database. It can be configured to use other databases like MongoDB, MySQL, MariaDB, PostgreSQL, etc.

We will also learn how to:

- Create collections on Strapi,
- Setup a Postgres database in our machine,
- How to add API endpoints manually on Strapi, and
- Create a Bank admin app in React to consume the Strapi API endpoints.

## An Introduction to Strapi

[Strapi is an open-source headless CMS based on Nodejs and used in designing APIS and managing content.](https://strapi.io/) Strapi helps us scaffold our backend quickly, build APIs and consume the APIs from the client-side. The client can be mobile, web, desktop, cURL, etc.

The APIs are created from the Strapi UI admin panel. We create collections as single-types; a collection in Strapi maps to the endpoints:

- **POST** `/YOUR_COLLECTION_s`: Creates new content.
- **GET** `/YOUR_COLLECTION_s`: Gets all the contents.
- **GET** `/YOUR_COLLECTION_s/:ID`: Gets a single content based on its ID.
- **PUT** `/YOUR_COLLECTION_s/:ID`: Edits content.
- **DELETE** `/YOUR_COLLECTION_s/:ID`: Deletes content.

By default, Strapi gives us RESTful APIs, but also we can create GraphQL APIs in Strapi. We can then use the GraphQL playground in the browser to run the queries and mutations. Setting up Strapi is very easy; run the command below:

```bash
     npx create-strapi-app strapi-api
     # OR
     yarn create strapi-app strapi-api
```
Next, we’re going to install some other dependencies that are required in this project.

```bash
     npm install --save @strapi/utils
     # OR
     yarn add @strapi/utils
```
Then, we run the `yarn develop` command to start the server at `localhost:1337`. The API endpoints are consumed from the `localhost:1337` URL. Also, we can load the admin UI from the same URL at `localhost:1337/admin`.

Strapi contains both a server and a database. The server hosts the APIs, and the database is used to store the application's content. Strapi uses the [Koajs](https://koajs.com/) framework for its server.

To verify this, go to `strapi-API/config/` folder. You should see the following:
```
    |---- config
    |     |- api.js
    |     |- admin.js
    |     |- database.js
    |     |- middleware.js
    |     |- server.js
    |     |- cron-tasks.js
    |     |- plugins.js
```

This is where Strapi API configurations are kept. The `api.js` file contains general settings for API calls.
```js
    // path: ./config/api.js
    module.exports = ({ env }) => ({
      responses: {
        privateAttributes: ['_v', 'id', 'created_at'],
      },
      rest: {
        prefix: '/v1',
        defaultLimit: 100,
        maxLimit: 250,
      },
    });
```
The `admin.js` file contains an admin panel configuration for your Strapi application.
```js
    // path: ./config/admin.js
    module.exports = ({ env }) => ({
      apiToken: {
        salt: env('API_TOKEN_SALT', 'someRandomLongString'),
      },
      auth: {
        secret: env('ADMIN_JWT_SECRET', 'someSecretKey'),
      },
    });
```
The `cron-tasks.js` file is where we can set our cron jobs on Strapi. These jobs are scheduled to run periodically based on the format we input: `\[SECOND (optional)\] [MINUTE] \[HOUR\] [DAY OF MONTH] \[MONTH OF YEAR\] [DAY OF WEEK]`. To enable cron jobs, set `cron.enable` to `true` in the `./config/server.js` file.

```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.).
      },
    };
```
The `server.js` file is where we configure the Strapi server. We can set our host, port, and session keys. Strapi, by default, serves at `0.0.0.0` at port 1337. We can change them in this file.

```js
    // 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'),
      },
    });
```
The `database.js` file is where is the database that will be used to store application content is configured. The database's client, hostname, port, etc., are set here.

```js
    // path: ./config/database.js
    module.exports = ({ env }) => ({
      connection: {
        client: 'sqlite',
        connection: {
          filename: env('DATABASE_FILENAME', '.tmp/data.db'),
        },
        useNullAsDefault: true,
        debug: false,
      },
    });
```
You see here that these are the default database settings for Strapi. It is using the SQLite database, as we said earlier.


- The `connection` field contains database configuration options.
- The `settings`  field contains database settings that are specific to Strapi.
- The `connection.client` field specifies the database client to create the connection.
- The `connection.connection` field is used to configure database connection information.
- The `connection.connection.filename` field defines the path to the database file for sqlite.

In the below section, we will install the PostgreSQL binary.

## PostgresDB Setup

We need to set up and install PostgreSQL. If you don't have PostgresSQL installed in your machine, [go to PostgresSQL downloads](https://www.postgresql.org/download/) and download the binaries for your machine.

- [Linux](https://www.postgresql.org/download/linux/)
- [macOS](https://www.postgresql.org/download/macosx/)
- [Solaris](https://www.postgresql.org/download/solaris/)
- [Windows](https://www.postgresql.org/download/windows/)
- [BSD](https://www.postgresql.org/download/bsd/)

After installation, start the Postgres server. Make sure you remember the Postgres port, username, and password because we will use them in connecting Strapi to the Postgres.Create a database in PostgreSQL, name it `bank` because we will be building a bank app to demonstrate further how to use PostgreSQL DB with Strapi.

Also, if you want to build the PostgreSQL from the source,  [download the source code from here](https://www.postgresql.org/ftp/source/) and compile it.

## Configuring PostgreSQL in Strapi

To configure our Strapi to use our PostgreSQL, we will add some configurations in our `strapi-api/config/database.js` file.

Open the `strapi-api/config/database.js` and paste the below code in the file:
```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,
      },
    });
```

- In the `connection` object, we set the `client` to `postgres`. This client is the PostgreSQL database client to create the connection to the DB.
- The `host` is the hostname of the PostgreSQL server we set it to `localhost`.
- The `port` is set to `5432`, and this is the default port of the PostgreSQL server.
- The `database` is set to the `bank`, and this is the name of the database we created in the PostgreSQL server.
- The `password` is the password of our PostgreSQL server.
- The `username` is the username of our PostgreSQL. It is set to `Postgres` because it is the username of our PostgreSQL server.
- The `schema` is the database schema, and it is set to the `public` here. This schema is used to expose databases to the public.

With this, our Strapi is using PostgreSQL to persist our API content. Now, start Strapi.

```bash
     yarn develop
```

Strapi will load `localhost:1337/admin` on our browser. Now register and click on the `LET'S START` button, this will take you to the admin panel.

If you get a “cannot find module ‘pg’” error, install it by running `yarn add pg` or `npm install` `--``save pg`

## Building our Collections

Everything is ready to roll. We have connected our Strapi app to PostgreSQL. Now, we start building our collections. We are building a bank app; this is a bank admin app that bankers will use to manage accounts in Strapi, and the DB persistence will be PostgreSQL. Let's write out the core functions of our bank app.

- New accounts can be created in the app.
- Transactions can be carried out, i.e., money can be sent from a user to another user.

We will have two models: `Account` and `Transact`.

The Account holds the accounts in the bank, and the Transact holds the transactions carried out.

### The Account Model
```js
    Account {
      name
      balance
    }
```

The `name` field will hold the name of the account holder, and the `balance` will hold the balance of the account holder in Dollars.

### The Transact Model
```js
    Transact {
      sender
      receiver
      amount
    }
```
- The `sender` field holds the name of the account holder that transfers the money.
- The `receiver` is the beneficiary.
- The `amount` is the amount the sender sends to the receiver.

Let's begin creating the collections in our Strapi admin. We will start with the `Account` model.

- Click on the `Create First Content Type` button and type in "account" for a collection name.Now we add the fields for the `account` collection.
- Click on the `+ Add another field` button and select `Text` and type in `name`, and then click on the `+ Add another field` button to add another field.
- Select `Number` and on the `Number format` select `float (ex. 3.3333333)`, then type in `balance` and click on the `Finish` button.
- On the `Account` page that appears click on the `Save` button that is on the top-right corner of the page.

We generate the `Transact` collection:


- Click on the `+ Create new collection type` link, a modal will show up, type in `transact`. Click on the `+ Add another field` button.
- Add the fields: `sender`, `receiver`, and `amount`. The fields `sender` and `receiver` will be `Text` fields while `amount` will be a `Number` field with `float (ex. 3.333333)` Number format.
- After adding them click on the `Finish` button and the `Save` button.

Now we have created our collections.

### Business Logic

We need to implement our business logic. This business logic will be an API endpoint to transfer money from a sender to the recipient.

The logic will be this:
- deduct the amount from the sender
- add the amount to the receiver
- add the transaction to transact collection

I want this to be done in the `/transfer` API, a POST method. A transfer HTTP request will look like this:

```js
    http://localhost:1337/transfer
    Method: POST
    Body:
    {
      sender: nnamdi
      receiver: chidme
      amount:  10
    }
```
So we see that collections can't handle this. This is a single endpoint. The single-type does not do the job for me. I usually find it hard to create a single API endpoint from the Strapi admin panel, so I go to the project source code to add it.

APIs in a Strapi project are kept in the `api` folder. So we go to our `src/api` folder, we will see folders created for our APIs: `transact` and `account`.

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

The `routes/[apiName].js` file contains the endpoints contained in an API. Strapi provides two different router file structures ([core routers](https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html#configuring-core-routers) and [custom routers](https://docs.strapi.io/developer-docs/latest/development/backend-customization/routes.html#creating-custom-routers)). Core routers are the common CRUD operations automatically created by Strapi.

The `controllers` folder contains files that the user can use to customize the endpoints in an API. The user can apply his logic for an endpoint.

- These two things are what we need in other to create our `transfer` API.
So we create a `transfer` folder in our `api` folder:


    mkdir src/api/transfer

- Next, we create `routes` and `controllers` folders inside the `transfer` folder.


    mkdir src/api/transfer/routes src/transfer/controllers

- Create a `transfer.js` file inside the `routes` folder:


    touch src/api/transfer/routes/transfer.js

Inside it, we configure a custom router with a `/transfer` endpoint. Then, we will make the handler point to an `index` function that will export from `controllers`.

```js
      module.exports = {
        routes: [
          {
            method: "POST",
            path: "/transfer",
            handler: "transfer.index",
            config: {
              policies: [],
              middlewares: [],
            },
          },
        ],
      };
```    

- Create a `transfer.js` file in the `controllers` folder.


    touch src/api/transfer/controllers/transfer.js

Here, we will export an `index` function. This function will be called when the `localhost:1337/transfer` HTTP request is made. The function will handle that request. This is where we will apply our business logic of sending money from an account to another beneficiary account.

See the code below:

```js
      "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;
        },
      };
```
The `ctx` holds the `res` and `req` just like in Expressjs or Koajs. The `ctx` is an object that contains properties and methods for accessing the incoming message and for responding to the client.

We retrieved the `sender`, `receiver`, and `amount` from `data` field on the `ctx.request.body`.
Notice we have a `strapi` object. Yes, it is a Strapi object that is global in a Strapi project. We use the object to access different properties and methods.

Here we are using it to use the [entityService](https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/entity-service-api.html) API object, which contains methods to access the database. See the functions in it: `create`, `update`, `find`, `findOne`, etc. They are used to create data in the database, update the database, retrieve values from the database.

So, we retrieved the sender's account details and also the receiver's account details. We then made the transaction, subtracted the `amount` from the sender's balance, and added the receiver's balance.

Next, we updated the sender's and receiver's balances in the database with their new values.

Then, we created a new transaction in the `transact` table, and finally, we returned the result of the new transaction.

The `sanitizeOutput` function removes all private fields from the model and its relations.
Save the file and this will restart our Strapi server. You won't see the `transfer` API appear on the admin panel, and it is a standalone API, not a collection type.

### Allow Access

Now, we will allow access to all our APIs.

Click on the `Settings` item on the sidebar menu, then on the `Roles` item on the second sidebar menu that appears. On the right section, click on the `Public` item and scroll down.
You will see all the APIs with their handlers. Click on the `Select all` checkbox in each API and click on the `Save` button at the top. This will allow public access to all the APIs in our Strapi project:


- account
- transact
- transfer

### Seed Data

Now, we seed our data.

1. Click on `Content Manager`  in the left sidebar and click on the `Account` in the second left sidebar under `Collection Types`. Click on the `+ Create new entry` button.

2. Add the data:


    name -> nnamdi
    balance -> 2000000

3. Click on the `Save` button and the `Publish` button.

4. Add another data:


    name -> chidume
    balance -> 1000000

5. Click on the `Save` button and the `Publish` button.

See our PostgreSQL UI, the contents were persisted on PostgreSQL:


![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622228864829_Screenshot+2021-05-28+at+20.05.16.png)

## Build the Frontend: Bank Admin

Our frontend will be a bank admin app. We will use Nextjs to build the app. So we scaffold our project.

```bash
    yarn create next-app strapi-bank
```
Our app will have two page routes:


- `/`
- `/account/[id]`

The index `/` route will display all the accounts on the system.

The `/account/[id]` route will display a particular account details. This is a dynamic route, the `id` can hold any value, its dynamic, and it will be the unique id of an account.

We will have components:


- `Header`: This will render the Header.
- `AccountCard`: This component will display a few of the account details in the `/` route.
- `AddAccountDialog`: This is a dialog that renders the UI we will use to add new accounts to the system.
- `TransactionDialog`: This dialog renders UI where transactions will be made, sending money from one Account to another.
- `TransactionCard`: This component will render the transactions of a user.
- `Accounts`: This is the page component for the `/` page. It displays all the accounts in the bank.
- `Account`: This is the page component for the `/account/[id]` page.

Our final app will look like this:


![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226607024_Screenshot+2021-05-28+at+18.37.35.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226607053_Screenshot+2021-05-28+at+18.37.49.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226607075_Screenshot+2021-05-28+at+18.37.56.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226607090_Screenshot+2021-05-28+at+18.38.04.png)


OK, so we start creating the components.

```bash
     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
```

**Header**

This will be a simple UI, it will display the text `Bank Admin`. Paste the below code on `components/Header/index.js`:

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

This component will be rendered by the `Accounts` component. It will display a mini detail of an account.

Paste the below code in `components/AccountCard/index.js`:

```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>
        );
      }
```
It receives the `account` object in its `props` argument. Next, we destructure `id`, `name`, `balance`, `createdAt` from the `account.attributes` object.
Yes, `id` and `createdAt` are fields set by Strapi in each model content.

So, the `AccountCard` component renders the details.

**TransactionCard**

This component will render a specific transaction passed to it. It will display the `sender`, `receiver`, and the `amount` sent. The Account page component renders this component to show the transactions done by an account user—the debits and credits.

Paste the code below in `components/TransactionCard/index.js`:

```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>
        );
      }
```
It receives a `transaction` object in its props. The fields `sender`, `receiver`, `amount`, `createdAt` are destructured from the `transaction.attributes` object. These are then rendered by the component.

**Accounts**

This component is rendered when the index page `/` route is navigated. This component will make an HTTP request to the Strapi backend to retrieve the list of accounts and render them.

Paste the below code on `pages/index.js`:

```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>
        );
      }
```
We have three states:
- `accounts`: is a state that holds the accounts retrieved from the `/accounts` endpoint. 
- `showTransactModal`: This is a boolean state that toggles the visibility of the `TransactionModal`.
- `showAddAccountModal`: this is also a boolean state used to display and remove the `AddAccountModal`.

The `useEffect` callback calls the `/api/accounts` endpoint, and the result is set in the `accounts` state.

The `accounts` array is rendered and each account is rendered by the `AccountCard` component, each account is passed to the `AccountCard` via its `account` props.

See that we are conditionally rendering the `AddAccountDialog` and `TransactDialog` dialog components. The `Transact` button toggles the `TransactDialog` and the `Add Account` button toggles the `AddAccountDialog`.

See that we pass a function to each dialog via `closeModal` props. The function will enable the dialogs to close themselves from their components.

**Account**

This is a page component that is rendered when the `/api/account/[id]` route is navigated.
This component displays the account details and its transactions. We can delete an account from there also.

Paste the below code in `pages/account/[id].js`:

```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>
        );
      }
```
The component retrieves the `id` from the URL. We have states `account` and `transactions`, that hold the account and its transactions respectively.

The `useEffect` hook callback calls the `/api/accounts/" + id` endpoint with the `id` value to get the account via its id. Next, it calls the `/api/transacts` endpoint to retrieve the transactions and filter out the transaction made or received by the current account user. The result is set in the `transactions` state while the account details are set in the `account` state.

The UI then displays the account details and their transactions.

There is a `Delete` button that when clicked deletes the current account user. It does this by calling the endpoint `/api/accounts/" + id` over the DELETE HTTP method with the account's id. This makes Strapi delete the account.

**AddAccountDialog**

This component is a dialog that we use to add a new account.

Paste the below code in `components/AddAccountDialog/index.js`:

```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>
        );
      }
```
We have input boxes to type in the account name and its initial balance deposited. The `Add Account` button when clicked calls the `addAccount` function. This function retrieves the account name and balance and calls the `/api/accounts` endpoint via the POST HTTP with the payload: account name and balance. This creates a new account with this payload.

**TransactionDialog**

This component is where we send money from one account to another.

Paste the below code to `components/TransactionDialog/index.js`:

```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>
        );
      }
```
  
The input boxes collect the sender and receiver names and the amount to transfer.

The `transact` function does the job. It retrieves the sender, receiver, and amount values from the input boxes, and then calls the endpoint `/api/transfer` via HTTP POST passing in the sender, receiver, and amount as payload. The `/api/transfer` endpoint will then transfer the `amount` from the `sender` to the `receiver`.

We are done building our components, let's test it.

## Test

**Add new account**

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226905242_Screenshot+2021-05-28+at+18.38.34.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226924226_Screenshot+2021-05-28+at+18.38.43.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226944937_Screenshot+2021-05-28+at+18.39.27.png)


**Perform a transaction**


![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226959115_Screenshot+2021-05-28+at+18.40.03.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622226985971_Screenshot+2021-05-28+at+18.40.21.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622227010065_Screenshot+2021-05-28+at+18.40.26.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622227022418_Screenshot+2021-05-28+at+18.40.34.png)


**Delete an account**


![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622227054544_Screenshot+2021-05-28+at+18.40.48.png)

![](https://paper-attachments.dropbox.com/s_0BA9C20C088A45BE65DF12B51336D9BE7ACA75ACC69E7432BED3DE1319758436_1622227079175_Screenshot+2021-05-28+at+18.40.53.png)

## Source Code
- [Frontend code](https://github.com/Mayowa-Ojo/postgres-strapi-client)
- [Backend code](https://github.com/Mayowa-Ojo/postgres-strapi-api)

## Conclusion

Strapi is great! It's awesome! You see how we were able to integrate PostgreSQL into our Strapi project seamlessly.

We started by introducing Strapi and the goodies it brings to software development. Later on, we learned about the default DB it uses for data persistence.

Next, we introduced PostgreSQL and showed where to download and install it. We learned how to configure a Strapi project to use PostgreSQL as the database to store its application content.

We went further to build a bank app to demonstrate how to use PostgreSQL in Strapi to the fullest.

Always consider using Strapi in your projects. It is straightforward and highly configurable.