Setting up Nuxt.js and Strapi applications and deploying them to the server
Nuxt.js is a popular framework that enhances the capabilities of Vue.js for building websites. It provides developers with guidelines and restrictions to simplify the website creation process. It is particularly well-suited for creating content-rich websites such as company websites or blogs. By leveraging the features of Vue.js, Nuxt.js enables developers to build dynamic and engaging web experiences with ease.
Strapi is an open-source headless CMS built on Node.js, designed for efficient content modelling and management. It offers a user-friendly interface, allowing developers to define and structure content types easily. With its headless architecture, Strapi separates the content management system from the presentation layer, enabling flexible API consumption. It is a customizable CMS focused on content types, making it ideal for content-driven applications and websites.
PM2 is an open-source process manager for Node.js applications that simplifies management and deployment. It offers process monitoring, automatic restarts, load balancing, and log management. With PM2, developers can easily manage their Node.js applications in production.
Setting up the project
Firstly, we have to set up both Nuxt.js and Strapi, which will be referred to as frontend and backend.
# setting up Nuxt.js
mkdir nuxt-strapi
npx nuxi init frontend
cd frontend
yarn install
# setting up Strapi
yarn create strapi-app backend
After this, we will configure PM2 to handle the management of both Nuxt.js and Strapi. This setup will provide a centralized solution for managing the environment variables as well.
ecosystem.config.js
:
const env = process.argv[process.argv.indexOf('--env') + 1];
const isDev = env === 'dev';
module.exports = {
apps: [
// Frontend - Nuxt 3
{
name: 'frontend',
cwd: './frontend', // Adjust the path to your frontend project directory
append_env_to_name: true,
script: 'yarn',
args: isDev ? 'dev' : 'start',
interpreter: 'none',
env_dev: {
NODE_ENV: 'development',
HOST: '0.0.0.0',
STRAPI_URL: 'http://127.0.0.1:1337',
PORT: 3000, // Choose the port you want to use for your frontend
},
watch: false,
max_memory_restart: '1G',
instances: 1, // Only one instance
hooks: {
pre_start: {
command: 'yarn',
args: 'build', // Build the frontend before starting
},
},
},
// Backend - Strapi
{
append_env_to_name: true,
name: 'backend',
cwd: './backend',
script: 'yarn',
args: isDev ? 'develop' : 'start',
interpreter: 'none',
env_dev: {
NODE_ENV: 'development',
HOST: '0.0.0.0',
PORT: '1337',
URL: 'http://127.0.0.1:1337',
APP_KEYS: "toBeModified1,toBeModified2",
API_TOKEN_SALT: 'tobemodified',
ADMIN_JWT_SECRET: 'tobemodified',
JWT_SECRET: 'tobemodified',
DATABASE_CLIENT: 'sqlite',
DATABASE_FILENAME: './tmp/data.db'
},
watch: false,
max_memory_restart: '1G',
instances: 1, // Only one instance
hooks: {
pre_start: {
command: 'yarn',
args: 'build', // Build the backend before starting
},
},
},
],
};
Then, we can use the following command to start both the frontend and the backend.
pm2 start ecosystem.config.js --env dev
To let the Nuxt.js interact with Strapi, we can add @nuxtjs/strapi
dependency.
yarn add --dev @nuxtjs/strapi
Loading the .env
file into the PM2 configuration
Although PM2 provides a nice unified entrypoint of managing the env variables and the applications, it is currently not possible to load environment variables directly from the .env
file. Therefore I wrote an npm package to resolve this problem.
Modify the ecosystem.config.js
:
const { injectEnvs } = require('pm2-dotenv')
module.exports = {
apps: [
{
name: 'frontend',
// add this line
...injectEnvs('frontend')
},
{
name: 'backend',
...injectEnvs('backend')
}
]
}
Now you can place all the variables in a .env.production
file and let pm2 loads them:
FRONTEND_NODE_ENV='production'
FRONTEND_HOST='0.0.0.0'
FRONTEND_STRAPI_URL='http://127.0.0.1:1337'
FRONTEND_PORT=3000
BACKEND_NODE_ENV='production'
BACKEND_HOST='0.0.0.0'
BACKEND_PORT='1337'
BACKEND_URL='http://127.0.0.1:1337'
BACKEND_APP_KEYS="toBeModified1toBeModified2"
BACKEND_API_TOKEN_SALT='tobemodified'
BACKEND_ADMIN_JWT_SECRET='tobemodified'
BACKEND_JWT_SECRET='tobemodified'
BACKEND_DATABASE_CLIENT='sqlite'
BACKEND_DATABASE_FILENAME='./tmp/data.db'
Note that you may change the database connection from SQLite to MySQL. However, you might have to modify the config/database.js
file in the Strapi project.
const path = require('path');
module.exports = ({ env }) => {
const client = env('DATABASE_CLIENT', 'sqlite');
const connections = {
mysql: {
connection: {
connectionString: env('DATABASE_URL'),
host: env('DATABASE_HOST', 'localhost'),
port: env.int('DATABASE_PORT', 3306),
database: env('DATABASE_NAME', 'strapi'),
user: env('DATABASE_USERNAME', 'strapi'),
password: env('DATABASE_PASSWORD', 'strapi'),
ssl: env.bool('DATABASE_SSL', false) && {
key: env('DATABASE_SSL_KEY', undefined),
cert: env('DATABASE_SSL_CERT', undefined),
ca: env('DATABASE_SSL_CA', undefined),
capath: env('DATABASE_SSL_CAPATH', undefined),
cipher: env('DATABASE_SSL_CIPHER', undefined),
rejectUnauthorized: env.bool(
'DATABASE_SSL_REJECT_UNAUTHORIZED',
true
),
},
},
pool: { min: env.int('DATABASE_POOL_MIN', 2), max: env.int('DATABASE_POOL_MAX', 10) },
},
sqlite: {
connection: {
filename: path.join(
__dirname,
'..',
env('DATABASE_FILENAME', './tmp/data.db')
),
},
useNullAsDefault: true,
},
};
return {
connection: {
client,
...connections[client],
acquireConnectionTimeout: env.int('DATABASE_CONNECTION_TIMEOUT', 60000),
},
};
};
Also, you might have to change the config/server.js
:
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
url: env('URL', 'http://0.0.0.0:1337'),
app: {
keys: env.array('APP_KEYS'),
},
webhooks: {
populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false),
},
});
Finally, start the processes:
pm2 start ecosystem.config.js --env production
Use Nginx for reverse proxy
When all is set, the final step is to use nginx to proxy the request. You may add the following configuration to your existing nginx config. Note that in this example we put the Strapi to the /api
path.
upstream strapi {
server localhost:1337;
}
upstream nuxt {
server localhost:3000;
}
server {
listen 80;
gzip on;
gzip_types text/plain application/xml text/css application/javascript;
gzip_min_length 1000;
# Static Root
location / {
expires $expires;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 1m;
proxy_connect_timeout 1m;
proxy_pass http://nuxt;
}
# Strapi API and Admin
location /api/ {
rewrite ^/api/?(.*)$ /$1 break;
proxy_pass http://strapi;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
}