DEV Community

Syd
Syd

Posted on

How I Deployed a Markdown-to-HTML API on Cloudflare Workers in 10 Lines

I needed to render Markdown in a project but didn't want to bundle a parser. A serverless function felt like overkill for what should be a curl away. Here's what I did.

The Worker

import { marked } from 'marked';

export default {
  async fetch(request) {
    if (request.method !== 'POST')
      return new Response('Send POST', { status: 405 });
    const { text } = await request.json();
    const html = await marked.parse(text);
    return new Response(JSON.stringify({ html }), {
      headers: {
        'content-type': 'application/json',
        'access-control-allow-origin': '*'
      }
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

That's it. One file, one dependency (marked), one export.

The Build

I bundled with esbuild so the Worker is self-contained:

npx esbuild worker.js --bundle --minify --outfile=dist/worker.js
Enter fullscreen mode Exit fullscreen mode

The output is ~54KB (most of it is marked). Then:

npx wrangler deploy
Enter fullscreen mode Exit fullscreen mode

Total time from mkdir to live endpoint: about 10 minutes.

Quick Test

curl -X POST https://md-to-html.sydseriousman.workers.dev \
  -H "Content-Type: application/json" \
  -d '{"text": "# Hello\n\n**bold** and *italic*"}'

# {"html":"<h1>Hello</h1>\n<p><strong>bold</strong> and <em>italic</em></p>"}
Enter fullscreen mode Exit fullscreen mode

Why This Approach

  • No cold starts (Workers runs V8 isolates)
  • Free tier: 100k requests/day
  • No server to patch or maintain
  • CORS enabled for browser use

The Worker URL is directly usable, or I published it through RapidAPI for discoverability and subscription management.

One thing I'd do differently next time: add streaming response support. marked.parse() returns a string, but for large documents, sending chunks as they render would be better UX.

Top comments (0)