Develop locally with Strapi v4

To run your Strapi v4 app locally with all of its services, follow these steps:

  1. Download your deployed code by running the following command using the Platform.sh CLI:

    platform get PROJECT_ID
  2. Create a new branch. Whenever you develop Platform.sh, you should develop in an isolated environment. This way you aren’t opening SSH tunnels to your production environment. By creating a branch from your default environment, you create a new environment with copies of all production code and data.

    Create an isolated environment named updates by running the following command:

    platform environment:branch updates

    You can name the environment anything you want, just use the name you choose in later steps.

  3. Assuming you’re using a PostgreSQL or MySQL database, modify your database connection to look like the following:

    config/database.js
    const path = require("path");
    let connection;
    let db_relationship = "postgresdatabase";
    // Helper function for decoding Platform.sh base64-encoded JSON variables.
    function decode(value) {
      return JSON.parse(Buffer.from(value, "base64"));
    }
    if (!process.env.PLATFORM_RELATIONSHIPS) {
      if (process.env.PLATFORM_PROJECT) {
        console.log(
          "In Platform.sh build hook. Using default SQLite configuration until services are available."
        );
      } else {
        console.log(
          'Configuring local SQLite connection. \n\nIf you meant to use a tunnel, be sure to run \n\n$ export PLATFORM_RELATIONSHIPS="$(platform tunnel:info --encode)"\n\nto connect to that service.\n'
        );
      }
      // Define the default SQLite connection.
      connection = {
        connection: {
          client: "sqlite",
          connection: {
            filename: path.join(
              __dirname,
              "..",
              process.env.DATABASE_FILENAME || ".tmp/data.db"
            ),
          },
          useNullAsDefault: true,
        },
      };
    } else {
      if (process.env.PLATFORM_PROJECT) {
        console.log(
          "In Platform.sh deploy hook. Using defined service configuration."
        );
      } else {
        console.log("Using tunnel for local development.");
      }
      // Define the managed service connection.
      let credentials = decode(process.env.PLATFORM_RELATIONSHIPS)[
        db_relationship
      ][0];
      // Option 1. PostgreSQL.
      // The PostgreSQL configuration assumes the following in your .platform/services.yaml file:
      //
      // dbpostgres:
      //    type: postgresql:12
      //    disk: 256
      //
      // And a relationship defined in your .platform.app.yaml file as follows:
      //
      // relationships:
      //    postgresdatabase: "dbpostgres:postgresql"
      if (credentials.scheme == "pgsql") {
        console.log("PostgreSQL detected.");
        let postgres_pool = {
          min: 0,
          max: 10,
          acquireTimeoutMillis: 600000,
          createTimeoutMillis: 30000,
          idleTimeoutMillis: 20000,
          reapIntervalMillis: 20000,
          createRetryIntervalMillis: 200,
        };
        connection = {
          connection: {
            client: "postgres",
            connection: {
              host: credentials.ip,
              port: credentials.port,
              database: credentials.path,
              user: credentials.username,
              password: credentials.password,
              ssl: false,
            },
            debug: false,
            postgres_pool,
          },
        };
        // Option 2. Oracle MySQL.
        // The Oracle MySQL configuration assumes the following in your .platform/services.yaml file:
        //
        // dbmysql:
        //    type: oracle-mysql:8.0
        //    disk: 256
        //
        // And a relationship defined in your .platform.app.yaml file as follows:
        //
        // relationships:
        //    database: "dbmysql:mysql"
      } else if (credentials.scheme == "mysql") {
        console.log("MySQL detected.");
        connection = {
          connection: {
            client: "mysql",
            connection: {
              host: credentials.ip,
              port: credentials.port,
              database: credentials.path,
              user: credentials.username,
              password: credentials.password,
              ssl: false,
            },
            debug: false,
          },
        };
      }
    }
    // Export the connection to Strapi.
    module.exports = ({ env }) => connection;

    See the comments for explanations of individual sections.

    If you have defined the relationship to your service differently (in .platform.app.yaml) or are using a different service, use that name in place of postgresdatabase.

  4. Open a SSH tunnel to the environment’s database:

    platform tunnel:open -A <APP_NAME> -e updates

    Replace <APP_NAME> with your app’s name in your .platform.app.yaml file.

    If you get the error The pcntl PHP extension is required error, use this command instead:

    platform tunnel:single -A <APP_NAME> -e updates
  5. Add an environment variable that contains the service credentials:

    export PLATFORM_RELATIONSHIPS="$(platform tunnel:info -A <APP_NAME> -e updates --encode)"
  6. Download all media uploads from the environment:

    platform mount:download -A <APP_NAME> -e updates -m public/uploads --target public/uploads -y
  7. Build and start the Strapi server:

    yarn --frozen-lockfile
    yarn develop

Now your Strapi app should be running locally with a connection to a remote database that’s separate from your production database.