<?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>DEV Community: Alex Laird</title>
    <description>The latest articles on DEV Community by Alex Laird (@alexdlaird).</description>
    <link>https://dev.to/alexdlaird</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%2F353913%2F6b844155-e689-44e5-9c2b-5f769c290260.jpeg</url>
      <title>DEV Community: Alex Laird</title>
      <link>https://dev.to/alexdlaird</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexdlaird"/>
    <language>en</language>
    <item>
      <title>amazon-orders for Python</title>
      <dc:creator>Alex Laird</dc:creator>
      <pubDate>Wed, 31 Jan 2024 23:02:46 +0000</pubDate>
      <link>https://dev.to/alexdlaird/amazon-orders-python-library-2cnj</link>
      <guid>https://dev.to/alexdlaird/amazon-orders-python-library-2cnj</guid>
      <description>&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%2Famazon-orders.readthedocs.io%2F_images%2Flogo.png" class="article-body-image-wrapper"&gt;&lt;img alt="amazon-orders - A Python libray (and CLI) for Amazon order history" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Famazon-orders.readthedocs.io%2F_images%2Flogo.png" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;amazon-orders&lt;/code&gt; is an unofficial library that provides a Python API (and CLI) for Amazon order history.&lt;/p&gt;

&lt;p&gt;This package works by parsing data from Amazon's consumer-facing website. A periodic build validates functionality to ensure its stability, but as Amazon provides no official API to use, this package may break at any time. Pin the &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;minor version&lt;/a&gt; with a wildcard (ex. &lt;code&gt;==4.0.*&lt;/code&gt;, not &lt;code&gt;==4.0.7&lt;/code&gt;)—or reinstall with the &lt;code&gt;--upgrade&lt;/code&gt; (as shown below) often—to ensure you always get the latest stable release.&lt;/p&gt;

&lt;p&gt;This package only officially supports the English, &lt;code&gt;.com&lt;/code&gt; version of Amazon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;amazon-orders&lt;/code&gt; is available on &lt;a href="https://pypi.org/project/amazon-orders/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt; and can be installed using &lt;code&gt;pip&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;amazon-orders &lt;span class="nt"&gt;--upgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! &lt;code&gt;amazon-orders&lt;/code&gt; is now available as a package to your Python projects and from the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;

&lt;p&gt;You'll use &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.session.AmazonSession" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonSession&lt;/code&gt;&lt;/a&gt; to authenticate your Amazon account, then &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.orders.AmazonOrders" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonOrders&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.transactions.AmazonTransactions" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonTransactions&lt;/code&gt;&lt;/a&gt; to interact with account data. &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.orders.AmazonOrders.get_order_history" rel="noopener noreferrer"&gt;&lt;code&gt;get_order_history&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.orders.AmazonOrders.get_order" rel="noopener noreferrer"&gt;&lt;code&gt;get_order&lt;/code&gt;&lt;/a&gt; are good places to start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amazonorders.session&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AmazonSession&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;amazonorders.orders&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AmazonOrders&lt;/span&gt;

&lt;span class="n"&gt;amazon_session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AmazonSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;AMAZON_EMAIL&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;AMAZON_PASSWORD&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;amazon_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;amazon_orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AmazonOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amazon_session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amazon_orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_order_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grand_total&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the fields you're looking for aren't populated with the above, set &lt;code&gt;full_details=True&lt;/code&gt; (or pass &lt;code&gt;--full-details&lt;/code&gt; to the &lt;code&gt;history&lt;/code&gt; CLI command), since by default it is &lt;code&gt;False&lt;/code&gt; (enabling it slows down querying, since an additional request for each order is necessary). Have a look at the &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.entity.order.Order" rel="noopener noreferrer"&gt;Order&lt;/a&gt; entity's docs to see what fields are only populated with full details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Line Usage
&lt;/h3&gt;

&lt;p&gt;You can also run any command available to the main Python interface from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;amazon-orders login
amazon-orders &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;--year&lt;/span&gt; 2023
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automating Authentication
&lt;/h3&gt;

&lt;p&gt;Authentication can be automated by (in order of precedence) storing credentials in environment variables, passing them to &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.session.AmazonSession" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonSession&lt;/code&gt;&lt;/a&gt;, or storing them in &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.conf.AmazonOrdersConfig" rel="noopener noreferrer"&gt;&lt;code&gt;AmazonOrdersConfig&lt;/code&gt;&lt;/a&gt;. The environment variables &lt;code&gt;amazon-orders&lt;/code&gt; looks for are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AMAZON_USERNAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AMAZON_PASSWORD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AMAZON_OTP_SECRET_KEY&lt;/code&gt; (see &lt;a href="https://amazon-orders.readthedocs.io/api.html#amazonorders.session.AmazonSession.otp_secret_key" rel="noopener noreferrer"&gt;docs for usage&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;For more advanced usage, &lt;code&gt;amazon-orders&lt;/code&gt;'s official documentation is available at &lt;a href="http://amazon-orders.readthedocs.io" rel="noopener noreferrer"&gt;http://amazon-orders.readthedocs.io&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;If you would like to get involved, be sure to review the &lt;a href="https://github.com/alexdlaird/amazon-orders/blob/main/CONTRIBUTING.rst" rel="noopener noreferrer"&gt;Contribution Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to contribute financially? If you've found &lt;code&gt;amazon-orders&lt;/code&gt; useful, &lt;a href="https://github.com/sponsors/alexdlaird" rel="noopener noreferrer"&gt;sponsorship&lt;/a&gt; would also be greatly appreciated!&lt;/p&gt;

</description>
      <category>python</category>
      <category>amazon</category>
      <category>cli</category>
    </item>
    <item>
      <title>java-ngrok - a Java wrapper for ngrok</title>
      <dc:creator>Alex Laird</dc:creator>
      <pubDate>Fri, 27 Aug 2021 19:19:54 +0000</pubDate>
      <link>https://dev.to/alexdlaird/java-ngrok-a-java-wrapper-for-ngrok-2a15</link>
      <guid>https://dev.to/alexdlaird/java-ngrok-a-java-wrapper-for-ngrok-2a15</guid>
      <description>&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%2Fgithub.com%2Falexdlaird%2Fjava-ngrok%2Fraw%2Fmain%2Flogo.png" class="article-body-image-wrapper"&gt;&lt;img alt="java-ngrok - a Java wrapper for ngrok" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falexdlaird%2Fjava-ngrok%2Fraw%2Fmain%2Flogo.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;java-ngrok&lt;/code&gt; is a Java wrapper for &lt;code&gt;ngrok&lt;/code&gt; that manages its own binary, making &lt;code&gt;ngrok&lt;/code&gt; available via a convenient Java API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;&lt;code&gt;ngrok&lt;/code&gt;&lt;/a&gt; is a reverse proxy that opens secure tunnels from public URLs to localhost. It's perfect for rapid development (test webhooks, demo local websites, enable SSH access), establishing ingress to external networks and devices, building production APIs (traffic policies, OAuth, load balancing), and more. And it's made even more powerful with native Java integration through the &lt;code&gt;java-ngrok&lt;/code&gt; client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;java-ngrok&lt;/code&gt; is available on &lt;a href="https://central.sonatype.com/artifact/com.github.alexdlaird/java-ngrok" rel="noopener noreferrer"&gt;Maven Central&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want &lt;code&gt;ngrok&lt;/code&gt; to be available from the command line, &lt;a href="https://pyngrok.readthedocs.io/en/latest/#installation" rel="noopener noreferrer"&gt;&lt;code&gt;pyngrok&lt;/code&gt;&lt;/a&gt; can be installed using &lt;code&gt;pip&lt;/code&gt; to manage that for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Open a Tunnel
&lt;/h3&gt;

&lt;p&gt;To open a tunnel, use the &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/NgrokClient.html" rel="noopener noreferrer"&gt;&lt;code&gt;NgrokClient&lt;/code&gt;&lt;/a&gt;'s &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/NgrokClient.html#connect(com.github.alexdlaird.ngrok.protocol.CreateTunnel)" rel="noopener noreferrer"&gt;&lt;code&gt;connect()&lt;/code&gt;&lt;/a&gt; method, which returns a &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/NgrokClient.html#connect(com.github.alexdlaird.ngrok.protocol.CreateTunnel)" rel="noopener noreferrer"&gt;&lt;code&gt;Tunnel&lt;/code&gt;&lt;/a&gt;, and this returned object has a reference to the public URL generated by &lt;code&gt;ngrok&lt;/code&gt;, which can be retrieved with &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/protocol/Tunnel.html#getPublicUrl()" rel="noopener noreferrer"&gt;&lt;code&gt;getPublicUrl()&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NgrokClient&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NgrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Open a HTTP tunnel on the default port 80&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;Tunnel: "https://&amp;lt;public_sub&amp;gt;.ngrok.io" -&amp;gt; "http://localhost:80"&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Tunnel&lt;/span&gt; &lt;span class="n"&gt;httpTunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Open a SSH tunnel&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;Tunnel: "tcp://0.tcp.ngrok.io:12345" -&amp;gt; "localhost:22"&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CreateTunnel&lt;/span&gt; &lt;span class="n"&gt;sshCreateTunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreateTunnel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withProto&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Proto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TCP&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withAddr&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Tunnel&lt;/span&gt; &lt;span class="n"&gt;sshTunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sshCreateTunnel&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Open a named tunnel from the config file&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CreateTunnel&lt;/span&gt; &lt;span class="n"&gt;createNamedTunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreateTunnel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"my-config-file-tunnel"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Tunnel&lt;/span&gt; &lt;span class="n"&gt;namedTunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createNamedTunnel&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Open an Internal Endpoint that's load balanced&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;Tunnel: "https://some-endpoint.internal" -&amp;gt; "http://localhost:9000"&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CreateTunnel&lt;/span&gt; &lt;span class="n"&gt;createInternalEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CreateTunnel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withAddr&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"9000"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDomain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"some-endpoint.internal"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withPoolingEnabled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Tunnel&lt;/span&gt; &lt;span class="n"&gt;internalEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createInternalEndpoint&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/NgrokClient.html#connect(com.github.alexdlaird.ngrok.protocol.CreateTunnel)" rel="noopener noreferrer"&gt;&lt;code&gt;connect()&lt;/code&gt;&lt;/a&gt; method can also take a &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/protocol/CreateTunnel.html" rel="noopener noreferrer"&gt;&lt;code&gt;CreateTunnel&lt;/code&gt;&lt;/a&gt; (which can be built through &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/protocol/CreateTunnel.Builder.html" rel="noopener noreferrer"&gt;its Builder&lt;/a&gt;) that allows you to pass additional properties that are supported by &lt;code&gt;ngrok&lt;/code&gt; (or &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/protocol/CreateTunnel.Builder.html#withName(java.lang.String)" rel="noopener noreferrer"&gt;&lt;code&gt;withName()&lt;/code&gt;&lt;/a&gt; to use a tunnel defined in &lt;code&gt;ngrok&lt;/code&gt;'s config file), &lt;a href="https://alexdlaird.github.io/java-ngrok/#tunnel-configuration" rel="noopener noreferrer"&gt;as documented here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ngrok&lt;/code&gt;'s API
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest/com.github.alexdlaird.ngrok/com/github/alexdlaird/ngrok/NgrokClient.html#api(java.util.List)" rel="noopener noreferrer"&gt;&lt;code&gt;api()&lt;/code&gt;&lt;/a&gt; method allows you to use the local &lt;code&gt;ngrok&lt;/code&gt; agent to make requests against &lt;a href="https://ngrok.com/docs/agent/cli-api/" rel="noopener noreferrer"&gt;the &lt;code&gt;ngrok&lt;/code&gt; API&lt;/a&gt;, if you have &lt;a href="https://alexdlaird.github.io/java-ngrok/#setting-the-authtoken-or-api_key" rel="noopener noreferrer"&gt;set an API key&lt;/a&gt;. For example, here's how you would reserve a &lt;code&gt;ngrok&lt;/code&gt; domain, then create a Cloud Endpoint with an associated traffic policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NgrokClient&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NgrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"some-domain.ngrok.dev"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ApiResponse&lt;/span&gt; &lt;span class="n"&gt;domainResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"reserved-domains"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"create"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"--domain"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ApiResponse&lt;/span&gt; &lt;span class="n"&gt;endpointResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrokClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"endpoints"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"create"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"--bindings"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"public"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"--url"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://%s"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                &lt;span class="s"&gt;"--traffic-policy-file"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"policy.yml"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command Line Usage
&lt;/h3&gt;

&lt;p&gt;Assuming you have also installed &lt;a href="https://pyngrok.readthedocs.io/en/latest/#installation" rel="noopener noreferrer"&gt;&lt;code&gt;pyngrok&lt;/code&gt;&lt;/a&gt;, all features of &lt;code&gt;ngrok&lt;/code&gt; are available on the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For details on how to fully leverage &lt;code&gt;ngrok&lt;/code&gt; from the command line, see &lt;a href="https://ngrok.com/docs/agent/cli/" rel="noopener noreferrer"&gt;&lt;code&gt;ngrok&lt;/code&gt;'s official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;For more advanced usage, &lt;code&gt;java-ngrok&lt;/code&gt;'s official documentation is available &lt;a href="https://alexdlaird.github.io/java-ngrok/" rel="noopener noreferrer"&gt;here on GitHub&lt;/a&gt;, with additional API documentation on &lt;a href="https://javadoc.io/doc/com.github.alexdlaird/java-ngrok/latest" rel="noopener noreferrer"&gt;javadoc.io&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://alexdlaird.github.io/java-ngrok/integration/#spring" rel="noopener noreferrer"&gt;Spring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alexdlaird.github.io/java-ngrok/integration/#dropwizard" rel="noopener noreferrer"&gt;Dropwizard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alexdlaird.github.io/java-ngrok/integration/#play-scala" rel="noopener noreferrer"&gt;Play (Scala)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alexdlaird.github.io/java-ngrok/integration/#docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alexdlaird.github.io/java-ngrok/integration/#end-to-end-testing" rel="noopener noreferrer"&gt;End-to-End Testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 8
&lt;/h3&gt;

&lt;p&gt;A Java 8-compatible build was previously maintained in the &lt;a href="https://github.com/alexdlaird/java-ngrok/tree/1.4.x" rel="noopener noreferrer"&gt;&lt;code&gt;1.4.x&lt;/code&gt;&lt;/a&gt; branch. While it is no longer supported, it is available through the &lt;code&gt;java8-ngrok&lt;/code&gt; artifact instead on &lt;a href="https://central.sonatype.com/artifact/com.github.alexdlaird/java8-ngrok" rel="noopener noreferrer"&gt;Maven Central&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more details on what differs in the &lt;code&gt;java8-ngrok&lt;/code&gt; dependency, see &lt;a href="https://javadoc.io/static/com.github.alexdlaird/java8-ngrok/1.4.19/overview-summary.html#java8" rel="noopener noreferrer"&gt;the "Java 8" section of the docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;If you would like to get involved, be sure to review the &lt;a href="https://github.com/alexdlaird/java-ngrok/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Contribution Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to contribute financially? If you've found &lt;code&gt;java-ngrok&lt;/code&gt; useful, &lt;a href="https://github.com/sponsors/alexdlaird" rel="noopener noreferrer"&gt;sponsorship&lt;/a&gt; would also be greatly appreciated!&lt;/p&gt;

</description>
      <category>ngrok</category>
      <category>java</category>
      <category>tunnel</category>
      <category>webhook</category>
    </item>
    <item>
      <title>hookee - command line webhooks, on demand</title>
      <dc:creator>Alex Laird</dc:creator>
      <pubDate>Sat, 12 Sep 2020 21:57:43 +0000</pubDate>
      <link>https://dev.to/alexdlaird/hookee-command-line-webhooks-on-demand-2of8</link>
      <guid>https://dev.to/alexdlaird/hookee-command-line-webhooks-on-demand-2of8</guid>
      <description>&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%2Fhookee.readthedocs.io%2Fen%2Flatest%2F_images%2Flogo.png" class="article-body-image-wrapper"&gt;&lt;img alt="hookee - command line webhooks, on demand" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fhookee.readthedocs.io%2Fen%2Flatest%2F_images%2Flogo.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hookee&lt;/code&gt; is a utility that provides command line webhooks, on demand! Dump useful request data to the console, process requests and responses, customize response data, and configure &lt;code&gt;hookee&lt;/code&gt; and its routes further in any number of ways through custom plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;hookee&lt;/code&gt; is available on &lt;a href="https://pypi.org/project/hookee/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt; and can be installed using &lt;code&gt;pip&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;hookee
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or &lt;code&gt;conda&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;conda &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; conda-forge hookee
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! &lt;code&gt;hookee&lt;/code&gt; is now available as a Python package and is available from the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;hookee&lt;/code&gt; makes it easy to get webhooks on the fly right from the console. Simply start it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookee start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With its default configuration, this will start a server on port 8000, open a &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;&lt;code&gt;ngrok&lt;/code&gt;&lt;/a&gt; tunnel using &lt;a href="https://pyngrok.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;pyngrok&lt;/code&gt;&lt;/a&gt;, and mount a URL at &lt;code&gt;/webhook&lt;/code&gt;. Sending any request to the &lt;code&gt;/webhook&lt;/code&gt; endpoint will dump the request and response data to the console.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hookee&lt;/code&gt; can be configured in a number of ways to quickly and easily tweak request and response data. For example, here is how you can customize the response body from &lt;code&gt;/webhook&lt;/code&gt; using the &lt;code&gt;--response&lt;/code&gt; arg.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookee &lt;span class="nt"&gt;--response&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;Response&amp;gt;Ok&amp;lt;/Response&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--content-type&lt;/span&gt; application/xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;hookee&lt;/code&gt; can also be started without a tunnel (removing the dependency on an Internet connection). Using the &lt;code&gt;--no-tunnel&lt;/code&gt; flag only starts &lt;code&gt;hookee&lt;/code&gt;'s server, allowing responses to be mocked locally. This can be particularly useful when service discovery is done through a proxy service (ex. &lt;a href="https://www.haproxy.org/" rel="noopener noreferrer"&gt;HAProxy&lt;/a&gt;, &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt;, etc.), meaning you can tell &lt;code&gt;hookee&lt;/code&gt; to start on the port of an expected downstream, thus intercepting requests to that service to provide your own responses in an isolated environment, very useful for rapid local development, cluster testing, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookee &lt;span class="nt"&gt;--no-tunnel&lt;/span&gt; &lt;span class="nt"&gt;--response&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;Response&amp;gt;Ok&amp;lt;/Response&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--content-type&lt;/span&gt; application/xml &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--default-route&lt;/span&gt; /some/route &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--port&lt;/span&gt; 19780
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see the ways &lt;code&gt;hookee&lt;/code&gt; can be tweaked right from the console, view its documented args and commands like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookee &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;For more advanced usage, including how &lt;code&gt;hookee&lt;/code&gt;'s default configuration can be changed, extended through plugins, API integrations, and more, see its official documentation is available at &lt;a href="http://hookee.readthedocs.io" rel="noopener noreferrer"&gt;http://hookee.readthedocs.io&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;If you would like to get involved, be sure to review the &lt;a href="https://github.com/alexdlaird/hookee/blob/main/CONTRIBUTING.rst" rel="noopener noreferrer"&gt;Contribution Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to contribute financially? If you've found &lt;code&gt;hookee&lt;/code&gt; useful, &lt;a href="https://github.com/sponsors/alexdlaird" rel="noopener noreferrer"&gt;sponsorship&lt;/a&gt;&lt;br&gt;
would also be greatly appreciated!&lt;/p&gt;

</description>
      <category>python</category>
      <category>ngrok</category>
      <category>webhook</category>
      <category>cli</category>
    </item>
    <item>
      <title>Twilio-Powered Air Quality Texting Service</title>
      <dc:creator>Alex Laird</dc:creator>
      <pubDate>Sat, 12 Sep 2020 16:07:26 +0000</pubDate>
      <link>https://dev.to/alexdlaird/twilio-powered-air-quality-texting-service-4noj</link>
      <guid>https://dev.to/alexdlaird/twilio-powered-air-quality-texting-service-4noj</guid>
      <description>&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%2Fitlzxu7b3kd23qyd9vz0.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%2Fitlzxu7b3kd23qyd9vz0.png" alt="Alt Text" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With wildfire season upon us, use this handy texting tool to find out what the air quality is in your area. Simply text your zip code to (415) 212–4229 for air quality updates. You can also add “map” to the text to be sent an image of your region.&lt;/p&gt;

&lt;p&gt;This service isn’t just useful for individuals with limited access to smartphones or the Internet. It also alleviates the load put on air quality sites like AirNow, which are often overloaded and unavailable during wildfire season due to the spike in traffic. Texting this number instead is a great way to get the same information without bogging down those sites, helping them to stay up for others who need to access them.&lt;/p&gt;

&lt;p&gt;Spread the word and stay safe! If you’re interested in nerding out over the code, feel free to check it out on GitHub, and if you have questions or comments, tweet &lt;a href="https://twitter.com/alexdlaird" rel="noopener noreferrer"&gt;@alexdlaird&lt;/a&gt; to let me know.&lt;/p&gt;

</description>
      <category>twilio</category>
      <category>airquality</category>
      <category>programming</category>
      <category>wildfires</category>
    </item>
    <item>
      <title>pyngrok — a Python wrapper for ngrok</title>
      <dc:creator>Alex Laird</dc:creator>
      <pubDate>Thu, 09 Jul 2020 05:50:10 +0000</pubDate>
      <link>https://dev.to/alexdlaird/pyngrok-a-python-wrapper-for-ngrok-5cmn</link>
      <guid>https://dev.to/alexdlaird/pyngrok-a-python-wrapper-for-ngrok-5cmn</guid>
      <description>&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%2Fpyngrok.readthedocs.io%2Fen%2Flatest%2F_images%2Flogo.png" class="article-body-image-wrapper"&gt;&lt;img alt="pyngrok - a Python wrapper for ngrok" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpyngrok.readthedocs.io%2Fen%2Flatest%2F_images%2Flogo.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pyngrok&lt;/code&gt; is a Python wrapper for &lt;code&gt;ngrok&lt;/code&gt; that manages its own binary, making &lt;code&gt;ngrok&lt;/code&gt; available via a convenient Python API and the command line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;&lt;code&gt;ngrok&lt;/code&gt;&lt;/a&gt; is a reverse proxy that opens secure tunnels from public URLs to localhost. It's perfect for rapid development (test webhooks, demo local websites, enable SSH access), establishing ingress to external networks and devices, building production APIs (traffic policies, OAuth, load balancing), and more. And it's made even more powerful with native Python integration through the &lt;code&gt;pyngrok&lt;/code&gt; client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pyngrok&lt;/code&gt; is available on &lt;a href="https://pypi.org/project/pyngrok/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt; and can be installed using &lt;code&gt;pip&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pyngrok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or &lt;code&gt;conda&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;conda &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; conda-forge pyngrok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! &lt;code&gt;pyngrok&lt;/code&gt; is now available as a package to your Python projects, and &lt;code&gt;ngrok&lt;/code&gt; is now available from the command line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Open a Tunnel
&lt;/h3&gt;

&lt;p&gt;To open a tunnel, use the &lt;a href="https://pyngrok.readthedocs.io/en/latest/api.html#pyngrok.ngrok.connect" rel="noopener noreferrer"&gt;&lt;code&gt;connect&lt;/code&gt;&lt;/a&gt; method, which returns a &lt;a href="https://pyngrok.readthedocs.io/en/latest/api.html#pyngrok.ngrok.NgrokTunnel" rel="noopener noreferrer"&gt;&lt;code&gt;NgrokTunnel&lt;/code&gt;&lt;/a&gt;, and this returned object has a reference to the public URL generated by &lt;code&gt;ngrok&lt;/code&gt; in its &lt;a href="https://pyngrok.readthedocs.io/en/latest/api.html#pyngrok.ngrok.NgrokTunnel.public_url" rel="noopener noreferrer"&gt;&lt;code&gt;public_url&lt;/code&gt;&lt;/a&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyngrok&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ngrok&lt;/span&gt;

&lt;span class="c1"&gt;# Open a HTTP tunnel on the default port 80
# &amp;lt;NgrokTunnel: "https://&amp;lt;public_sub&amp;gt;.ngrok.io" -&amp;gt; "http://localhost:80"&amp;gt;
&lt;/span&gt;&lt;span class="n"&gt;http_tunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Open a SSH tunnel
# &amp;lt;NgrokTunnel: "tcp://0.tcp.ngrok.io:12345" -&amp;gt; "localhost:22"&amp;gt;
&lt;/span&gt;&lt;span class="n"&gt;ssh_tunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;22&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tcp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Open a named tunnel from the config file
&lt;/span&gt;&lt;span class="n"&gt;named_tunnel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-config-file-tunnel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Open an Internal Endpoint that's load balanced
# &amp;lt;NgrokTunnel: "https://some-endpoint.internal" -&amp;gt; "http://localhost:9000"&amp;gt;
&lt;/span&gt;&lt;span class="n"&gt;internal_endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ngrok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9000&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;some-endpoint.internal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="n"&gt;pooling_enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://pyngrok.readthedocs.io/en/latest/api.html#pyngrok.ngrok.connect" rel="noopener noreferrer"&gt;&lt;code&gt;connect&lt;/code&gt;&lt;/a&gt; method takes &lt;code&gt;kwargs&lt;/code&gt; as well, which allows you to pass additional tunnel configurations that are supported by &lt;code&gt;ngrok&lt;/code&gt; (or the &lt;code&gt;name&lt;/code&gt; of a tunnel defined in &lt;code&gt;ngrok&lt;/code&gt;'s config file), &lt;a href="https://pyngrok.readthedocs.io/en/latest/#tunnel-configurations" rel="noopener noreferrer"&gt;as documented here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ngrok&lt;/code&gt;'s API
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://pyngrok.readthedocs.io/en/latest/api.html#pyngrok.ngrok.api" rel="noopener noreferrer"&gt;&lt;code&gt;api&lt;/code&gt;&lt;/a&gt; method allows you to use the local &lt;code&gt;ngrok&lt;/code&gt; agent to make requests against &lt;a href="https://ngrok.com/docs/agent/cli-api/" rel="noopener noreferrer"&gt;the &lt;code&gt;ngrok&lt;/code&gt; API&lt;/a&gt;, if you have &lt;a href="https://pyngrok.readthedocs.io/en/latest/#setting-the-authtoken-or-api-key" rel="noopener noreferrer"&gt;set an API key&lt;/a&gt;. For example, here's how you would reserve a &lt;code&gt;ngrok&lt;/code&gt; domain, then create a Cloud Endpoint with an associated traffic policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyngrok&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ngrok&lt;/span&gt;

&lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;some-domain.ngrok.dev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ngrok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reserved-domains&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--domain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ngrok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;endpoints&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--bindings&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--traffic-policy-file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;policy.yml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command Line Usage
&lt;/h3&gt;

&lt;p&gt;This package puts the default &lt;code&gt;ngrok&lt;/code&gt; binary on your path, so all features of &lt;code&gt;ngrok&lt;/code&gt; are available on the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For details on how to fully leverage &lt;code&gt;ngrok&lt;/code&gt; from the command line, see &lt;a href="https://ngrok.com/docs/agent/cli/" rel="noopener noreferrer"&gt;&lt;code&gt;ngrok&lt;/code&gt;'s official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;For more advanced usage, &lt;code&gt;pyngrok&lt;/code&gt;'s official documentation is available on &lt;a href="https://pyngrok.readthedocs.io" rel="noopener noreferrer"&gt;Read the Docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pyngrok.readthedocs.io/en/latest/integrations.html#flask" rel="noopener noreferrer"&gt;Flask&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pyngrok.readthedocs.io/en/latest/integrations.html#django" rel="noopener noreferrer"&gt;Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pyngrok.readthedocs.io/en/latest/integrations.html#docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pyngrok.readthedocs.io/en/latest/integrations.html#google-colaboratory" rel="noopener noreferrer"&gt;Google Colab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pyngrok.readthedocs.io/en/latest/integrations.html#end-to-end-testing" rel="noopener noreferrer"&gt;End-to-End Testing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;If you would like to get involved, be sure to review the &lt;a href="https://github.com/alexdlaird/pyngrok/blob/main/CONTRIBUTING.rst" rel="noopener noreferrer"&gt;Contribution Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to contribute financially? If you've found &lt;code&gt;pyngrok&lt;/code&gt; useful, &lt;a href="https://github.com/sponsors/alexdlaird" rel="noopener noreferrer"&gt;sponsorship&lt;/a&gt; would also be greatly appreciated!&lt;/p&gt;

</description>
      <category>ngrok</category>
      <category>python</category>
      <category>tunnel</category>
      <category>webhook</category>
    </item>
  </channel>
</rss>
