Only show guides for your app
A complete Node.js service that accepts an uploaded image, resizes it, and returns a compact WebP — built on Express, multer, and sharp, with the full code and every run step.
Thumbnails, avatars, banners — almost every app needs to shrink images at some point, and shipping full-size uploads is slow and wasteful. This guide builds a small HTTP service that takes an image, resizes it to the dimensions you ask for, and hands back a compact WebP. It runs on the Node.js application and uses sharp, the fast image library whose prebuilt binaries install cleanly on Falix.
| At a glance | |
|---|---|
| You need | A server running the Node.js application |
| Build with | Express, multer (uploads), sharp (resizing) |
| Plan | Any — free runs while your session timer has time, premium runs 24/7 |
| Time | About twenty minutes |
New to the Node application? Read Node.js on Falix first — it covers the index.js entry file, automatic npm install, and the SERVER_PORT rule this project relies on.
| Endpoint | Method | Does |
|---|---|---|
/ |
GET | The upload form in public/ |
/resize?w=&h= |
POST | Takes an uploaded image, returns a resized WebP |
The resizer keeps aspect ratio, never upscales past the original, and caps the requested size so nobody can ask for a 40000×40000 monster.
You need three files. In the File Manager, create package.json:
{
"name": "image-resizer",
"version": "1.0.0",
"private": true,
"dependencies": {
"express": "^4.19.2",
"multer": "^1.4.5-lts.1",
"sharp": "^0.33.4"
}
}
Then index.js — the whole service:
const express = require('express');
const multer = require('multer');
const sharp = require('sharp');
const path = require('node:path');
const app = express();
const upload = multer({
limits: { fileSize: 10 * 1024 * 1024 }, // 10 MB cap
storage: multer.memoryStorage(),
});
app.use(express.static(path.join(__dirname, 'public')));
// POST an image file (field name "image") plus ?w= and/or ?h=.
// Returns a resized WebP.
app.post('/resize', upload.single('image'), async (req, res) => {
if (!req.file) return res.status(400).json({ error: 'No image uploaded (field name must be "image").' });
const width = clampDim(req.query.w);
const height = clampDim(req.query.h);
if (!width && !height) return res.status(400).json({ error: 'Pass at least ?w= or ?h=.' });
try {
const output = await sharp(req.file.buffer)
.rotate() // honour EXIF orientation
.resize({ width, height, fit: 'inside', withoutEnlargement: true })
.webp({ quality: 80 })
.toBuffer();
res.type('image/webp').send(output);
} catch (err) {
console.error('resize failed:', err.message);
res.status(422).json({ error: 'Could not process that file as an image.' });
}
});
function clampDim(v) {
const n = parseInt(v, 10);
if (Number.isNaN(n)) return undefined;
return Math.min(Math.max(n, 1), 4000); // 1..4000 px
}
const PORT = process.env.SERVER_PORT || 8080;
app.listen(PORT, '0.0.0.0', () => console.log(`Listening on port ${PORT}`));
Finally a tiny front end at public/index.html so you can test in a browser:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>Image resizer</title></head>
<body>
<h1>Resize an image</h1>
<form action="/resize?w=300" method="post" enctype="multipart/form-data">
<input type="file" name="image" accept="image/*" required>
<button>Resize to 300px wide</button>
</form>
</body>
</html>
Press Start on the Console page. Because a package.json exists, Falix runs npm install automatically before your code — you'll watch Express, multer, and sharp download, then:
Listening on port 25565
That line flips your server to online. sharp ships prebuilt binaries for the server's Linux environment, so there's no compiler step and no native-build failure to worry about — it just lands and loads.
🎯 Good to know:
multer.memoryStorage()keeps each upload in RAM and passes it straight to sharp — nothing is written to disk. That's simpler and avoids leaving stray files behind, at the cost of holding the file in memory while it's processed. The 10 MBfileSizelimit keeps that bounded.
Open your server's address (from the Network page) in a browser, pick an image, and submit. The response is a resized WebP. Or drive it from a terminal:
curl -F "[email protected]" "http://YOUR_ADDRESS:PORT/resize?w=200" -o small.webp
Ask for width only (?w=200) and height scales to match; ask for both and the image fits inside that box without distortion. A 600×400 source requested at w=200 comes back 200×133 — aspect ratio preserved.
The pipeline is four chained calls on the sharp object:
| Call | Why |
|---|---|
.rotate() |
Reads the photo's EXIF orientation flag and bakes it in, so phone photos aren't sideways |
.resize({ width, height, fit: 'inside', withoutEnlargement: true }) |
fit: 'inside' keeps aspect ratio within your box; withoutEnlargement refuses to blow a small image up |
.webp({ quality: 80 }) |
Encodes to WebP — much smaller than JPEG or PNG at the same quality |
.toBuffer() |
Runs the pipeline and returns the bytes, which you send() |
Want PNG or JPEG out instead? Swap .webp(...) for .png() or .jpeg({ quality: 80 }) and change the res.type(...) to match. Everything else beyond these Falix-shaped parts — crops, blurs, watermarks, format detection — is standard sharp; the official docs at sharp.pixelplumbing.com take it from here.
Right now the resizer is stateless — it processes and forgets. To keep thumbnails, write the buffer to disk with sharp(...).toFile('thumbs/name.webp') instead of .toBuffer(), and serve the thumbs/ folder with another express.static. One caveat worth knowing: files you write live in /home/container, and a reinstall (or switching the server's application) wipes them. For anything you can't lose, store the bytes in a managed database or re-generate on demand.
listen call must use SERVER_PORT and 0.0.0.0; see I can't reach my app.Cannot find module 'sharp' — the install didn't finish. Scroll to the top of the console and read the npm install output; or open the Packages page and install sharp there, then restart.image) or you didn't pass ?w= or ?h=.