Convert Progressive Web Apps (PWAs) into Android APK and AAB files using Bubblewrap CLI and a REST API.
Install dependencies:
npm installEnsure you have Node.js (>= 14) and npm (>= 6) installed:
# Add NodeSource repository for Node.js 14.x
curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
# Install Node.js and npm
sudo apt-get install -y nodejs
# Verify installation
node --version
npm --versionInstall a Java Development Kit (JDK). For example, on Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y default-jdkSet the JAVA_HOME environment variable. Add the following lines to your shell configuration file (e.g., ~/.bashrc or ~/.profile):
export JAVA_HOME=$(readlink -f /usr/bin/java | sed "s:bin/java::")
export PATH=$PATH:$JAVA_HOME/binReload your shell:
source ~/.bashrcVerify the installation:
java -version
javac -versionInstall the Android SDK (if not already installed):
sudo apt-get install -y android-sdkSet the ANDROID_HOME environment variable and update your PATH. Add these lines to your shell configuration file:
export ANDROID_HOME=/usr/lib/android-sdk
export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-toolsReload your shell:
source ~/.bashrcThe Android NDK is required for native code support. You have two options:
sudo apt-get install -y android-ndk- Visit the Android NDK Downloads page.
- Download the latest NDK archive for your system.
- Extract the archive to a directory (e.g.,
/opt/android-ndk).
Then set the ANDROID_NDK environment variable and update your PATH by adding these lines to your shell configuration file:
export ANDROID_NDK=/opt/android-ndk
export PATH=$PATH:$ANDROID_NDKReload your shell:
source ~/.bashrcInstall Bubblewrap CLI globally:
sudo npm install -g @bubblewrap/cliVerify installation:
bubblewrap --versionInitialize Bubblewrap (if running for the first time):
bubblewrap doctor
bubblewrap updateInstall PM2 globally to manage the application in production:
sudo npm install -g pm2Verify installation:
pm2 --versionClone the repository from GitHub:
git clone https://github.com/HashibulAmin/nodeBubblewrap.git
cd nodeBubblewrapInstall the project dependencies:
npm installCopy the example environment file and update it with your settings:
cp .env.example .env
nano .envExample .env file:
PORT=3000
NODE_ENV=production
UPLOAD_DIR=uploads
OUTPUT_DIR=output
ANDROID_HOME=/usr/lib/android-sdk
ANDROID_NDK=/opt/android-ndk
JAVA_HOME=/path/to/your/jdk
MAX_FILE_SIZE=10mb
maxProcess=3
maxConvertProcess=2Set up required directories:
mkdir -p uploads output logs
chmod 755 uploads output logsFor development, run:
npm run devTo start the server normally:
npm startTo run in production using PM2:
pm2 start ecosystem.config.js --env production
pm2 save
pm2 startupThe REST API provides endpoints to convert a PWA into APK and AAB files using Bubblewrap.
-
POST
/convertDescription:
Converts a PWA to APK/AAB files. Accepts a JSON payload containing aurland amanifestUrl(and optionally an existingprojectDir).Request Payload:
{ "url": "https://example.com", "manifestUrl": "https://example.com/manifest.json" }Response:
{ "success": true, "jobId": "a-unique-job-id" }Notes:
- If an existing project directory is provided (via
projectDir), the service will reuse it and skip manifest download and project regeneration. - The service automatically generates or reuses a signing key (stored in a root-level keystores folder) based on the domain name. If a keystore for the domain already exists, its key options are retrieved from the database and reused.
- If an existing project directory is provided (via
-
GET
/job/:jobIdDescription:
Retrieves the status and details of a conversion job by job ID.Response:
{ "jobId": "a-unique-job-id", "status": "completed", "created": 1623456789012, "updated": 1623456790123, "files": { "apk": "base64hash_timestamp.apk", "aab": "base64hash_timestamp.aab" } } -
GET
/download/:filenameDescription:
Downloads the generated APK or AAB file using the filename from the job files list.Response:
Returns the file as a download.
Below is an example of a manifest.json file that you can use for testing. Save this content as manifest.json in your PWA project:
{
"plugin": "PWA to TWA",
"id": "https://bajhi.com?app_id=be064000fba6fcf4f4442c8ec90885ab",
"name": "bajhi",
"short_name": "bajhi",
"description": "Build Websites in Minutes",
"theme_color": "#1858d1",
"background_color": "#1d2327",
"orientation": "portrait",
"display": "standalone",
"scope": "/",
"start_url": "/?utm_source=manifest.json&utm_medium=plugin&utm_campaign=iworks-pwa",
"icons": [
{
"sizes": "512x512",
"type": "image/png",
"src": "https://bajhi.com/wp-content/uploads/2024/11/bajhi-logo-1-1-1.png",
"purpose": "maskable"
},
{
"sizes": "36x36",
"type": "image/png",
"density": "0.75",
"src": "https://bajhi.com/wp-content/uploads/pwa/icon-pwa-36.png?v=233183"
},
{
"sizes": "48x48",
"type": "image/png",
"density": "1.0",
"src": "https://bajhi.com/wp-content/uploads/pwa/icon-pwa-48.png?v=233183"
},
{
"sizes": "72x72",
"type": "image/png",
"density": "1.5",
"src": "https://bajhi.com/wp-content/uploads/pwa/icon-pwa-72.png?v=233183"
},
{
"sizes": "96x96",
"type": "image/png",
"density": "2.0",
"src": "https://bajhi.com/wp-content/uploads/pwa/icon-pwa-96.png?v=233183"
},
{
"sizes": "144x144",
"type": "image/png",
"density": "3.0",
"src": "https://bajhi.com/wp-content/uploads/pwa/icon-pwa-144.png?v=233183"
},
{
"sizes": "192x192",
"type": "image/png",
"density": "4.0",
"src": "https://bajhi.com/wp-content/uploads/pwa/icon-pwa-192.png?v=233183",
"purpose": "any"
},
{
"sizes": "512x512",
"type": "image/png",
"src": "https://bajhi.com/wp-content/uploads/pwa/icon-pwa-512.png?v=233183",
"purpose": "any maskable"
}
],
"categories": [
"education",
"productivity",
"shopping"
],
"shortcuts": [
{
"name": "Websites",
"url": "/websites/?utm_source=manifest.json&utm_medium=application&utm_campaign=iworks-pwa"
},
{
"name": "Apps",
"url": "/apps/?utm_source=manifest.json&utm_medium=application&utm_campaign=iworks-pwa"
},
{
"name": "Services",
"url": "/services/?utm_source=manifest.json&utm_medium=application&utm_campaign=iworks-pwa"
},
{
"name": "Freelancers & Agencies",
"url": "/freelancers/?utm_source=manifest.json&utm_medium=application&utm_campaign=iworks-pwa"
}
]
}Install and configure Nginx:
sudo apt-get install -y nginx
sudo nano /etc/nginx/sites-available/pwa-converterInsert the following configuration (adjust your-domain.com accordingly):
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
client_max_body_size 10M;
}
}Enable the configuration:
sudo ln -s /etc/nginx/sites-available/pwa-converter /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx# View logs
pm2 logs pwa-converter
# Monitor application
pm2 monit
# Restart application
pm2 restart pwa-converter
# Stop application
pm2 stop pwa-converter
# View application status
pm2 status
# Save current PM2 process list
pm2 save-
Bubblewrap Initialization Issues:
bubblewrap doctor npm install -g @bubblewrap/cli@latest
-
PM2 Issues:
pm2 logs pm2 flush
-
Permission Issues:
sudo chown -R $USER:$USER uploads output logs chmod 755 uploads output logs
Feel free to submit pull requests or open issues on GitHub.
MIT License
Copyright (c) [2025] Md Hashibul Amin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.