<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sourabh Choraria</title>
    <description>The latest articles on Forem by Sourabh Choraria (@choraria).</description>
    <link>https://forem.com/choraria</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F248754%2F10656afa-9c06-4635-8da7-060d03d195a6.jpg</url>
      <title>Forem: Sourabh Choraria</title>
      <link>https://forem.com/choraria</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/choraria"/>
    <language>en</language>
    <item>
      <title>I think most teams are lying to themselves about usernames</title>
      <dc:creator>Sourabh Choraria</dc:creator>
      <pubDate>Fri, 26 Dec 2025 18:12:27 +0000</pubDate>
      <link>https://forem.com/choraria/i-think-most-teams-are-lying-to-themselves-about-usernames-1nd7</link>
      <guid>https://forem.com/choraria/i-think-most-teams-are-lying-to-themselves-about-usernames-1nd7</guid>
      <description>&lt;p&gt;I don't think usernames are a solved problem.&lt;/p&gt;

&lt;p&gt;In fact, I think a lot of teams are quietly lying to themselves about how fragile their username logic actually is.&lt;/p&gt;

&lt;p&gt;I've done it too.&lt;/p&gt;

&lt;p&gt;Early on, usernames feel boring. You add a uniqueness constraint, maybe a regex, block a few obvious words, and ship. It passes code review. It works in staging. Nobody complains.&lt;/p&gt;

&lt;p&gt;Then real users show up.&lt;/p&gt;

&lt;p&gt;And suddenly you're dealing with impersonation, abuse, legal edge cases, support tickets, and weird Unicode tricks you didn't even know existed.&lt;/p&gt;

&lt;p&gt;This post is about why I stopped pretending this was a solved problem, what pushed me to build &lt;a href="https://www.username.dev/" rel="noopener noreferrer"&gt;username.dev&lt;/a&gt;, and what I learned after getting called out on Hacker News.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fex97tdvxg91zjeohdqhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fex97tdvxg91zjeohdqhb.png" alt="username.dev homepage" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The lie we tell ourselves
&lt;/h2&gt;

&lt;p&gt;The lie sounds like this: "We'll handle usernames later."&lt;/p&gt;

&lt;p&gt;Most products start with a single question: is this username available?&lt;/p&gt;

&lt;p&gt;But the moment you ship to real users, that question explodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this a brand name?&lt;/li&gt;
&lt;li&gt;Is this a public figure?&lt;/li&gt;
&lt;li&gt;Is this a city or country?&lt;/li&gt;
&lt;li&gt;Is this offensive in another language?&lt;/li&gt;
&lt;li&gt;Is this a system term like &lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;root&lt;/code&gt;, or &lt;code&gt;support&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Is this a premium or high-demand handle that should be treated differently?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every product that allows user-generated identities eventually runs into the same quiet problem: usernames.&lt;/p&gt;

&lt;p&gt;The difference is &lt;em&gt;when&lt;/em&gt; you choose to confront it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this usually goes wrong
&lt;/h2&gt;

&lt;p&gt;In practice, I see three common approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A growing regex list&lt;/li&gt;
&lt;li&gt;A blacklist table someone manually edits&lt;/li&gt;
&lt;li&gt;Post-hoc moderation after damage is already done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've implemented all three.&lt;/p&gt;

&lt;p&gt;They all work at the beginning. They all fall apart under real usage.&lt;/p&gt;

&lt;p&gt;Regex lists turn into unreadable landmines. Blacklists grow without structure or context. Moderation queues become a tax you never budgeted for.&lt;/p&gt;

&lt;p&gt;The worst part is that all of these approaches only answer yes or no. They don't tell you &lt;em&gt;what&lt;/em&gt; the username actually is, or why it should be treated differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistakes I kept repeating
&lt;/h2&gt;

&lt;p&gt;I kept seeing the same patterns across different products:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocking English profanity while missing obvious abuse in other languages&lt;/li&gt;
&lt;li&gt;Forgetting to &lt;a href="https://www.username.dev/username-rules/system" rel="noopener noreferrer"&gt;reserve internal paths&lt;/a&gt; and roles&lt;/li&gt;
&lt;li&gt;Letting brand impersonation slip through until legal got involved&lt;/li&gt;
&lt;li&gt;Realizing too late that short dictionary words had real economic value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once usernames are live, fixing any of this is painful. Renaming users is messy. Policies change. Trust erodes.&lt;/p&gt;

&lt;p&gt;That's the part people underestimate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I decided to build instead
&lt;/h2&gt;

&lt;p&gt;I stopped building a &lt;a href="https://www.username.dev/reserved-usernames" rel="noopener noreferrer"&gt;reserved usernames list&lt;/a&gt; or trying to answer "is this allowed?" and started asking a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What &lt;em&gt;is&lt;/em&gt; this username?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="//username.dev"&gt;username.dev&lt;/a&gt; is an API that categorizes usernames instead of just approving or rejecting them.&lt;/p&gt;

&lt;p&gt;It tells you whether a username is a brand, a public figure, a city, a system term, a dictionary word, or something else entirely, along with relevant metadata.&lt;/p&gt;

&lt;p&gt;That context matters.&lt;/p&gt;

&lt;p&gt;Once you have structured information, you can make your own decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block system terms outright&lt;/li&gt;
&lt;li&gt;Flag brands for review&lt;/li&gt;
&lt;li&gt;Prevent impersonation&lt;/li&gt;
&lt;li&gt;Treat premium handles differently&lt;/li&gt;
&lt;li&gt;Apply different rules in different parts of your product&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not trying to enforce policy. I'm trying to give teams a reliable source of truth so they don't have to reinvent this logic over and over again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I finally shipped this
&lt;/h2&gt;

&lt;p&gt;The honest answer is exhaustion.&lt;/p&gt;

&lt;p&gt;I was tired of seeing the same bugs reappear in different codebases. Tired of seeing teams patch symptoms instead of causes. Tired of hearing "we'll fix it later" about something that sits at the core of user identity.&lt;/p&gt;

&lt;p&gt;Usernames feel small until they aren't. By the time they hurt, they're already everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  What other teams told me
&lt;/h2&gt;

&lt;p&gt;Once I started talking about this publicly, the responses were familiar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"We didn't think users would actually try that."&lt;/li&gt;
&lt;li&gt;"We have a list somewhere."&lt;/li&gt;
&lt;li&gt;"We only block English words."&lt;/li&gt;
&lt;li&gt;"We'll clean it up after launch."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Almost every team treats usernames as a UI concern instead of an identity, trust, and safety concern.&lt;/p&gt;

&lt;p&gt;That's usually a mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting called out on Hacker News
&lt;/h2&gt;

&lt;p&gt;When this hit Hacker News, I got pushback. Some of it was blunt. Some of it was uncomfortable. Most of it was fair.&lt;/p&gt;

&lt;p&gt;People pointed out that users will always find ways around naive guardrails. They questioned whether categorization alone was enough. They challenged assumptions about coverage and completeness.&lt;/p&gt;

&lt;p&gt;What I learned from that thread is that this needs to be a foundation, not a silver bullet.&lt;/p&gt;

&lt;p&gt;The API is meant to be an exhaustive, centralized source of structured data. Variations, heuristics, and policy decisions belong on top of that layer.&lt;/p&gt;

&lt;p&gt;HN also validated something important: nobody argued that the problem doesn't exist. The skepticism was about execution, not relevance.&lt;/p&gt;

&lt;p&gt;That told me I was working on a real problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  This problem is already costing you
&lt;/h2&gt;

&lt;p&gt;If your product has usernames, you're already paying for this. You're just paying in hidden ways: engineering time, support load, legal risk, or user trust.&lt;/p&gt;

&lt;p&gt;You can pay that cost reactively, or you can deal with it earlier when the surface area is smaller.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="//username.dev"&gt;username.dev&lt;/a&gt; because I didn't want to keep paying it the hard way.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to poke at it
&lt;/h2&gt;

&lt;p&gt;There's a free tier with no credit card. It's there so you can test your existing logic against a more exhaustive dataset and see what falls through.&lt;/p&gt;

&lt;p&gt;At worst, you'll confirm your approach is solid. At best, you'll catch something before your users or your lawyers do.&lt;/p&gt;

&lt;p&gt;Either way, you'll learn something.&lt;/p&gt;

&lt;p&gt;That alone makes it worth testing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>api</category>
    </item>
    <item>
      <title>Webhooks for Sheets</title>
      <dc:creator>Sourabh Choraria</dc:creator>
      <pubDate>Thu, 14 Apr 2022 18:51:38 +0000</pubDate>
      <link>https://forem.com/choraria/webhooks-for-sheets-3o1b</link>
      <guid>https://forem.com/choraria/webhooks-for-sheets-3o1b</guid>
      <description>&lt;p&gt;Hey peeps 👋🏾&lt;/p&gt;

&lt;p&gt;Just a quick share here that my upcoming product — &lt;strong&gt;Webhooks for Sheets&lt;/strong&gt; is now LIVE on Product Hunt and you can start your experiments with the add-on here —&lt;br&gt;
&lt;a href="https://www.producthunt.com/posts/webhooks-for-sheets" rel="noopener noreferrer"&gt;https://www.producthunt.com/posts/webhooks-for-sheets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vno1rng2zgntdr0du2o.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vno1rng2zgntdr0du2o.jpeg" alt="Webhooks for Sheets" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love to hear back from you about ways in which the tool can be improved or if you'd like to contribute to the actual Webhook code, you can do that too from here —&lt;br&gt;
&lt;a href="https://github.com/choraria/google-apps-script/blob/master/Sheets/Webhooks" rel="noopener noreferrer"&gt;https://github.com/choraria/google-apps-script/blob/master/Sheets/Webhooks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll continue sharing more updates on &lt;a href="https://twitter.com/choraria" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; and &lt;a href="https://www.linkedin.com/in/choraria/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; so feel free to follow along and engage in whatever way you feel like ☺️&lt;/p&gt;

&lt;p&gt;Cheers,&lt;br&gt;
Sourabh&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>googleappsscript</category>
    </item>
    <item>
      <title>develop &amp; implement some of the most common uses of puppeteer via pptr.io</title>
      <dc:creator>Sourabh Choraria</dc:creator>
      <pubDate>Sat, 09 Oct 2021 11:05:30 +0000</pubDate>
      <link>https://forem.com/choraria/develop-implement-some-of-the-most-common-uses-of-puppeteer-via-pptr-io-2i5f</link>
      <guid>https://forem.com/choraria/develop-implement-some-of-the-most-common-uses-of-puppeteer-via-pptr-io-2i5f</guid>
      <description>&lt;h2&gt;
  
  
  background
&lt;/h2&gt;

&lt;p&gt;in case this is the very first time you're hearing about &lt;strong&gt;puppeteer&lt;/strong&gt;, i'd highly recommend googling what it does first and also refer to the &lt;a href="https://developers.google.com/web/tools/puppeteer" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; to get a quick sense of what it's all about.&lt;/p&gt;

&lt;p&gt;here's a video from the docs that should help you get started too —&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/lhZOFUY1weo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  intro
&lt;/h2&gt;

&lt;p&gt;a couple days ago, i launched &lt;a href="https://pptr.io" rel="noopener noreferrer"&gt;pptr.io&lt;/a&gt; with the purpose to deliver a free &amp;amp; open source api that runs puppeteer as a service — think of it a spin-off of the original &lt;a href="https://github.com/GoogleChromeLabs/pptraas.com" rel="noopener noreferrer"&gt;pptraas.com&lt;/a&gt; (defunct) tool.&lt;/p&gt;

&lt;p&gt;the entire service is powered by &lt;a href="https://vercel.com/?utm_source=pptr-io&amp;amp;utm_campaign=oss" rel="noopener noreferrer"&gt;vercel&lt;/a&gt; — specifically, it's &lt;a href="https://vercel.com/docs/concepts/functions/introduction" rel="noopener noreferrer"&gt;serverless functions&lt;/a&gt; offering.&lt;/p&gt;

&lt;p&gt;as of this post, the api is able to deliver the following —&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;capture a &lt;a href="https://github.com/choraria/pptr-io#screenshot" rel="noopener noreferrer"&gt;screenshot&lt;/a&gt; from a given url&lt;/li&gt;
&lt;li&gt;fetch &lt;a href="https://github.com/choraria/pptr-io#metrics" rel="noopener noreferrer"&gt;metrics&lt;/a&gt; of a page&lt;/li&gt;
&lt;li&gt;get a timeline &lt;a href="https://github.com/choraria/pptr-io#trace" rel="noopener noreferrer"&gt;trace&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;render page as a &lt;a href="https://github.com/choraria/pptr-io#pdf" rel="noopener noreferrer"&gt;pdf&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;...more to be added soon 😉&lt;/p&gt;

&lt;p&gt;i've ensured to keep the documentation as simple as possible and you can find all you need on that here —&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/choraria" rel="noopener noreferrer"&gt;
        choraria
      &lt;/a&gt; / &lt;a href="https://github.com/choraria/pptr-io" rel="noopener noreferrer"&gt;
        pptr-io
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      puppeteer as a service.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;pptr.io&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;a free and open-source api that runs &lt;a href="https://developers.google.com/web/tools/puppeteer" rel="nofollow noopener noreferrer"&gt;puppeteer&lt;/a&gt; as a service.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/choraria/pptr-io/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e5052a68623835f0664ca8c21ab9f4a8b2ca7a0bcdf2e90fd8c763abd6f9e5a9/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f63686f72617269612f707074722d696f" alt="License"&gt;&lt;/a&gt; &lt;a href="https://github.com/choraria/pptr-io/deployments/activity_log?environment=Production" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4e5ce98180ee607024389055e5f5461cac38b97004743f32a805cd42e3d0b308/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6465706c6f796d656e74732f63686f72617269612f707074722d696f2f70726f64756374696f6e3f6c6162656c3d76657263656c266c6f676f3d76657263656c" alt="vercel"&gt;&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/puppeteer-core" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/da667ce23d838d5f56fcdc9a124c1e3d1b9a93a10c089ec744fbee6c22d6e2ae/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7061636b6167652d6a736f6e2f646570656e64656e63792d76657273696f6e2f63686f72617269612f707074722d696f2f7075707065746565722d636f7265" alt="puppeteer-core"&gt;&lt;/a&gt; &lt;a href="https://www.npmjs.com/package/chrome-aws-lambda" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/12292a7cf3744e12d46b2d32a3202a9c1df9a8b41621f291b0ee036da89bd643/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f7061636b6167652d6a736f6e2f646570656e64656e63792d76657273696f6e2f63686f72617269612f707074722d696f2f6368726f6d652d6177732d6c616d626461" alt="chrome-aws-lambda"&gt;&lt;/a&gt; &lt;a href="https://twitter.com/intent/user?screen_name=choraria" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a711008ca35a1c544e1041532347ca762a15f37de461bf37eb849fdcd52261f0/68747470733a2f2f696d672e736869656c64732e696f2f747769747465722f666f6c6c6f772f63686f72617269613f6c6162656c3d25343063686f7261726961267374796c653d736f6369616c" alt="twitter"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://vercel.com?utm_source=pptr-io&amp;amp;utm_campaign=oss" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5f61aa4d8bdac5f5eb273d5028ddd24567cd356fb4c0b5e313f56f79246f64ef/68747470733a2f2f7777772e6461746f636d732d6173736574732e636f6d2f33313034392f313631383938333239372d706f77657265642d62792d76657263656c2e737667" alt="powered by vercel"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;table of contents&lt;/h1&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/choraria/pptr-io#usage" rel="noopener noreferrer"&gt;usage&lt;/a&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#screenshot" rel="noopener noreferrer"&gt;screenshot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#metrics" rel="noopener noreferrer"&gt;metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#trace" rel="noopener noreferrer"&gt;trace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#pdf" rel="noopener noreferrer"&gt;pdf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#meta" rel="noopener noreferrer"&gt;meta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#duckduckgo_profiles" rel="noopener noreferrer"&gt;duckduckgo_profiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#version" rel="noopener noreferrer"&gt;version&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#contributing" rel="noopener noreferrer"&gt;contributing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#credits" rel="noopener noreferrer"&gt;credits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#license" rel="noopener noreferrer"&gt;license&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/choraria/pptr-io#host-your-own" rel="noopener noreferrer"&gt;host your own&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;usage&lt;/h1&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;base url:
&lt;ul&gt;
&lt;li&gt;production: &lt;code&gt;https://pptr.io/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;preview (staging): &lt;code&gt;https://dev.pptr.io/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;default path: &lt;code&gt;api/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;endpoint: any one of the individual &lt;code&gt;.js&lt;/code&gt; files in the &lt;a href="https://github.com/api" rel="noopener noreferrer"&gt;api&lt;/a&gt; folder
&lt;ul&gt;
&lt;li&gt;ignore the &lt;code&gt;index.js&lt;/code&gt; file inside the &lt;code&gt;api&lt;/code&gt; folder&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;endpoints&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;screenshot&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;task: takes a screenshot of the input url&lt;/li&gt;
&lt;li&gt;class: &lt;a href="https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagescreenshotoptions" rel="noopener noreferrer"&gt;page.screenshot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;method: &lt;code&gt;GET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;api: &lt;code&gt;https://pptr.io/api/screenshot?url=https://dev.to&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;source: &lt;a href="https://github.com/api/screenshot.js" rel="noopener noreferrer"&gt;screenshot.js&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;optional params&lt;/th&gt;
&lt;th&gt;type&lt;/th&gt;
&lt;th&gt;description&lt;/th&gt;
&lt;th&gt;default&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;width&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;width of the screenshot.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1920&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;height&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;height of the screenshot.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1080&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deviceScaleFactor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;device scale factor (can be thought of as DPR).&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fullPage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;when &lt;code&gt;true&lt;/code&gt;, takes a screenshot of the full scrollable page.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;can be either &lt;code&gt;jpeg&lt;/code&gt;, &lt;code&gt;png&lt;/code&gt; or &lt;code&gt;webp&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;png&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

sample output of the screenshot endpoint
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/946e8d061a6125d5f0d7672f1541973bf5a5e4348afd7280bda2b9119f818d9c/68747470733a2f2f707074722e696f2f6170692f73637265656e73686f743f75726c3d68747470733a2f2f6465762e746f"&gt;&lt;img src="https://camo.githubusercontent.com/946e8d061a6125d5f0d7672f1541973bf5a5e4348afd7280bda2b9119f818d9c/68747470733a2f2f707074722e696f2f6170692f73637265656e73686f743f75726c3d68747470733a2f2f6465762e746f" alt="screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;metrics&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;task: fetch metrics of the page&lt;/li&gt;
&lt;li&gt;class: &lt;a href="https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagemetrics" rel="noopener noreferrer"&gt;page.metrics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/choraria/pptr-io" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  contribute
&lt;/h2&gt;

&lt;p&gt;i'm actively working to —&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;partner with folks who'd want to add more methods to the api.&lt;/li&gt;
&lt;li&gt;check-in with the community on some feedback &amp;amp; inspiration on what new can be done.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  discuss
&lt;/h2&gt;

&lt;p&gt;feel free to reach out in case you have any cool problem statements that you'd like to see solved using this api —&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1444732327133806595-194" src="https://platform.twitter.com/embed/Tweet.html?id=1444732327133806595"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1444732327133806595-194');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1444732327133806595&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>puppeteer</category>
      <category>opensource</category>
      <category>hacktoberfest</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Workbook Statistics - Google Spreadsheet Add-on</title>
      <dc:creator>Sourabh Choraria</dc:creator>
      <pubDate>Fri, 10 Jan 2020 19:32:57 +0000</pubDate>
      <link>https://forem.com/choraria/workbook-statistics-google-spreadsheet-add-on-jgb</link>
      <guid>https://forem.com/choraria/workbook-statistics-google-spreadsheet-add-on-jgb</guid>
      <description>&lt;p&gt;&lt;a href="https://www.producthunt.com/posts/workbook-statistics?utm_source=badge-featured&amp;amp;utm_medium=badge&amp;amp;utm_souce=badge-workbook-statistics" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.producthunt.com%2Fwidgets%2Fembed-image%2Fv1%2Ffeatured.svg%3Fpost_id%3D180567%26theme%3Ddark" alt="Workbook Statistics - Get stats for your entire Google Spreadsheet in one place. | Product Hunt Embed" width="250" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Get stats for your current active sheet and the entire workbook in one place.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;blockquote data-conversation="none" data-lang="en"&gt;
&lt;p&gt;Finally! Its live on the G Suite Marketplace 🎉&lt;br&gt;&lt;br&gt;Would love for some of you to critique it so I can improve 😅 will soon put out the code on GitHub and open-source this + also share a post on the entire process.&lt;a href="https://t.co/5koer6xQt2" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://t.co/5koer6xQt2" rel="noopener noreferrer"&gt;https://t.co/5koer6xQt2&lt;/a&gt; &lt;a href="https://t.co/goyRZFYEoJ" rel="noopener noreferrer"&gt;pic.twitter.com/goyRZFYEoJ&lt;/a&gt;&lt;/p&gt;— Sourabh Choraria (@schoraria911) &lt;a href="https://twitter.com/schoraria911/status/1215666955132133377?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;January 10, 2020&lt;/a&gt;
&lt;/blockquote&gt; 

&lt;p&gt;Post install tip:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refresh the sheet&lt;/li&gt;
&lt;li&gt;Navigate to the 'Add-ons &amp;gt; Workbook Statistics' menu&lt;/li&gt;
&lt;li&gt;Click on 'Get Stats' and wait until the add-on fetches the details&lt;/li&gt;
&lt;li&gt;Re-navigate to the 'Workbook Statistics' option and then hover over any of the 3 available options&lt;/li&gt;
&lt;li&gt;Hit 'Refresh All' to update stats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find stats for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current Sheet:

&lt;ul&gt;
&lt;li&gt;Sheet Name&lt;/li&gt;
&lt;li&gt;End of Sheet = Cell reference of where the sheet ends&lt;/li&gt;
&lt;li&gt;Cells with Data&lt;/li&gt;
&lt;li&gt;Named Ranges - similar to "Tables" in MS Excel&lt;/li&gt;
&lt;li&gt;Pivot Tables&lt;/li&gt;
&lt;li&gt;Cells with Formulas&lt;/li&gt;
&lt;li&gt;Charts&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Entire Workbook:

&lt;ul&gt;
&lt;li&gt;Count of Sheets&lt;/li&gt;
&lt;li&gt;Cells with Data&lt;/li&gt;
&lt;li&gt;Named Ranges&lt;/li&gt;
&lt;li&gt;Pivot Tables&lt;/li&gt;
&lt;li&gt;Cells with Formulas&lt;/li&gt;
&lt;li&gt;Charts&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You can always connect with me on &lt;a href="https://twitter.com/schoraria911" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; to share any suggestions or feedback as well!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>googleappsscript</category>
      <category>gsuite</category>
      <category>addon</category>
    </item>
    <item>
      <title>Build a Telegram Bot using Google Apps Script</title>
      <dc:creator>Sourabh Choraria</dc:creator>
      <pubDate>Sun, 27 Oct 2019 08:04:18 +0000</pubDate>
      <link>https://forem.com/choraria/build-a-telegram-bot-using-google-apps-script-2a9f</link>
      <guid>https://forem.com/choraria/build-a-telegram-bot-using-google-apps-script-2a9f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on &lt;a href="https://script.gs/build-a-telegram-bot-using-apps-script/" rel="noopener noreferrer"&gt;script.gs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I just launched &lt;a href="https://t.me/SphygmoBot" rel="noopener noreferrer"&gt;SphygmoBot&lt;/a&gt; which is a human "Heartbeat" checker that pings you every 24 hours to see if you're alive and notifies someone in case your response fails.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.producthunt.com/posts/sphygmobot?utm_source=badge-featured&amp;amp;utm_medium=badge&amp;amp;utm_souce=badge-sphygmobot" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.producthunt.com%2Fwidgets%2Fembed-image%2Fv1%2Ffeatured.svg%3Fpost_id%3D172472%26theme%3Ddark" alt="SphygmoBot - A human " width="250" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Apps Script for it's backend architecture&lt;/li&gt;
&lt;li&gt;Google Spreadsheet as a database&lt;/li&gt;
&lt;li&gt;Telegram Bot API and it's &lt;a href="https://core.telegram.org/bots/api#setwebhook" rel="noopener noreferrer"&gt;webhook services&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UAYKd4kpOzM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You can access the entire codebase on my GitHub repository &lt;a href="https://github.com/schoraria911/gas-SphygmoBot" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>googleappsscript</category>
      <category>telegram</category>
      <category>bot</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Build a URL Shortening service using Google Apps Script</title>
      <dc:creator>Sourabh Choraria</dc:creator>
      <pubDate>Sun, 13 Oct 2019 09:31:51 +0000</pubDate>
      <link>https://forem.com/choraria/build-a-url-shortening-service-using-google-apps-script-3ple</link>
      <guid>https://forem.com/choraria/build-a-url-shortening-service-using-google-apps-script-3ple</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted on &lt;a href="https://script.gs/url-shortener-powered-by-google-apps-script/" rel="noopener noreferrer"&gt;script.gs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It all started when I came across Jonathon Broughton's technique to use &lt;a href="https://github.com/stardotbmp/slack-gas-signup#github-pages" rel="noopener noreferrer"&gt;Apps Script web apps within embeded ifames in Github Pages&lt;/a&gt;, which got my gears turning for I was jonesing to make use of an &lt;a href="https://stackoverflow.com/questions/58172084/grab-web-app-link-info-besides-querystring-parameters/58172160#58172160" rel="noopener noreferrer"&gt;undocumented parameter&lt;/a&gt; within Apps Script's &lt;code&gt;doGet&lt;/code&gt; function - &lt;code&gt;pathInfo&lt;/code&gt;; which in the end, blew up on my face and I rolled back to using &lt;strong&gt;queryString&lt;/strong&gt; instead.&lt;/p&gt;

&lt;p&gt;You can access the working demo on (&lt;a href="https://tmt.pw" rel="noopener noreferrer"&gt;https://tmt.pw&lt;/a&gt;) and refer the entire codebase (with documented instructions) on &lt;a href="https://github.com/schoraria911/gas-url-shortener" rel="noopener noreferrer"&gt;my github repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zfzo0ci97p2f4u6mu7u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zfzo0ci97p2f4u6mu7u.gif" alt="tmt-demo" width="760" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The architecture is rather not that of a conventional URL shortening and redirection service because my methodology does not throw a 301/302 permanent redirect status but instead, uses JavaScript's &lt;code&gt;window.open&lt;/code&gt; to have a new URL loaded on the same browser window (using &lt;code&gt;_top&lt;/code&gt;), based on the slug.&lt;/p&gt;

&lt;p&gt;Midway through the development I get to know that Github Pages serves a 404 if it doesn't recognise the path or have a resource already allocated against it in the project repository and that's what led me to plug Rafael Pedicini's &lt;a href="https://github.com/rafrex/spa-github-pages" rel="noopener noreferrer"&gt;Single Page Apps for GitHub Pages&lt;/a&gt; - I doubt I'd have been able to host my service on Github Pages (for free), had it not been for this ingenious technique to deploy single page apps.&lt;/p&gt;

&lt;p&gt;Access to the original resources can be found here -&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The entire folder on my &lt;a href="https://drive.google.com/drive/folders/1SuHsBLbSYm5dSnp3NeGmUOTnC9NPqEje?usp=sharing" rel="noopener noreferrer"&gt;Google Drive&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A Spreadsheet that acts as the &lt;a href="https://docs.google.com/spreadsheets/d/1xPs4Ht-3PVBGgdNSxyN3p2F5TtOifgvunrEvYaiu_A4/edit?usp=sharing" rel="noopener noreferrer"&gt;datasource&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Finally, &lt;a href="https://script.google.com/d/1wdNu632PfJNv0iCCNLjA-9nARemz7DLcK28Lio6YdVNMP3iEtOUtR4_R/edit?usp=sharing" rel="noopener noreferrer"&gt;the script&lt;/a&gt; itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm still working my way through a whole bunch of &lt;a href="https://github.com/schoraria911/gas-url-shortener/blob/master/README.md#caveats" rel="noopener noreferrer"&gt;caveats&lt;/a&gt; and from my &lt;a href="https://github.com/schoraria911/gas-url-shortener/blob/master/README.md#bucket-list" rel="noopener noreferrer"&gt;bucket list&lt;/a&gt; but I'm more than happy to have &lt;a href="https://github.com/schoraria911/gas-url-shortener/blob/master/README.md#license" rel="noopener noreferrer"&gt;your contributions&lt;/a&gt; considered too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/schoraria911/gas-url-shortener/blob/master/README.md#google-login" rel="noopener noreferrer"&gt;Major learning&lt;/a&gt; from all of this was that in order to use &lt;code&gt;e.pathInfo&lt;/code&gt;, we'd need to append the script URL and in doing so, end up having Apps Script prompt user to login using their Google accounts (not particularly to allow script permissions but just because - we don't know yet).&lt;/p&gt;

&lt;p&gt;Overall, it has been a humbling experience for having to publish a tool of my own 😊 - also available on &lt;a href="https://www.producthunt.com/posts/take-me-there-2" rel="noopener noreferrer"&gt;Product Hunt&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>javascript</category>
      <category>googleappsscript</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
