Dev.to · 6 min read

My Dev.to CLI Got Its First Community PR. Image Uploads From Terminal.

Three weeks after launching devpub, I got a notification I wasn't expecting: a pull request from someone I'd never talked to. Harish / @harishteens had forked the repo, read the issues, picked one that I'd been putting off for weeks, and built a complete solution. Tests included. Design decisions documented. Edge cases handled. The feature? devpub upload. The one command that should have existed from day one but couldn't, because the Forem API literally doesn't support it. pip install devpub==0.3.0 devpub upload cover.png That one command now gives you this: Uploaded: cover.png Uploaded Images ┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ File ┃ URL ┃ ┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ cover.png │ https://dev-to-uploads.s3.amazonaws.com/… │ └──────────────┴──────────────────────────────────────────┘ No more opening dev.to/new in the browser just to drag an image and copy the URL. Table of Contents The problem How devpub upload works The authentication problem What Harish built Try it What's next The problem: no image endpoint in the API When I launched devpub in v0.1, the goal was to replace the Dev.to web editor entirely. Write locally, push with one command, track analytics. Done. But there was a gap. Every time I wrote an article with diagrams or a cover image, I had to: Open dev.to/new in the browser Click the image upload button Select the file Wait for upload Copy the URL Paste it into my local markdown Close the tab Seven steps for something that should be devpub upload figure.png. The reason I hadn't built this: the Forem API V1 has no image upload endpoint. It doesn't exist. You can set cover_image in article frontmatter, but only to a URL that already exists somewhere. The API cannot create that URL. I filed issue #11 with a note saying "this requires reverse-engineering the web editor's upload mechanism" and moved on to other features. Harish didn't move on. He figured it out. How devpub upload works The Dev.to web editor uploads images to POST /image_uploads. It's a multipart form submission, same as any file upload. But it's not authenticated with your API key. It uses your browser session. # Upload a single image devpub upload cover.png # Upload multiple images devpub upload *.png # Get ready-to-paste Markdown devpub upload architecture.png --markdown # Output: ![architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/...) The --markdown flag is the one I use most. Write your article with ![](./figures/diagram.png), then run devpub upload figures/*.png --markdown and paste the output directly over your local references. The authentication problem Here's why this feature sat undone for three weeks. The /image_uploads endpoint requires: A _Devto_Forem_Session cookie (your login session) A CSRF token (anti-forgery protection) An Origin header matching https://dev.to Your API key? Useless. This endpoint doesn't accept it. So devpub needs two extra credentials beyond your API key. They live in .devpub/.env (which devpub init gitignores by default): DEVPUB_SESSION_COOKIE=_Devto_Forem_Session=abc123... DEVPUB_CSRF_TOKEN=a1b2c3d4... If you run devpub upload without setting these, it doesn't just fail with a cryptic error. It shows you exactly where to find them: ╭─ Session Credentials Required ─────────────────────────────╮ │ │ │ devpub upload needs your browser session (not API key). │ │ │ │ 1. Open https://dev.to/new in Chrome │ │ 2. Press F12 → Application → Cookies → dev.to │ │ 3. Copy _Devto_Forem_Session value │ │ 4. View page source → find │ │ 5. Add both to .devpub/.env │ │ │ ╰─────────────────────────────────────────────────────────────╯ One deliberate design choice: these are login credentials, not a scoped token. The .env file is gitignored, and when they expire (you'll get a 401 or 403), you just re-copy them. devpub tells you that upfront rather than leaving you to guess why uploads suddenly stopped working. What Harish built Harish's PR wasn't a quick hack. It was a proper module with clear boundaries: File Role src/devpub/api/uploads.py ImageUploader class with all HTTP logic src/devpub/cli/images.py Click command + Rich table output tests/test_uploads.py 29 tests, all HTTP-mocked with respx Three things stood out in his implementation: 1. Fail before the network. File existence, extension validation, and size check (25 MB limit) all happen before any HTTP request fires. A typo in a filename costs zero network round-trips. 2. Flexible response parsing. Dev.to's upload response isn't documented, so the _extract_url method handles every response shape that's been observed: links.url, image.url, images[0], a raw string. If none match, it raises with the full response body included. A wrong URL silently landing in your article would be worse than a loud error. 3. Cookie flexibility. You can paste the full cookie header (a=1; b=2; _Devto_Forem_Session=xyz) or just the session value. Both work. Because copying one value out of DevTools is easy to get wrong. The PR description was thorough. Design rationale for keeping ImageUploader separate from DevtoClient (different auth models shouldn't share a class). Explicit call-out that tests pin the request shape, not the live response. A suggestion to smoke-test against a real session before release. This is what good open source contributions look like. Not just code that works, but code that explains why it works that way. Other changes in v0.3.0 Beyond the upload feature: Fixed hardcoded User-Agent: Was stuck at devpub/0.1.0. Now reads the actual version from importlib.metadata. Fixed empty API key edge case: DevtoClient(api_key='') used to fall through to environment variables silently. Now it raises. Repo cleanup: Removed personal draft articles from tracking, updated .gitignore for research docs and recordings. Try it yourself pip install devpub==0.3.0 For uploads, you need the session credentials (one-time setup): # Add to .devpub/.env DEVPUB_SESSION_COOKIE=your_session_cookie_here DEVPUB_CSRF_TOKEN=your_csrf_token_here Then: devpub upload cover.png # Upload and get URL devpub upload *.png --markdown # Ready-to-paste Markdown tags devpub upload diagram.png --dry-run # Validate without uploading Full source: github.com/simplynadaf/devpub What's next Two follow-ups that Harish explicitly scoped out of his PR (smart -- ship the primitive first, then build on it): Auto-rewrite during push: devpub push could detect local image paths like ![](./figures/arch.png), upload them automatically, and rewrite the URLs in-place before publishing. Cover image shortcut: devpub push -f article.md --cover photo.png to upload the image and set cover_image in frontmatter in one step. Both become trivial now that the upload primitive exists. Shoutout Big thanks to Harish @harishteens for the first external contribution to devpub. The PR was clean, well-tested, and properly documented. If you're looking for an open-source project to contribute to, devpub has open issues ranging from beginner-friendly to architecturally interesting. What's your image workflow for Dev.to articles? Drag-and-drop in the browser? Hosted on GitHub? Imgur? I'd like to know if devpub upload fills a gap people feel, or if everyone's already solved this differently. Follow me for more on AWS architecture, DevOps, and AI Infrastructure: Portfolio | LinkedIn | Dev.to | YouTube | Email | AWS Builder Center | X

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News