blog.iankulin.com

Node

Express router for better code organisation

In this beginner-level walkthrough, I tidy up a Node/Express app whose server.js has grown unwieldy by moving routes into separate files with Express Router. I show how to mount routers as middleware with app.use, note that a routes folder is a common convention, and highlight the gotcha that the mounted path prefix gets stripped from the request URL. A consistent naming convention for route groups makes the split easier, and sample code is available on GitHub...

npm ERR! Exit handler never called!

My routine npm update, triggered by a GitHub security advisory, runs into the cryptic “Exit handler never called!” error. Alongside a refresher on what package-lock.json and npm update do, my fix is to confirm the lock file is at fault using npm install –no-package-lock, then delete it and regenerate it with a fresh npm install, followed by retesting and rebuilding any artifacts...

Code reuse by publishing to NPM

I walk through publishing my own JavaScript utility to npm so it can be installed and updated like any other package, rather than copying source files between Node projects. I cover creating an npm account, scoping the package name to my username, writing and publishing the code with npm publish, and installing it in another project, using a simple is-even example...

Uploading files to a web app with Node

A beginner-friendly walkthrough of enabling file uploads in a Node/Express web app using the multer middleware. I cover setting up a basic Express server, configuring multer’s disk storage, and submitting files via an HTML form with multipart/form-data encoding, before briefly noting production concerns like sanitizing filenames and restricting file size and type...

Authentication basics for Node apps

I walk through building user authentication in an Express.js app from the ground up, starting with how HTTP requests, cookies, and sessions work before moving to session IDs and the express-session library. I then cover a login flow with bcrypt-hashed passwords and file-based session persistence, laying groundwork for a future post that will use Passport.js...

Deploying a Node app in Docker

I walk through turning my small Node.js Markdown server into a Docker image, explaining the Dockerfile line by line, building the image locally, and pushing it to Docker Hub. I finish with a sample docker-compose file so self-hosters can deploy the app with a single command...

Quick & Dirty auth with nginx & Node

I walk through protecting a Node/Express app with nginx basic auth, aimed at simple utilities on public servers: firewall off the app’s port so only nginx can reach it, configure nginx to proxy requests and require htpasswd credentials, and pass the authenticated username to the app via a request header. I also cover the approach’s limitations, such as plaintext passwords without SSL, no logout mechanism, and no brute-force protection...

Testing Node.js apps - Mocha, Chai, and Supertest

I walk through testing a Node/Express API with mocha, chai, and supertest, using a simple maths-endpoint app as the example. I cover installing the tools as dev dependencies, exporting the app so tests can control it, writing test suites and cases, running tests via an npm script, and the role each tool plays. I had been using Bruno for lightweight endpoint checks and found the full setup trivial to configure...

Simple SQLite in Express

In this walkthrough, I build a minimal Node/Express REST API backed by SQLite as a learning exercise for migrating an app away from Mongoose. I cover query strings versus request bodies, CRUD endpoints for a users table, and rewriting string-interpolated SQL as parameterized queries to prevent injection. I close with a table of REST conventions for HTTP methods and a link to the finished project on GitHub...

Adding Front Matter To mdserver

The mdserver project needed page titles defined as YAML front matter inside markdown files, in the style of Jekyll and Hugo, instead of being generated from the file name. Rather than writing custom extraction code, I use Showdown’s built-in metadata support, enabled with a converter flag, and confirm the front matter leaves the HTML output unchanged...

New Project Routine

A step-by-step walkthrough of my routine for starting new Node/Express SSR web projects with HTMX, covering directory setup, npm and git initialization, GitHub repo creation, and my ongoing dilemma about committing htmx.min.js versus using a CDN. In the second half, I describe an Express starter skeleton with EJS views and partials, basic routing with 404 handling, and some starter CSS...

Lightweight Web Servers

After a couple of homelab incidents that Uptime Kuma didn’t catch, including a vanished USB mount and an NVMe drive filling up, I consider adding custom checks for things like disk space, memory use, and mount status via a small metrics endpoint. Weighing Node/Express against Python, C, and Golang, I conclude that C’s benchmark edge isn’t worth the hassle for endpoints polled only every few minutes...

Sorting out Node package dependencies when cloning old repos

I add authentication to an old Node.js app by cloning a four-year-old tutorial repo, which produces a flood of npm install errors. I explain how package.json and package-lock.json pin dependency versions (carets, tildes, exact pins, transitive dependencies), then work through escalating fixes in order of risk: deleting the lockfile, loosening version ranges, wildcard updates, and npm audit fix –force, followed by testing...

Installing a Node app on a server

A walkthrough of deploying a Node.js app to a server so it runs as a systemd service rather than an attached terminal session. Using a minimal Express app as the example, I cover copying the files over, installing dependencies, writing a systemd unit file with restart behaviour, and confirming the app still responds after logging out. It’s my manual groundwork for a later Ansible playbook that will automate the setup...

Finding the host IP from inside a Docker container

Migrating my node.js API from a homelab VM to a VPS hits a snag: nginx, running in a Docker container, serves static files fine but returns Bad Gateway when proxying API requests. I walk through working out that localhost inside the container doesn’t reach the host, trying host.docker.internal without success, and finding the fix by checking the docker0 bridge interface, which gave 172.17.0.1 as the address to put in the nginx config...

nginx in Front of a node.js app

I configure NGINX on a VPS to serve static files while forwarding /api routes to a Node.js app running on localhost. I cover a server block example with proxy_pass and the Host header, the conf.d include convention, and restarting or validating the config. This post is part of a series on my weather API, but it is self-contained...

How to deploy a Node.js app

I deploy a small Node.js and Express service from my MacBook to an Ubuntu VPS, covering the choice between a native install and a Docker container, installing Node and npm via apt (including why the Ubuntu repository version is quite old), and copying project files to the server with scp rather than setting up git-based CI/CD for such a tiny project. I test with Insomnia instead of Postman, which I avoid because of its data collection...

Using Node.js to return a static file

In this beginner-level walkthrough, I move a static temperature text file from NGINX to a Node.js server. I introduce NGINX and reverse proxying, explain what Node and Express are, and show a short Express setup that serves the file on localhost port 3000, with deploying it left for later...

Complicating the Temperature API

A short YouTube video on building REST APIs with Node and Express prompts a plan to replace my existing weather data setup—a Python cron job on a VPS that writes API results to a text file served by NGINX—with a Node.js endpoint that fetches and caches fresh weather data on request. This would cut the up-to-ten-minute staleness of temperature data polled by homelab servers, and I break the work into small steps, starting with replicating the current text-file behaviour in Node behind NGINX...