DEV Community

Cover image for Setup a Vendure E-Commerce API on a Dokku Droplet
Manuel Sommerhalder
Manuel Sommerhalder

Posted on • Edited on

Setup a Vendure E-Commerce API on a Dokku Droplet

If you haven't heard of Vendure, it is a "modern, headless GraphQL-based e-commerce framework built with TypeScript & Nodejs". It's still in beta but already used in production and a major release is planned until end of this year. Check it out on vendure.io!

This guide assumes that you have already installed a local Vendure project via @vendure/create. It will lead you through the setup of Vendure on a Dokku droplet, hosted by DigitalOcean with some tips for your production system. If you have no DigitalOcean account yet, you can use this referral link if you want, to get $100 over 60 days.

Create Droplet

First you can create the Dokku droplet with the one-click installer here: https://marketplace.digitalocean.com/apps/dokku

When creating the droplet, you will see three setting fields:

  • Public Key: For adding an SSH key to login into your server.
  • Hostname: For setting the hostname (e.g. example.com). You an also just use the IP address of your droplet.
  • Use virtualhost naming for apps: Enable this if you want the app URLs by default to be APP_NAME.example.com instead of example.com:APP_PORT_NUMBER.

Make sure your droplet has:

  • Enough disk space: I'm currently using 9.5GB including OS, Docker containers and around 200 product images.
  • Enough memory: Especially if you are going use the ElasticsearchPlugin to search through products. I would recommend at least 3GB of memory and a swapfile of 3GB (we will create one later). This should be enough in the beginning and the swapfile can cover possible memory peaks.
  • A firewall: To secure your droplet, make sure you restrict inbound rules to only HTTP(S) and also SSH if you want to login on your server via SSH. This will prevent outsiders from accessing your Elasticsearch instance on port 9200/9300. On your droplet overview click on Secure your Droplets and add a new firewall. Set the inbound rules to HTTPS and SSH and save. You firewall should look like this:

Alt Text

It might also make sense for you to enable backups for weekly snapshots, after the shop is up and running.

Setup Dokku Environment

When the droplet is ready and you are able to connect with your previously added SSH key (ssh -i SSH_KEY_NAME root@IP_OF_YOUR_DROPLET), we can start setting up Dokku and its services. First we will create the app:

dokku apps:create myshopapi 
Enter fullscreen mode Exit fullscreen mode

So our API is later going to be available on myshopapi.example.com/shop-api and the admin area on myshopapi.example.com/admin. Dokku will provide the ENV variable PORT, which we will use later in our config file.

Create storage folder

Then we will create a persistent storage folder that will get mounted to the /storage folder of the app when the application starts. It stores product assets, mail templates and test mails. On your droplet run the following:

# create folder and set correct ownership mkdir -p /var/lib/dokku/data/storage/myshopapi chown -R dokku:dokku /var/lib/dokku/data/storage/myshopapi # mount it to your app container to /storage dokku storage:mount myshopapi /var/lib/dokku/data/storage/myshopapi:/app/storage 
Enter fullscreen mode Exit fullscreen mode

Then zip and upload the contents of the /static folder from your local computer:

# create zip file cd ~/YOURLOCALPROJECTFOLDER/static zip -r ../storage.zip . * # upload it to your droplet scp ~/YOURLOCALPROJECTFOLDER/storage.zip root@IP_OF_YOUR_DROPLET:/var/lib/dokku/data/storage/myshopapi 
Enter fullscreen mode Exit fullscreen mode

Back at your droplet unzip it:

# unzip folders unzip /var/lib/dokku/data/storage/myshopapi/storage.zip # remove the zip rm /var/lib/dokku/data/storage/myshopapi/storage.zip 
Enter fullscreen mode Exit fullscreen mode

Now you should have your assets and email folders inside the /var/lib/dokku/data/storage/myshopapi folder.

Install MySQL Dokku Plugin

I choose MySQL but you can also use Postgres, MariaDB or SQLite if you like. Let's call the service myshopapi-mysql and link it to the app:

sudo dokku plugin:install https://github.com/dokku/dokku-mysql.git mysql dokku mysql:create myshopapi-mysql dokku mysql:link myshopapi-mysql myshopapi 
Enter fullscreen mode Exit fullscreen mode

After the installation is complete you should get some data/config directories and the ENV variable DATABASE_URL. The value should look like this: mysql://mysql:YOUR_MYSQL_PASSWORT@dokku-mysql-myshopapi-mysql:3306/myshopapi_mysql

For easier usage of the login data in our config file later, we set our own custom ENV variables:

dokku config:set --no-restart myshopapi MYSQL_PORT=3306 dokku config:set --no-restart myshopapi MYSQL_USER=mysql dokku config:set --no-restart myshopapi MYSQL_PASSWORD=YOUR_MYSQL_PASSWORD dokku config:set --no-restart myshopapi MYSQL_HOST=dokku-mysql-myshopapi-mysql dokku config:set --no-restart myshopapi MYSQL_DB=myshopapi_mysql 
Enter fullscreen mode Exit fullscreen mode

Install Elasticsearch Dokku Plugin

First we install the plugin and create the service. Vendure should work with v7.0 or higher. I'm currently using v7.5.2. Then we increase the max_map_count option of the virtual machine to prevent out of memory exceptions:

# install plugin sudo dokku plugin:install https://github.com/dokku/dokku-elasticsearch.git elasticsearch # set version you want to use export ELASTICSEARCH_IMAGE_VERSION="7.5.2" # create service dokku elasticsearch:create myshopapi-elasticsearch # expose the service to ports dokku elasticsearch:expose myshopapi-elasticsearch 9200 9300 # link the service to your app dokku elasticsearch:link myshopapi-elasticsearch myshopapi # increase max_map_count echo 'vm.max_map_count=262144' | sudo tee -a /etc/sysctl.conf; sudo sysctl -p 
Enter fullscreen mode Exit fullscreen mode

Since Dokku seems to have an issue connecting with Elasticsearch v7.*, you will get an unable to connect error after creating the service. We also have to paste in following into the /var/lib/dokku/services/elasticsearch/myshopapi-elasticsearch/config/elasticsearch.yml file, to be able to connect to the instance:

node.name: node-1 cluster.name: docker-cluster network.host: 0.0.0.0 cluster.initial_master_nodes: - node-1 
Enter fullscreen mode Exit fullscreen mode

We also get an ENV variable during this process named ELASTICSEARCH_URL which looks like this: http://dokku-elasticsearch-myshopapi-elasticsearch:9200

We will also split it in our own variables, to use it later in our config file:

dokku config:set --no-restart myshopapi ELASTICSEARCH_HOST=http://dokku-elasticsearch-myshopapi-elasticsearch dokku config:set --no-restart myshopapi ELASTICSEARCH_PORT=9200 
Enter fullscreen mode Exit fullscreen mode

Create a Swapfile

I still experienced memory overflows sometimes on production when Elasticsearch was busy. We can create a 3GB swapfile to help cover those like previously mentioned. You can also create a larger one, the recommendations vary. Changing it or adding another file later is possible too.

Further, we will set the swappiness variable to 10, so the virtual machine is less likely going to use the swapfile instead of the memory.

# create 3GB swapfile fallocate -l 3G /swapfile # set correct permissions chmod 600 /swapfile # set up swap area mkswap /swapfile # turn swap one swapon /swapfile # save swap file in config to use after restart echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab # check if the swap is on swapon --show # set the swappiness sysctl vm.swappiness=10 # save config to use after restart echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf 
Enter fullscreen mode Exit fullscreen mode

Install LetsEncrypt Dokku Plugin

What would be a good shop without SSL? So now we add the domain for the vendure instance and install the LetsEncrypt plugin and add a cronjob to renew the certificate automatically:

# add domain to dokku app dokku domains:set myshopapi API.YOURDOMAIN.COM # install letsencrypt sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git # set your email dokku config:set --no-restart myshopapi DOKKU_LETSENCRYPT_EMAIL=YOUREMAIL@example.com # install certificate for domain and add renewal cron job dokku letsencrypt:enable myshopapi dokku letsencrypt:cron-job --add 
Enter fullscreen mode Exit fullscreen mode

Set further Environment Variables

Since sensitive data should not be in your source code when checking into git, we will add some more ENV variables for the SMTP connection, which will be used to send emails and also one for the session secret.

dokku config:set --no-restart myshopapi SESSION_SECRET=YOUR_SESSION_SECRET_KEY dokku config:set --no-restart myshopapi SMTP_HOST=YOUR_SMTP_HOST dokku config:set --no-restart myshopapi SMTP_PORT=YOUR_SMTP_PORT dokku config:set --no-restart myshopapi SMTP_USER=YOUR_SMTP_USER dokku config:set --no-restart myshopapi SMTP_PASSWORD=YOUR_SMTP_PASSWORD 
Enter fullscreen mode Exit fullscreen mode

Change your vendure-config.ts File

Now that we have everything ready, we can update our config file with all the ENV variables. We will also add cors.origin setting to be able to query the API myshopapi.example.com from example.com and set the correct assetUrlPrefix. Here's how your config file could look:

import path from 'path'; import { VendureConfig, DefaultJobQueuePlugin, examplePaymentHandler } from '@vendure/core' import { Transport } from '@nestjs/microservices' import { AssetServerPlugin } from '@vendure/asset-server-plugin'; import { AdminUiPlugin } from '@vendure/admin-ui-plugin'; import { ElasticsearchPlugin } from '@vendure/elasticsearch-plugin'; import { EmailPlugin, defaultEmailHandlers } from '@vendure/email-plugin' export const config: VendureConfig = { workerOptions: { transport: Transport.TCP, options: { host: 'localhost', port: 3020 } }, apiOptions: { port: Number(process.env.PORT) || 3000, adminApiPath: 'admin-api', shopApiPath: 'shop-api', cors: { origin: /example\.com$/ } }, authOptions: { sessionSecret: process.env.SESSION_SECRET }, dbConnectionOptions: { type: 'mysql', synchronize: false, logging: false, port: Number(process.env.MYSQL_PORT) || 3306, database: process.env.MYSQL_DB, host: process.env.MYSQL_HOST, username: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, migrations: [path.join(__dirname, '../migrations/*.ts')] }, paymentOptions: { paymentMethodHandlers: [examplePaymentHandler] }, plugins: [ DefaultJobQueuePlugin, AssetServerPlugin.init({ port: 3001, route: 'assets', assetUploadDir: '/storage/assets', assetUrlPrefix: 'https://myshopapi.example.com/assets/' }), ElasticsearchPlugin.init({ host: process.env.ELASTICSEARCH_HOST, port: Number(process.env.ELASTICSEARCH_PORT) || 9200 }), EmailPlugin.init({ handlers: defaultEmailHandlers, templatePath: '/storage/email/templates', transport: { type: 'smtp', host: process.env.SMTP_HOST || '', port: Number(process.env.SMTP_PORT) || 587, auth: { user: process.env.SMTP_USER || '', pass: process.env.SMTP_PASSWORD || '' } }, globalTemplateVars: { fromAddress: '"Example" <info@example.ch>', verifyEmailAddressUrl: 'https://example.com/verify', passwordResetUrl: 'https://example.com/password-reset', changeEmailAddressUrl: 'https://example.com/verify-email-address-change' } }), AdminUiPlugin.init({ port: 3002 }) ] } module.exports = { config }; 
Enter fullscreen mode Exit fullscreen mode

Setup Git

Finally we can add the droplet as remote in our git repostory and push our code to it:

git remote add dokku dokku@IP_OF_YOUR_DROPLET:myshopapi git push dokku master 
Enter fullscreen mode Exit fullscreen mode

Some useful Dokku commands

# output app logs dokku logs myshopapi # output Elasticsearch logs dokku elasticsearch:logs myshopapi-elasticsearch # restart the app dokku ps:restart myshopapi # connect to MySQL database dokku mysql:connect myshopapi-mysql USE myshopapi_mysql; # export/import an SQL file from/into database dokku mysql:export myshopapi-mysql > backup.sql dokku mysql:import myshopapi-mysql < backup.sql 
Enter fullscreen mode Exit fullscreen mode

I hope this guide will help you setting up your shop API. Please comment if something is not working or if you have other tips, that you would like to share. You can also join the Slack channel or look into the real world Vendure project on Github, which might help you too.

Top comments (0)