Deploy HTML to markdown converter
Converts HTML to Markdown via REST API. Deploy on Railway.
html-markdown-converter
Just deployed
HTML and Markdown Converter
A lightweight, bidirectional REST API for converting HTML to Markdown using Turndown and Markdown to HTML using marked. Deploy on Railway with zero configuration.
Features
- Fast HTML to Markdown conversion
- Fast Markdown to HTML conversion
- RESTful API with health check and bidirectional conversion endpoints (
/convert,/convert-md) - Configurable — control heading style, list markers, code blocks, emphasis delimiters, and more
- CORS-enabled — call directly from browser-based clients
- TypeScript with full type safety
- Error handling for invalid input
- Multi-stage Docker build — smaller, more secure production image (Node 22, non-root user)
- Railway template metadata (
railway.json) for a polished marketplace listing
Deploy to Railway
One-Click Deploy
Manual Deploy
- Fork or clone this repository to your GitHub account.
- In the Railway dashboard, click New Project → Deploy from GitHub.
- Select your repository.
- Railway auto-detects the Dockerfile and deploys — no configuration needed.
- Access your service at the generated
*.railway.appURL.
Usage
API Endpoints
GET /
Health check. Returns HTML to Markdown Converter is running!.
GET /health
JSON health check. Returns version and uptime information.
{
"status": "ok",
"version": "1.0.0",
"uptime": 1234
}
POST /convert
Converts HTML to Markdown.
Request:
{
"html": "<h1>Hello World</h1><p>This is a <strong>test</strong>.</p>"
}
Response:
{
"markdown": "# Hello World\n\nThis is a **test**."
}
With Options
All Turndown options are supported via an optional options field:
{
"html": "<h1>Hello World</h1><ul><li>Item 1</li><li>Item 2</li></ul>",
"options": {
"headingStyle": "atx",
"bulletListMarker": "-",
"codeBlockStyle": "fenced",
"emDelimiter": "*",
"strongDelimiter": "**",
"linkStyle": "inlined",
"linkReferenceStyle": "full"
}
}
Available options:
| Option | Values | Default |
|---|---|---|
headingStyle | setext, atx | setext |
hr | any string | --- |
bulletListMarker | -, +, * | * |
codeBlockStyle | indented, fenced | indented |
fence | ```, ~~~ | ``` |
emDelimiter | _, * | _ |
strongDelimiter | **, __ | ** |
linkStyle | inlined, referenced | inlined |
linkReferenceStyle | full, collapsed, shortcut | full |
preformattedCode | true, false | false |
Omitting options entirely (or passing an empty object) uses defaults — fully backward compatible.
POST /convert-md
Converts Markdown to HTML.
Request:
{
"markdown": "# Hello World\n\nThis is a **test**."
}
Response:
{
"html": "<h1>Hello World</h1>\n<p>This is a <strong>test</strong>.</p>\n"
}
With Options
Markdown conversion supports an optional options field:
| Option | Values | Default | Description |
|---|---|---|---|
gfm | true, false | true | GitHub Flavored Markdown (tables, strikethrough, task lists, autolinks) |
breaks | true, false | false | Render single newlines (\n) as <br> |
Omitting options entirely (or passing an empty object) uses defaults.
Example with curl
# Basic HTML -> Markdown conversion
curl -X POST https://your-service.up.railway.app/convert \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Hello World</h1><p>This is a <strong>test</strong>.</p>"}'
# HTML -> Markdown with options
curl -X POST https://your-service.up.railway.app/convert \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Hello</h1>", "options": {"headingStyle": "atx"}}'
# Markdown -> HTML conversion
curl -X POST https://your-service.up.railway.app/convert-md \
-H "Content-Type: application/json" \
-d '{"markdown": "# Hello\n\nThis is **bold**."}'
# Markdown -> HTML with breaks enabled
curl -X POST https://your-service.up.railway.app/convert-md \
-H "Content-Type: application/json" \
-d '{"markdown": "line one\nline two", "options": {"breaks": true}}'
# Health check
curl https://your-service.up.railway.app/health
Local Development
Prerequisites
- Node.js 18+
- npm
Setup
git clone https://github.com/INAPP-Mobile/html-markdown-converter
cd html-markdown-converter
cp .env.example .env
npm install
npm run dev # starts with hot-reload at http://localhost:3000
Production build
npm run build
npm start # serves from dist/
Docker
docker build -t html-markdown-converter .
docker run -p 3000:3000 html-markdown-converter
The multi-stage Dockerfile produces a minimal production image based on node:22-alpine running as a non-root user.
Environment Variables
| Variable | Default | Description |
|---|---|---|
PORT | 3000 | Port the server listens on (Railway sets this automatically) |
Customization
Edit src/converter.ts to add custom Turndown rules or marked options. See the Turndown documentation for available options, and the marked documentation for advanced Markdown rendering options.
Project Structure
.
├── .dockerignore # Ignored files for Docker builds
├── .env.example # Example environment variables
├── railway.json # Railway template metadata
├── Dockerfile # Multi-stage production build
├── src/
│ ├── index.ts # Server entrypoint (listens on PORT)
│ ├── app.ts # Express app & routes (/convert, /convert-md)
│ ├── converter.ts # HTML ↔ Markdown conversion logic
│ ├── converter.test.ts # Unit tests for conversion functions
│ └── api.test.ts # API endpoint tests
└── package.json
License
MIT
Template Content
html-markdown-converter
INAPP-Mobile/html-markdown-converter