Digital Ocean offers a service called App Platform that allows for a managed environment to deploy and host a web app on. From the docs:
App Platform is a Platform-as-a-Service (PaaS) offering that allows developers to publish code directly to DigitalOcean servers without worrying about the underlying infrastructure.
The main objective here is to allow developers to get their app into production as quickly and as easily as possible and then once there, to allow for seamless scaling.
In my case, I have an app that I have been building locally for months. It is a bedtime checklist app to help with my kids’ bedtime routine- it allows for the creation of multiple checklists which can then be selected from, and as the child goes thru his bedtime routine, he is rewarded at the end with a fun animation. The animations are built with lottie technology. It has been a helpful tool for getting my kids to bed in a timely manner :-).
The app can be deployed directly from it’s github repo, without much change to it, other then making sure dev-only node packages are included in the devDependencies of package.json, not dependencies.
I followed the steps outlined in this digital ocean documentation to connect my repo to app platform and configure for auto build and deployment on new merges.
DO’s App Platform can be configured to build and deploy updates made to the project’s source repo. When setting up the app the process will walk thru the steps to do so using DO’s visual interface, and the output– once the app has been granted access to the source repo– will be these relevant lines in the project.yaml file:
services:
- environment_slug: node-js
github:
branch: project/release/branch
deploy_on_push: true
repo: githubuser/app-name
While deploying my app the first time however, I ran into errors. I was seeing this error in the logs:
Installing node modules
[2025-02-28 01:44:24] │ npm error code 1
[2025-02-28 01:44:24] │ npm error path /workspace/node_modules/node-sass
I found this strange because I had node-sass listed in the devDependencies of my package.json, so npm should not even be trying to install it in the first place:
“devDependencies”: {
“browser-sync”: “^3.0.2”,
“gulp”: “^4.0.2”,
“gulp-sass”: “^5.1.0”,
“node-sass”: “^7.0.1”
},
I tried adding –omit=dev flag to the npm install command per this SO post, however that didn’t work. I also tried the –production flag, which also didn’t work.
I created a fork of a DO sample node repo and was able to deploy that to my app platform instance successfully. I then added in node-sass as a devDependency and the re-deployment failed.
This made me think the node-sass declaration itself might be the culprit.
However, when I tried removing it from my bedtime checklist app repo, the deployment still failed, and for the same exact error code with the /workspace/node-sass. Now I’m really confused.
Removing it from both package.json and package-lock.json did resolve there error (though while the build was successful, the actual deployment failed for another, separate error).
Based on this SO post, it seems that, while the deployment does not try to actually install the devDependencies, it may be trying to resolve them. Apparently this is a known issue. In this poster’s case he decided to write a script that removes the devDependencies field from the package.json before running npm install.
Other ideas I tried:
- Set node in production to the specific version being used in development environment (17.7.2)
- Set npm to the version being used in development (8.5.2)
I did also try setting the the node engine’s value in package.json so that the version used in prod matched dev:
"engines": {
"node": "10.9.0"
}
No luck.
As it turned out, thanks to this SO post and this SO post node-sass is deprecated. uninstalling that and installing the sass package resolved the issue
npm uninstall node-sass
npm install sass
Unfortunately, while this corrected the issue causing my build to fail, I then found that my deployment failed with the following error message:
"Deploy Error: Run Command Not Executable"
For this I immediately had a huntch. It seemed clear the 'Run Command' referred to here was likely the executable defined in my package.json:
"scripts": {
"start": "bedtime-checklist.js"
}
This confused me, because bedtime-checklist.js was the correct name of my entry script. So in that case the reason had to be something elase. I noticed my dev script defined the engine in front of it:
"scripts": {
"dev": "nodemon bedtime-checklist.js"
}
So in that case, I wondered if I may need to define the production engine, node:
"scripts": {
"start": "node bedtime-checklist.js"
}
Sure enough, that worked!
At that point, I was able to deploy my app now, once in production it mostly worked, but there were some issues.
The first issue I noticed was that some pages failed to load. This was specific to the checklist pages themselves. The reason was I had no database yet. when the app tried to retrieve a checklist it failed.
Next step was to create a database with digital ocean. This was easy enough, though it did then take me a couple of tries to connect my app to the db successfully.
I was making a silly error- I still had localhost as my connection string in my database connection object. I was able to correct the issue by changing my url in the db connection from localhost to my DO app domain.
These allowed me to then create checklists and save them to the db, and then retrieve them, fill them out, and submit on the checklist page.
However, after submission the lottie file animation was not playing correctly. When I looked into it I found that this was because the fetch() call to retrieve the lottie josn was hard-coded with the local host value.
I've now added evnironment varialble to help build that value, and I've defined that variable in the DO interface. I'm rebuilding the app to see if that works.
The environment variable I was trying to use is process.env.BASE_URL. One thing I was not realizing initially is that the code to retrieve the json file is run in the browser. The variable process is defined in node, so it is not available to the browser script.
I was able to split out prod and dev environments using an environment variable.
Then, I ran into an issue where loading of the /bedtime-checklist route would fail if no user had logged in.
If a user has not logged in and tries to access the following route:
The program crashes. This results in an error screen returned when accessing from the digital oceans platform and a crashed app console message plus an infinite loading icon in dev.
How does load of the view happen in node?
When the route is requested, two controllers are called:
router.get("/bedtime-checklist", checklistController.getActiveChecklist, checklistController.getUsers);
router.get("/bedtime-checklist", checklistController.getActiveChecklist, checklistController.getUsers);- The second of these controllers, getUsers, looks for a first and last username from a form submission:
exports.getUsers = (req, res, next) => {User.fetchAll((users) => {res.render("checklists/bedtime-checklist", {firstname: users[0].username,lastname: users[0].lastname,activeChecklist: req.activeChecklist,});});};- getUsers makes a call to the module-level User.fetchAll to look for users present in the users.json file:
static fetchAll(cb) {const p = pathFinder('data', 'users.json');fs.readFile(p, (err, fileContent) => {if (err) {cb([])}cb(JSON.parse(fileContent));})}};
as a temporary work-around I added a default user of ‘my friend’, to make sure the the user cannot accidentally try accessing the /bedtime-checklist without a user logged in (unless they purposely log out).
I’m still fuzzy on how exactly the user authentication is wired up (even though I wrote it lol). But at this point I plan to revamp the user auth process using passport anyway, so I don’t suppose there is much point in looking into the current issue too much further. Besides, in the process of building this new authentication workflow I’ll likely identify how the old one was exactly working.
Adding a Submodule
Digital Ocean’s App Platform also allows for the inclusion of submodules in your app, with the following conditions:
- Submodule must be stored in the same account that you have authorized App Platform to access.
- Submodule must use HTTPS path links instead of SSH links.
Leave a Reply