How to Create a Thumbnail-image-for-post API like dev.to with JavaScript in 100 Seconds
Richard Wynn
Posted on May 29, 2021
In this article, I will show you step by step to create a thumbnail-image-for-post API like dev.to (just like the image below) by using JavaScript just in 100 seconds ⏰
📂 Repository
- You can download the source code of this article on my Github: https://github.com/richard-wynn/dev-to-thumbnail-image-for-post-api
- If it helps, don't forget to give my repo a star ⭐
🔧 Necessary Stuffs
- NodeJS
- uuid: Used to generate a unique id for every created image
- express: Fast, unopinionated, minimalist web framework for node
- body-parser: Node.js body parsing middleware
- moment: Used to format a given date string
- puppeteer: Used to get the snapshot of our html content
- Postman: Used to test our API
💻 It's Coding Time!
1️⃣ First phase: Create the image-creator module
Create a new folder then run npm init -y
inside it to create a package.json
file.
Next, run the command below to install our necessary packages:
$ npm install uuid express body-parser moment puppeteer
After that, create a child folder called public
(this is the place where the output images will be saved) and update the script
attribute inside package.json
like this:
...
"scripts": {
"start": "node index.js"
},
...
image-creator.js
Create an image-creator.js
file with the following content.
const moment = require('moment');
const { v4 } = require('uuid');
const puppeteer = require('puppeteer');
const fs = require('fs');
const renderHead = () => {
return `
<head>
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #606060;
padding: 82px;
font-size: 38px;
font-family: 'Roboto', sans-serif;
width: 1600px;
}
.post-image-wrapper {
background-color: white;
border: 2px solid black;
border-top-left-radius: 24px;
border-top-right-radius: 24px;
padding: 32px 42px;
box-shadow: 12px 12px 0 black;
margin: 0 auto;
padding-top: 62px;
}
.post-image-title {
font-size: 3em;
}
.post-image-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 132px;
}
.post-image-footer-left {
display: flex;
align-items: center;
}
.post-image-avatar {
width: 64px;
height: 64px;
border-radius: 50%;
border: 3px solid black;
object-fit: cover;
padding: 1px;
margin-right: 10px;
}
.post-image-dot {
margin: 0 12px;
}
.post-image-badge {
width: 64px;
height: 64px;
object-fit: cover;
}
#js-badge {
transform: rotate(-2deg);
}
#dev-to-badge {
transform: rotate(8deg);
margin-left: 3px;
}
</style>
</head>
`;
};
const renderBody = (post) => {
const { title, avatar, full_name, creation_time } = post;
return `
<body>
<div class="post-image-wrapper">
<div class="post-image-header">
<h1 class="post-image-title">${title}</h1>
</div>
<div class="post-image-footer">
<div class="post-image-footer-left">
<img src="${avatar}" alt="Avatar" class="post-image-avatar" />
<span class="post-image-author">${full_name}</span>
<span class="post-image-dot">•</span>
<span class="">${moment(creation_time).format('MMMM DD')}</span>
</div>
<div class="post-image-footer-right">
<div class="post-image-badges">
<img src="https://i.imgur.com/Xe9C9kI.png" alt="JavaScript Badge" class="post-image-badge" id="js-badge" />
<img src="https://i.imgur.com/OW7qG1B.png" alt="Dev.to Badge" class="post-image-badge" id="dev-to-badge" />
</div>
</div>
</div>
</div>
</body>
`;
};
const getImageHtml = (post) => {
return `
<html lang="en">
${renderHead()}
${renderBody(post)}
</html>
`;
};
const createImage = async (post) => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
try {
const fileName = `${v4()}.png`;
await page.setContent(getImageHtml(post));
const content = await page.$('body');
const imageBuffer = await content.screenshot({ omitBackground: true });
fs.writeFileSync(`./public/${fileName}`, imageBuffer);
return fileName;
} catch (error) {
return '';
} finally {
await browser.close();
}
};
module.exports = {
createImage,
};
index.js
Create an index.js
file with the following content.
const { createImage } = require('./image-creator');
(async () => {
const fileName = await createImage({
title:
'How to Create a Thumbnail-image-for-post API like dev.to with JavaScript in 100 seconds',
avatar: 'https://i.imgur.com/bHoLpV6.jpeg',
full_name: 'Richard Wynn',
creation_time: '2021-05-29',
});
console.log(fileName);
})();
Let run npm start
to run the script and hurrayyy, we've got a new image created inside the public folder 😍 😍
2️⃣ Second phase: Create an image-creator API with Express.js
Let update index.js
with the following content:
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
var os = require('os');
const { createImage } = require('./image-creator');
const port = process.env.PORT || 5000;
const app = express();
// Configure body-parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Serve static files
app.use('/public', express.static(path.join(__dirname, 'public')));
app.post('/', async (req, res) => {
try {
const data = req.body;
const errors = {};
if (!data['title'] || data['title'] === '') {
errors['title'] = 'Title is required!';
}
if (!data['avatar'] || data['avatar'] === '') {
errors['avatar'] = 'Avatar is required!';
}
if (!data['full_name'] || data['full_name'] === '') {
errors['full_name'] = 'Full name is required!';
}
if (!data['creation_time'] || data['creation_time'] === '') {
errors['creation_time'] = 'Creation time is required!';
}
if (Object.keys(errors).length > 0) {
return res.status(500).json({
status: 'FAILED',
message: 'Failed to create a thumbnail image for this post!',
errors,
});
}
const fileName = await createImage(data);
return res.status(200).json({
status: 'SUCCESS',
message: 'Create a thumbnail image successfully!',
data: `/public/${fileName}`,
});
} catch (error) {
console.log(error);
return res.status(500).json({
status: 'FAILED',
message: 'Failed to create a thumbnail image for this post!',
});
}
});
app.listen(port, (err) => {
if (!err) {
console.log(`Server is listening on port ${port}...`);
}
});
Let run npm start
to run the script and use Postman to make a request to http://localhost:5000
with the following JSON data:
{
"title": "How to Create a Thumbnail-image-for-post API like dev.to with JavaScript in 100 seconds",
"avatar": "https://i.imgur.com/bHoLpV6.jpeg",
"full_name": "Richard Wynn",
"creation_time": "2021-05-29"
}
Click Send
and boom, we've got a link to the image:
Then make another request to the link to see what we get:
Hurrayyyy, it works like a charm 🎉🎉
This is just a for-fun project of mine. It it helps, don't forget to give my Github repo a star or a like to this post 😉😉
📱 Keep in Touch
If you like this article, don't forget to follow and stay in touch with my latest ones in the future by following me via:
- Twitter: https://twitter.com/RichardWynn01
- Medium: https://richard-wynn.medium.com
- Github: https://github.com/richard-wynn
Posted on May 29, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
May 29, 2021