DEV Community

Cover image for Imagegen as a Service (Free), All Bloggers Should Have One
Minh-Phuc Tran
Minh-Phuc Tran

Posted on • Originally published at phuctm97.com

Imagegen as a Service (Free), All Bloggers Should Have One

Hi folks, I've just built an Imagegen (Image Generator) as a Service. I originally built it as a feature of my website, then DigitalOcean hackathon was announced, so here it is an introduction, a submission, as well as a quick tutorial how I built it. One cool thing about this service is that it is open-sourced, easily to customize for your needs, and can be deployed completely free.

What I built

🌌 Imagegen (Image Generator) as a Service, which is a (REST) API service that can generate dynamic images for different purposes, is especially useful to generate cover images for content distribution:

  • Blogging & writing.

  • Videos' thumbnails.

  • Open-source repositories' social images.

  • etc.

Features:

  • Markdown support.

  • Dark mode enabled.

  • Easily display popular brands' icons by names.

  • Twitter emojis.

  • Requires zero action if you publish posts to DEV.to using DEV.to API.

  • Easily share consistent styles between your content images, embed your name/trademark to all your content.

  • Update all images with a single code change.

  • Cached requests to improve performance and reduce loads.

  • Multiple API versions.

  • There are 2 deployment models:

    • Node.js + Docker, which can be deployed to every cloud platform that supports Docker deployment, e.g. DigitalOcean. The project is pre-configured to deploy to DigitalOcean App Platform with a single click, too.
    • Next.js + Serverless functions, which can be deployed serverlessly to platforms that support Next.js API routes as Serverless functions (only Vercel currently supports).

Category Submission

Well, frankly, it's Random Roulette. Nevertheless, I believe it can be very useful for personal site/portfolio (I use it on my website) and business blogs.

App Link

img-nodejs-dwkvo.ondigitalocean.app

It's an API service, so there's no UI, the API is served at /api/v1 and /api/v2, the URL above will redirect you to the Github repository with details documentation.

Screenshots

Following are some examples of using this service's API, you can copy and paste each URL on your browser to see it yourself.

With title and brands' logos

https://img-nodejs-dwkvo.ondigitalocean.app/api/v2/%F0%9F%97%BE%20**Imagegen**%20as%20a%20Service?&icons=Node.js&icons=Docker&icons=DigitalOcean
Enter fullscreen mode Exit fullscreen mode

🗾 Imagegen as a Service

With Markdown and brands' logos

https://img-nodejs-dwkvo.ondigitalocean.app/api/v2/%F0%9F%91%8B%F0%9F%8F%BB**Hi**%20This%20is%20a%20_generated%20image_%20with%20**Markdown**%20support?icons=dev.to
Enter fullscreen mode Exit fullscreen mode

👋🏻Hi! This is a generated image with Markdown support

With Dark mode

https://img-nodejs-dwkvo.ondigitalocean.app/api/v2/%F0%9F%8C%99%20**Dark%20mode**%20is%20supported,%20too!?theme=dark&icons=dev.to&colors=invert
Enter fullscreen mode Exit fullscreen mode

🌙 Dark mode is supported, too!

Link to Source Code

Node.js + DigitalOcean repository

Next.js + Serverless repository

Permissive License

The project is released under the MIT license and is completely free.

Background

I'd actually still build this project and release it open-source even if DigitalOcean didn't organize the hackathon because it was a part of my website.

My website is where I'll blog very often (currently one post per ~2 days) and document everything I did and learned as a software engineer. One thing I want to achieve building my website is to automatically distribute my content as much as possible, in order to maximize distribution in long term and at scale. It currently supported auto distribution to DEV.to and Hashnode (you're probably reading a distributed copy of the original post).

One part of this automation is to generate cover images for posts and let people know where to find me. ➡️ This service 🤓

I believe automating all that is the a scalable way to grow an audience in the long term, while I can focus my energy on high leverage tasks.

How I built it

This article is already fairly long (compared to typical dev posts), so I'll write a quick concept here and write complete tutorials in separate posts for both Node.js based and Next.js based versions. There are also more details in the project's repository.

Concept

The purpose of this service is to dynamically turn structured data into images. An easy way to think about it is that it is like a React component that receives props then renders visualization, which is an image in this case.

Each API is a HTTP GET at /api/{version}/{...props}:

  • A version represents a component that requires a certain type of props and defines how an image should look.

  • props are constructed using HTTP path and query params.

For example, /api/v2/Hello, World!?theme=dark will render an image in Dark mode with title Hello, World!.

API spec

See img-nodejs#API.

Implementation

Generate images

To be able to render dynamic images, I used Puppeteer to render HTML in a headless Chrome, then take screenshots and render them back to clients.

export default async (req: NextApiRequest, res: NextApiResponse) => {
  const props = parseRequest(req);
  const html = getHTML(props);
  if (isHTMLDebug) {
    res.setHeader("Content-Type", "text/html");
    res.end(html);
    return;
  }

  const { fileType } = props;
  const screenshot = await getScreenshot(html, fileType, {
    width: props.width,
    height: props.height,
  });
  renderScreenshot(res, screenshot, fileType);
};
Enter fullscreen mode Exit fullscreen mode
Configure Docker

There're one tricky part with deployment of this service, especially to DigitalOcean. When developing on a laptop, it's easy because Puppeteer will simply use your installed Chrome instance with admin access and resources attached to it. When deploying to a cloud platform like DigitalOcean, you'll need to install and configure a Chrome instance yourself. So, to make it easier for others, I made a Docker image to prepare everything for you 🥳. Security was also taken care of.

FROM alpine:3.12

# Install dependencies to run Puppeteer in Alpine.
# See https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-on-alpine.
RUN apk add --no-cache \
        chromium \
        nss \
        freetype \
        freetype-dev \
        harfbuzz \
        ca-certificates \
        ttf-freefont \
        nodejs \
        yarn

# Setup app's dependencies and configure envs.
ENV CONTAINERIZED_BROWSER=/usr/bin/chromium-browser
ADD . /app/
WORKDIR /app/
RUN yarn

# Run everything as non-root user for security.
RUN addgroup -S appuser && adduser -S -g appuser appuser \
 && mkdir -p /home/appuser/Downloads /app/ \
 && chown -R appuser:appuser /home/appuser/ \
 && chown -R appuser:appuser /app/
USER appuser
CMD ["yarn", "start"]
Enter fullscreen mode Exit fullscreen mode
Deploy to DigitalOcean

To make deployment even easier, I also created a Deploy to DO button, which enable you to deploy the service to your account with a single click.

Deploy to DO

One cool thing about deploying to DigitalOcean App Platform is that it will take care of setting up proxies, port binding, and SSL (HTTPS) certificates for you. It will also redeploy the service when you push changes to master branch.

Additional Resources/Info

Special thanks to Vercel's og-image, this project is heavily inspired by og-image.

Alright, I hope you like it and deploy an instance successfully for you, feel free to DM me on Twitter if you have any questions. Also, if you're interested in my journey building my blog, open-source, and SaaS products, please consider subscribing to my newsletter or following me on Twitter.

Top comments (2)

Collapse
 
joshuatz profile image
Joshua Tzucker

This looks neat! The theme switching and markdown support are really nice touches.

Collapse
 
phuctm97 profile image
Minh-Phuc Tran

Glad you like it man!