<?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: Scott Lepper</title>
    <description>The latest articles on DEV Community by Scott Lepper (@scottlepp).</description>
    <link>https://dev.to/scottlepp</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%2F77814%2Fd72bdbdc-49dd-488b-8d18-5ba754d002ea.jpeg</url>
      <title>DEV Community: Scott Lepper</title>
      <link>https://dev.to/scottlepp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scottlepp"/>
    <language>en</language>
    <item>
      <title>Build MCP Servers that don't suck...tokens.</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Tue, 19 May 2026 00:27:45 +0000</pubDate>
      <link>https://dev.to/scottlepp/build-mcp-servers-that-dont-sucktokens-im2</link>
      <guid>https://dev.to/scottlepp/build-mcp-servers-that-dont-sucktokens-im2</guid>
      <description>&lt;p&gt;First-generation MCP servers were great. They gave AI agents access to a ton of external apps and data — Jira, Confluence, GitHub, Linear, you name it. But most of them just wrapped REST APIs. And that causes a ton of context bloat, hallucinations, and token burning.&lt;/p&gt;

&lt;p&gt;Combining a few strategies from the &lt;a href="https://github.com/scottlepp/ultra-mcp-toolkit" rel="noopener noreferrer"&gt;ultra-mcp-toolkit&lt;/a&gt;, you can reduce that bloat dramatically — and save money.&lt;/p&gt;

&lt;p&gt;Generating a cost-efficient MCP server is easy. Just install the skill and off you go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here's what "dramatically" looks like
&lt;/h2&gt;

&lt;p&gt;Real benchmark, live Jira instance, &lt;a href="https://github.com/scottlepp/ultra-jira-mcp/blob/main/docs/BENCHMARK.md" rel="noopener noreferrer"&gt;reproducible&lt;/a&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  Per-call response size
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;scenario&lt;/th&gt;
&lt;th&gt;naive&lt;/th&gt;
&lt;th&gt;with toolkit&lt;/th&gt;
&lt;th&gt;savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fetch 1 simple ticket&lt;/td&gt;
&lt;td&gt;20.3KB&lt;/td&gt;
&lt;td&gt;1.2KB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17.5×&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;investigate rich ticket&lt;/td&gt;
&lt;td&gt;270.7KB&lt;/td&gt;
&lt;td&gt;15.5KB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17.5×&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JQL search ~10 tickets&lt;/td&gt;
&lt;td&gt;20.5KB&lt;/td&gt;
&lt;td&gt;3.5KB&lt;/td&gt;
&lt;td&gt;5.8×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That rich-ticket row is the one that hurts. &lt;strong&gt;270 KB → 15.5 KB.&lt;/strong&gt; ~67k tokens down to ~3.9k tokens. Same content; the full payload still lands on disk and the agent can fetch it via a &lt;code&gt;ref:&lt;/code&gt; path only if it actually needs the detail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool-list cost (paid every conversation)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;approach&lt;/th&gt;
&lt;th&gt;bytes&lt;/th&gt;
&lt;th&gt;~tokens&lt;/th&gt;
&lt;th&gt;savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;naive (one tool per op)&lt;/td&gt;
&lt;td&gt;38.9KB&lt;/td&gt;
&lt;td&gt;9,947&lt;/td&gt;
&lt;td&gt;1×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;consolidated tools&lt;/td&gt;
&lt;td&gt;25.1KB&lt;/td&gt;
&lt;td&gt;6,427&lt;/td&gt;
&lt;td&gt;1.5×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;consolidated + filtered&lt;/td&gt;
&lt;td&gt;~6 KB&lt;/td&gt;
&lt;td&gt;~1,600&lt;/td&gt;
&lt;td&gt;5×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;code-api mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;401B&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99×&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You read that right. Tool listings drop from ~10k tokens to ~100 tokens. On every. single. conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why MCP servers leak tokens
&lt;/h2&gt;

&lt;p&gt;Four anti-patterns show up almost everywhere:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Returning raw API JSON.&lt;/strong&gt; A Jira issue carries &lt;code&gt;iconUrl&lt;/code&gt;s, nested &lt;code&gt;self&lt;/code&gt; URLs, schema metadata, expand hints, three different shapes of the same status field. The agent needs none of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One MCP tool per endpoint.&lt;/strong&gt; A typical CRM has ~80 endpoints → 80 tool descriptions in the listing → ~10k tokens before the user types anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asking the LLM to filter or paginate.&lt;/strong&gt; The model can't reliably page through huge structures, and the chunking logic itself costs tokens. Filtering belongs server-side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No discipline on what gets kept.&lt;/strong&gt; Denylist trimming (&lt;code&gt;delete result.iconUrl&lt;/code&gt;) silently breaks the day the API adds a new noisy field. Allowlists keep the contract stable.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The fix, in three strategies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Allowlist-style trim projections
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pick&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ultra-mcp-toolkit/trim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;issueSummary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;pick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;priority&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assignee&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register the trim once. Every response routes through it. New API fields default to &lt;em&gt;dropped&lt;/em&gt;. The model sees what it needs; the full response lives on disk as a &lt;code&gt;ref:&lt;/code&gt; the agent can dereference on demand.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Consolidated tools (action-discriminated)
&lt;/h3&gt;

&lt;p&gt;Instead of 80 tools, expose ~15 — each taking an &lt;code&gt;action&lt;/code&gt; arg:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;issueIdOrKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PROJ-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;projectKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PROJ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transition&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;issueIdOrKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PROJ-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same operations, 1/5th the tool-list cost. The toolkit's dispatcher handles per-action Zod validation, manifest routing, and a &lt;code&gt;full: true&lt;/code&gt; escape hatch when the model genuinely needs the raw response.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Code-api mode (the 99× lever)
&lt;/h3&gt;

&lt;p&gt;Expose a &lt;em&gt;single&lt;/em&gt; MCP tool that hands the agent a path to a bundled CLI plus a socket address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &amp;lt;cli-path&amp;gt; issue.get &lt;span class="nt"&gt;--issueIdOrKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;PROJ-1
&lt;span class="c"&gt;# stdout: trimmed summary as JSON&lt;/span&gt;
&lt;span class="c"&gt;# final line: `ref: /path/to/full-response.json`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent drives the whole API from its shell. Tool list stays at one tool forever, no matter how many operations exist. For shell-capable agents (Claude Code, Cursor, anything with bash), it's pure win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;ultra-mcp-toolkit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The toolkit ships a Claude Code skill that auto-loads when you work on an MCP server. Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run install-skill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The skill walks the agent through manifest design, trim projections, dispatcher wiring, and server boot — the patterns that produce the numbers above.&lt;/p&gt;

&lt;p&gt;Working from a non-Claude agent (Codex CLI, Cursor, Aider, Continue, Zed)? Point it at the skill markdown directly — &lt;a href="https://github.com/scottlepp/ultra-mcp-toolkit/blob/main/AGENTS.md" rel="noopener noreferrer"&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; shows you how.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the box
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Operation manifest&lt;/strong&gt; — declare endpoints as pure data; powers MCP tools, CLI, and code-api bridge from one source of truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trim registry&lt;/strong&gt; — type-safe allowlist projections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content-addressed sandbox&lt;/strong&gt; — full responses land on disk; the model sees a &lt;code&gt;ref:&lt;/code&gt; only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page cache&lt;/strong&gt; — versioned-id disk cache for stable keys (PR diffs by SHA, Confluence pages by version).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pooled retry-aware HTTP transport&lt;/strong&gt; — &lt;code&gt;undici&lt;/code&gt; + 429-aware retry honoring &lt;code&gt;Retry-After&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic streaming downloads&lt;/strong&gt; — sha256-verified, path-traversal-safe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consolidated tool dispatcher&lt;/strong&gt; — Zod-validated, action-discriminated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI scaffolding&lt;/strong&gt; — bridge mode + direct mode, free with &lt;code&gt;createCli&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundled Claude Code skill&lt;/strong&gt; — installs in one command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production proof
&lt;/h2&gt;

&lt;p&gt;Used in &lt;a href="https://github.com/scottlepp/ultra-jira-mcp" rel="noopener noreferrer"&gt;ultra-jira-mcp&lt;/a&gt; and &lt;a href="https://github.com/scottlepp/ultra-bitbucket-mcp" rel="noopener noreferrer"&gt;ultra-bitbucket-mcp&lt;/a&gt;. The benchmark numbers above come from the Jira server running against a real Jira Cloud instance — every byte measured is one a production agent would actually receive.&lt;/p&gt;




&lt;p&gt;If you're building an MCP server for any enterprise API — Jira, Confluence, GitHub, Linear, Notion, ServiceNow, Salesforce, whatever — and your token bill or context window is starting to bite, give it a try.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;&lt;a href="https://github.com/scottlepp/ultra-mcp-toolkit" rel="noopener noreferrer"&gt;github.com/scottlepp/ultra-mcp-toolkit&lt;/a&gt;&lt;/strong&gt; — issues, PRs, and benchmark contributions welcome.&lt;/p&gt;

&lt;p&gt;What's the most token-bloated MCP server &lt;em&gt;you've&lt;/em&gt; shipped or seen? Drop it in the comments — I'm collecting horror stories.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Uploading files with Deno</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Sun, 11 Apr 2021 15:46:24 +0000</pubDate>
      <link>https://dev.to/scottlepp/uploading-files-with-deno-1m00</link>
      <guid>https://dev.to/scottlepp/uploading-files-with-deno-1m00</guid>
      <description>&lt;p&gt;Here I will show a quick tutorial of how to upload files using Deno. Files will simply be stored to disk along with the ability to view them in a browser.&lt;/p&gt;

&lt;p&gt;First of all, install &lt;a href="https://deno.land/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we will add a file to serve our app. Create a project folder to contain your app, then add a subfolder called &lt;strong&gt;server&lt;/strong&gt;. We will also create a client to test it, having a server and client folder will help organize our project.&lt;/p&gt;

&lt;p&gt;Add a server.ts file to your server folder. And add the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/x/oak/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FormFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;multiParser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://deno.land/x/multiparser@v2.1.0/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;oakCors&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://deno.land/x/cors/mod.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&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;Application&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;oakCors&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;multiParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serverRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;FormFile&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`images/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"status": "ok"}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/images&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Deno&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8082&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is happening here?  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At the top we import our dependencies&lt;/li&gt;
&lt;li&gt;Then we create an app, then app.use to register middleware.
&lt;/li&gt;
&lt;li&gt;oakCors will allow any client to call the server (ok for a demo)&lt;/li&gt;
&lt;li&gt;In the second app.use block we define routes: &lt;strong&gt;/upload&lt;/strong&gt; and &lt;strong&gt;/images&lt;/strong&gt;. From the client we will post to &lt;strong&gt;/upload&lt;/strong&gt; and we can use &lt;strong&gt;/images&lt;/strong&gt; to view the images.&lt;/li&gt;
&lt;li&gt;/upload gets a file from a posted form and writes it to a folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's run the app. We need to set permissions to allow-net for http calls, and allow-write/allow-read for writing and reading the images. Then we tell deno to run our server.ts file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deno run &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--allow-net&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--allow-write&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./images &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--allow-read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./,./images &lt;span class="se"&gt;\&lt;/span&gt;
./server/server.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's add the client. Add a &lt;strong&gt;client&lt;/strong&gt; folder and create an index.html file with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./index.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"imgfile"&lt;/span&gt; &lt;span class="na"&gt;onchange=&lt;/span&gt;&lt;span class="s"&gt;"loadImage()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'btnLoad'&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;'Upload'&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"upload()"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"canvas"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add an index.js file with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ts&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&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;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8082/upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;imgfile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please select a file before clicking 'Load'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fr&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;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createImage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;img&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;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageLoaded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;imageLoaded&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;imgfile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;File uploaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open the index.html file, select your image, and upload it! We could serve this page with Deno, but we don't really need to for this demo.&lt;/p&gt;

&lt;p&gt;To view your uploaded image, you can go to: localhost:8082/images/[yourimagename]&lt;/p&gt;

&lt;p&gt;Here is the full &lt;a href="https://github.com/scottlepp/deno-file-upload-example" rel="noopener noreferrer"&gt;GitHub Project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, &lt;a href="https://deno.land/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt; is super easy, fun and fast! Happy coding!&lt;/p&gt;

</description>
      <category>deno</category>
      <category>typescript</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Say Goodbye to Status Meetings</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Sat, 27 Feb 2021 21:29:03 +0000</pubDate>
      <link>https://dev.to/scottlepp/jira-on-steroids-4olk</link>
      <guid>https://dev.to/scottlepp/jira-on-steroids-4olk</guid>
      <description>&lt;h2&gt;
  
  
  ...and hello to Grafana
&lt;/h2&gt;

&lt;p&gt;As a lead developer, I have often played the role of Scrum master on projects, managing tasks and spending lots of time in &lt;strong&gt;Jira&lt;/strong&gt;.  Other roles such as Product Owner and Project Manager also spend a lot of time using Jira, trying to keep track of velocity, progress, status, blockers, estimates and well...if the project is going off the rails.  And of course we developers use it to create stories, track of our tasks, update progress, etc.&lt;/p&gt;

&lt;p&gt;So following an agile process we end up having lots of status meetings where the project manager, product owner, and other stakeholders swoop in and want updates. Some times even "mid-sprint" updates. What? What the heck is that? So we stop all development, all progress and all get together say where we are at. This is not productive. There has to be a better way.&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%2F9nx9h3yzwlvn09usuubj.jpg" 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%2F9nx9h3yzwlvn09usuubj.jpg" alt="Alt Text" width="642" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;. Wait what? &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;? Isn't that an open source observability platform? &lt;/p&gt;

&lt;p&gt;Yes...Exactly. You can monitor anything in &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;, and now you can monitor Jira projects too.&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://grafana.com/grafana/plugins/grafana-jira-datasource" rel="noopener noreferrer"&gt;Jira Enterprise Plugin for Grafana&lt;/a&gt; allows us to connect to Jira and get all of the observability features we love from Grafana, so we always know exactly what is happening in Jira at any time.&lt;/p&gt;

&lt;p&gt;Ok, so tell me how this helps eliminate status meetings? &lt;/p&gt;

&lt;p&gt;Well, in &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; you can set up dashboards that will tell the story of where the project is, show velocity over time, show completion status of epics, show burndown charts, etc, etc. You can also set up alerts to send notifications if a project is heading in the wrong direction, velocity is dropping, story points are overloaded...whatever those managers want to know they can get from a dashboard.  Heck, they don't even have to go look at the dashboard, if the alerts are setup they can just sit back, relax, and know if something goes wrong they will be the first to know.&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%2F8nm38dxjxeatiqydvx1k.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%2F8nm38dxjxeatiqydvx1k.png" alt="Alt Text" width="535" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Is your team kicking ass? Great! Set up alerts and send it to those PMs and Product Owners. Even integrate those alerts into performance management systems! Yeah, alerts support webhooks and other integrations. Let Grafana toot your horn for you.&lt;/p&gt;

&lt;p&gt;If you are familiar with Jira then you undoubtedly used JQL to search for issues at some point. Ever wish you could get SQL like features with JQL?  Like &lt;strong&gt;Grouping&lt;/strong&gt;, &lt;strong&gt;Joins&lt;/strong&gt;,and &lt;strong&gt;Aggregating&lt;/strong&gt;. Well with Grafana transformations, now you can get SQL like capabilities, allowing you to tell stories that was previously not possible (without a bunch of custom coding anyway).&lt;/p&gt;

&lt;p&gt;Ok, enough talk, show me a dashboard already!&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%2Fywuzgoz4k9cjifltae6g.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%2Fywuzgoz4k9cjifltae6g.png" alt="Alt Text" width="799" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Woah! This team is killing it in Sprint 2!&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%2Fi66fraux0rlz3avoxddc.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%2Fi66fraux0rlz3avoxddc.png" alt="Alt Text" width="585" height="141"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How we doing over time?&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%2Fk4g17j1v0td777q2i9qt.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%2Fk4g17j1v0td777q2i9qt.png" alt="Alt Text" width="778" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are familiar with Grafana, you know you have a lot of flexibility to tell whatever story you want. Now go check out the &lt;a href="https://grafana.com/grafana/plugins/grafana-jira-datasource" rel="noopener noreferrer"&gt;Jira plugin for Grafana&lt;/a&gt;  and try it for yourself.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Firebase bulk updates with FireDrill</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Fri, 27 Nov 2020 15:06:40 +0000</pubDate>
      <link>https://dev.to/scottlepp/firebase-bulk-updates-with-firedrill-1e5g</link>
      <guid>https://dev.to/scottlepp/firebase-bulk-updates-with-firedrill-1e5g</guid>
      <description>&lt;p&gt;Ever want to update a bunch of documents in your Firebase collection(s)? Usually you have to write a custom script to do a mass update, but with a new feature in &lt;a href="https://github.com/scottlepp/fire-drill" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt; you can now do batch updates without writing any code. Let's take a look.&lt;/p&gt;

&lt;p&gt;First, run your query to get the documents you want to update. We've also added text filtering so you can perform compound filters! Here we are querying the &lt;strong&gt;track&lt;/strong&gt; collection and applying some filters.&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%2Fi%2Fadwyfq76hqlryankn17o.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%2Fi%2Fadwyfq76hqlryankn17o.png" alt="Firedrill filtering" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our documents we want to update, lets update them.&lt;/p&gt;

&lt;p&gt;Open the hamburger menu on the right and select &lt;strong&gt;Update Results&lt;/strong&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%2Fi%2Fnzjnkia6nmy3cn7720ay.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%2Fi%2Fnzjnkia6nmy3cn7720ay.png" alt="Update Results" width="341" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we see a dialog. Here we can add a new field/value or update an existing field.  Click the &lt;strong&gt;Add Field&lt;/strong&gt; button. Enter a new field/value or enter an existing field name and value you want to update.&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%2Fi%2F0lnnqopiyv14medzqrdi.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%2Fi%2F0lnnqopiyv14medzqrdi.png" alt="Add Field" width="600" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click &lt;strong&gt;Save&lt;/strong&gt;. Boom! All documents (within this filter and limit) have been updated!&lt;/p&gt;

&lt;p&gt;If you like the new bulk updating and text filtering, please let me know by adding a star to the &lt;a href="https://github.com/scottlepp/fire-drill" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt;. Thanks! &lt;/p&gt;

</description>
      <category>firebase</category>
      <category>nocode</category>
      <category>angular</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Generating beautiful HTML forms with FireDrill</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Thu, 15 Nov 2018 15:40:14 +0000</pubDate>
      <link>https://dev.to/scottlepp/generating-beautiful-html-forms-with-firedrill-3hgp</link>
      <guid>https://dev.to/scottlepp/generating-beautiful-html-forms-with-firedrill-3hgp</guid>
      <description>&lt;p&gt;In a previous &lt;a href="https://dev.to/scottlepp/introducing-firedrill---power-tools-for-firebase-g9f"&gt;Post&lt;/a&gt; I showed you how to use &lt;a href="https://firedrill.co" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt; to easily find data in your Firebase database. Now I'll show you how you can easily generate beautiful material design based html forms from your data.&lt;/p&gt;

&lt;p&gt;Let's do this! Head over to &lt;a href="https://firedrill.co" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://dev.to/scottlepp/introducing-firedrill---power-tools-for-firebase-g9f"&gt;here&lt;/a&gt; for how to Connect.&lt;/p&gt;

&lt;p&gt;Once you're connected, click &lt;strong&gt;Forms&lt;/strong&gt;. Enter your collection that you want to generate a form from, then click &lt;strong&gt;Generate Form&lt;/strong&gt;. You will see the form generated below. Notice it will detect the datatypes based on your data and generate the appropriate input.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2h8cxq3gk2p990fv3pb9.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2h8cxq3gk2p990fv3pb9.png" title="Generate" alt="gen" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we see it's generated the appropriate inputs based on the data: checkboxes, text inputs and date pickers are generated. Now we want to move our fields around. Hover over a field to see it's drag indicator.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhahtof5v3mrtbr9526s7.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fhahtof5v3mrtbr9526s7.png" title="Drag" alt="drag" width="585" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I've dragged &lt;strong&gt;available&lt;/strong&gt; down to the bottom, and also moved &lt;strong&gt;created&lt;/strong&gt; down a bit. &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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5g55amho6160a9831n90.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5g55amho6160a9831n90.png" title="Drop" alt="drop" width="380" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, this is the form I want. Now let's &lt;strong&gt;Copy&lt;/strong&gt; it so we can add it to an app we are developing.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq9dxgu97i9l3jpni72lm.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fq9dxgu97i9l3jpni72lm.png" title="Copy" alt="copy" width="748" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! Now the HTML form is copied to your clipboard! Let's paste it into our favorite HTML editor (VS Code for me) and see what it looks like.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fleh1vlf5nux0sg5thcpx.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fleh1vlf5nux0sg5thcpx.png" title="Paste" alt="paste" width="619" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! In a matter of seconds we've generated a complete HTML form. &lt;strong&gt;NOTE&lt;/strong&gt; The form elements use components from &lt;a href="https://material.angular.io/" rel="noopener noreferrer"&gt;Angular Material&lt;/a&gt;, so you'll need that included in your project.&lt;/p&gt;

&lt;p&gt;If you like &lt;a href="https://firedrill.co" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt; show some love and give me a star on &lt;a href="https://github.com/scottlepp/fire-drill" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Thanks for reading!&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>angular</category>
      <category>productivity</category>
      <category>materialdesign</category>
    </item>
    <item>
      <title>Firebase  - Tables &amp; Reports</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Wed, 17 Oct 2018 14:03:30 +0000</pubDate>
      <link>https://dev.to/scottlepp/firebase---tables--reports-4ibh</link>
      <guid>https://dev.to/scottlepp/firebase---tables--reports-4ibh</guid>
      <description>

&lt;p&gt;Using FireDrill for tabular view and reporting of Firebase data.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://firedrill.co/"&gt;FireDrill&lt;/a&gt; we can switch from the default Card view to Table view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MtV05trT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/992/0%2A5f9F-v_N8OhzbdmD" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MtV05trT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/992/0%2A5f9F-v_N8OhzbdmD" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we see a tabular view of our data with sorting and filtering. Nice!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oCeEGaUb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AeDj1LAetCKUtB9Xq" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oCeEGaUb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AeDj1LAetCKUtB9Xq" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also adjust the columns we see and show a Totals footer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GZ6wJW76--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/430/0%2A--bi41WwTW4cWcKl" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GZ6wJW76--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/430/0%2A--bi41WwTW4cWcKl" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After unchecking &lt;strong&gt;Category&lt;/strong&gt; and checking &lt;strong&gt;Totals&lt;/strong&gt; above we no longer see the &lt;strong&gt;Category&lt;/strong&gt; column and we now see a sum of the &lt;strong&gt;Price&lt;/strong&gt; field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qH_cTuLr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AMUW41S1t8ghidF-L" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qH_cTuLr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2AMUW41S1t8ghidF-L" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A nice report of our total shoe inventory! Lets print this so we can share it as a PDF document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VhIlmhDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/309/0%2AZTg1DUhBIhYO3Lnd" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VhIlmhDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/309/0%2AZTg1DUhBIhYO3Lnd" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I see the preview on the right of my report to be printed. I’m using the CutePDF driver which allows printing to PDF, or if you want a hard copy just select your printer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--er0cxJCi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1019/0%2AkmsxdIZOKYrN_-aM" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--er0cxJCi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1019/0%2AkmsxdIZOKYrN_-aM" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here is our pdf report!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9jmR4jzb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2A03m2m3tRWfObCSIV" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9jmR4jzb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/0%2A03m2m3tRWfObCSIV" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do you &lt;strong&gt;FIND&lt;/strong&gt; , &lt;strong&gt;EDIT&lt;/strong&gt; , and &lt;strong&gt;REPORT&lt;/strong&gt; data from Firebase? Drill baby drill…with &lt;a href="https://firedrill.co/"&gt;FireDrill&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As always, if you have any issues please report them &lt;a href="https://github.com/scottlepp/fire-drill"&gt;here&lt;/a&gt;. Happy drilling!&lt;/p&gt;


</description>
      <category>firebase</category>
      <category>typescript</category>
      <category>materialdesign</category>
      <category>angular</category>
    </item>
    <item>
      <title>Super easy Firebase data editing with FireDrill</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Mon, 01 Oct 2018 14:22:28 +0000</pubDate>
      <link>https://dev.to/scottlepp/super-easy-firebase-data-editing-with-firedrill-4j9d</link>
      <guid>https://dev.to/scottlepp/super-easy-firebase-data-editing-with-firedrill-4j9d</guid>
      <description>&lt;p&gt;In my last &lt;a href="https://dev.to/scottlepp/introducing-firedrill---power-tools-for-firebase-g9f"&gt;Post&lt;/a&gt; I showed you how to use &lt;a href="https://firedrill.co" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt; to easily find data in your Firebase database. Now I'll show you how you can easily manage your data.&lt;/p&gt;

&lt;p&gt;With FireDrill we can quickly find our Firebase data and once we find it we can easily edit it. It's also easy to Export your data to CSV (Spreadsheet, Excel) if you want to use Spreadsheets to further visualize or manipulate the data. And finally, you can Import bulk data from a CSV format.&lt;/p&gt;

&lt;p&gt;Let's take a look.&lt;/p&gt;

&lt;p&gt;Again I'll work with my "shoes" collection (which is no where near the size of my wife's shoe collection). Here I've drilled into "shoes".&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9gmjciniaogokxfgtsbl.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9gmjciniaogokxfgtsbl.png" title="Shoes" alt="shoes" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, I found my shoes. I'm having a sale so I want to change the Price of the Converse "Chuck Taylor" shoe. So I'll click "Edit" on the search result. The card will go into Edit mode.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ffbitmwbjfv00s4bx5a5u.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Ffbitmwbjfv00s4bx5a5u.png" title="Edit" alt="edit" width="292" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here I can update any field now, including dates with a nice date picker. I'll update my price and click "Save". That's it! Easy! My price is now updated.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpfv1kjec58ljj03wfww7.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpfv1kjec58ljj03wfww7.png" title="Edit" alt="edit" width="282" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I want to add a new shoe. If I were using the Firebase console, I would add a new item to my collection, then enter each field name and value again, a bit cumbersome. In Firedrill, I can just clone an existing shoe and update the values.&lt;/p&gt;

&lt;p&gt;On any search result item, click "Clone" and a new item will be added to the results in Edit mode:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpdbxqpjtd736ygvhvlo5.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fpdbxqpjtd736ygvhvlo5.png" title="Edit" alt="edit" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now update the value(s) and click "Save". Boom! New shoe. Much easier.&lt;/p&gt;

&lt;p&gt;Awesome, I can add and edit stuff with ease. Now I want to Delete some items. No problem! Just click on the Cards to select them, once selected, you'll see a Delete link. &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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1c5lv4fp5gd34bhu0zy7.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1c5lv4fp5gd34bhu0zy7.png" title="Delete" alt="delete" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click the "Delete" link you see above. Poof! Gone! (Yeah, be careful).&lt;/p&gt;

&lt;p&gt;So now I'd like to Export my shoes into CSV (Spreadsheet) format so I can run some spreadsheet functions or just have it in a format that is common to other tools, like Tableau or something to visualize the data. Or I could just Export to have an easy starting point for Importing bulk data. Easy! Just click the "Export" link.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6zqywft9u59p7xcxt569.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6zqywft9u59p7xcxt569.png" title="Export" alt="export" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Boom! Exported. Now I want to Import lots of shoes from some other data source. I can mass copy or export that other datasource into CSV. Once I have a CSV to import, just click "Import"&lt;/p&gt;

&lt;p&gt;So here is my spreadsheet data. Let's import this.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3tu0abe1gitudub7kia9.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F3tu0abe1gitudub7kia9.png" title="Export" alt="export" width="419" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the "Import" link and browse to your csv file and select it. Boom! Imported!&lt;/p&gt;

&lt;p&gt;So I've showed you how easy it is to manage your Firebase data with FireDrill. Give it a shot. If you encounter any issues, please log them here &lt;a href="https://github.com/scottlepp/fire-drill/issues" rel="noopener noreferrer"&gt;FireDrill Issues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have more ideas, or you would like to become a contributor, let me know!&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>angular</category>
      <category>productivity</category>
      <category>tools</category>
    </item>
    <item>
      <title>Introducing FireDrill - Power tools for Firebase</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Thu, 27 Sep 2018 15:32:05 +0000</pubDate>
      <link>https://dev.to/scottlepp/introducing-firedrill---power-tools-for-firebase-g9f</link>
      <guid>https://dev.to/scottlepp/introducing-firedrill---power-tools-for-firebase-g9f</guid>
      <description>&lt;p&gt;Firebase rocks! We devs can create apps faster than ever, not having to worry about setting up and maintaining back end infrastructure. &lt;/p&gt;

&lt;p&gt;The challenge I found with Firebase was using the Firebase console to find data. Also adding/modifying data in the console is a bit cumbersome.&lt;/p&gt;

&lt;p&gt;To overcome these challenges, I've created &lt;a href="https://firedrill-74829.web.app" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's check it out!&lt;/p&gt;

&lt;p&gt;First we need to connect to our database. The easiest way is to open your Firebase project, then click the Gear and "Project Settings":&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2ahx0ifm55ys9y3pnweh.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F2ahx0ifm55ys9y3pnweh.png" title="Settings" alt="settings" width="543" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now scroll down and select "Add Firebase to your Web App":&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F79g45t81le051hgqm54y.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F79g45t81le051hgqm54y.png" title="Add" alt="add" width="799" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Highlight the section you see below and Ctrl-C (don't use the Copy button)&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fu2859buh9et6uidtg6zs.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fu2859buh9et6uidtg6zs.png" title="Config" alt="config" width="765" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will also need to add &lt;strong&gt;firedrill-74829.web.app&lt;/strong&gt; as an Authorized domain. On the left under Develop click Authentication. Then choose "Sign-in Method", then scroll down to "Authorized domains" and click "Add Domain".  Enter "firedrill-74829.web.app" as your domain.&lt;/p&gt;

&lt;p&gt;Now open &lt;a href="https://firedrill-74829.web.app" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt;. On the Connect page click the JSON tab and paste your connection info. The default database type is "Firestore" but you can change this to "Realtime" depending on your database type. When ready click the Connect button.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fi9kmhy8j162l116xgag1.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fi9kmhy8j162l116xgag1.png" title="Connect" alt="connect" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You are connected! Now you need to sign in. You can use your email or sign in with Google (if you have enabled Google authentication in Firebase). You can skip this step if you haven't secured your data.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv4tkmi9neiqsq5vp2zle.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv4tkmi9neiqsq5vp2zle.png" title="Sign In" alt="signin" width="625" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OK! We are ready to start drilling!&lt;/p&gt;

&lt;p&gt;On the Drill screen you see a "Path" input. Here you can enter a Collection name.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8sutxwcivzsufhd8bwdc.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8sutxwcivzsufhd8bwdc.png" title="Path" alt="path" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have a Collection called "shoes". I'll type "shoes" and click "Drill".&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fayg5o4du50x3t8ihp756.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fayg5o4du50x3t8ihp756.png" title="Path" alt="path" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great I found my shoes. Notice the limit at the right of the Drill button is 10. You can adjust the limit to your liking. You may also notice all the values are links. This allows quick filtering. I'll click the Brand "Jordan" on the first result.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdgspebvnqwj1gp4hfeqn.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fdgspebvnqwj1gp4hfeqn.png" title="Path" alt="path" width="799" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I was able to quickly filter down to only "Jordan" shoes. This is nice for quick filtering, but I want more advanced filtering, like date range or price range. No problem. &lt;/p&gt;

&lt;p&gt;Let's open the filters drawer. I'm going to click the "Show Filters" link you see above the results. Now the drawer opens and I can choose a field to filter on.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv4dqui8o6yw9idyp41hj.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fv4dqui8o6yw9idyp41hj.png" title="Filter" alt="filter" width="698" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm going the select the "price" field and the "Between" type, then add 100 as the Start and 150 as the End price and click "Apply". Now I see only items within that price range. This also works for date fields.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fg6uj9h33r116csr8scm7.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fg6uj9h33r116csr8scm7.png" title="Filter" alt="filter" width="799" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also use "Greater Than", "Less Than" or "Equals" as the Type. &lt;/p&gt;

&lt;p&gt;Now I want to see the highest prices first. No problem. On the right click the "Sort" link and click the "price" field. It sorts in ascending order first (notice the icon next to the field). Just click "Sort" again and click the "price" field again. It will now be in descending order. Notice the icon changed pointing downward.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F18jb9t5wf3pe679c3tx0.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F18jb9t5wf3pe679c3tx0.png" title="Sort" alt="sort" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we can see our results are now sorted by highest price first:&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fol4aoxhs8ahu7m01lsav.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fol4aoxhs8ahu7m01lsav.png" title="Sort" alt="sort" width="799" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I've shown how you can use &lt;a href="https://firedrill-74829.web.app" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt; to quickly find your data in Firebase. I've also shown how you can use range filters and how you can sort your data. In the next article, I'll show you how you can quickly edit and manage your Firebase data. Stay tuned!&lt;/p&gt;

&lt;p&gt;In the meantime, try &lt;a href="https://firedrill-74829.web.app" rel="noopener noreferrer"&gt;FireDrill&lt;/a&gt; out for yourself. If you encounter any issues, please feel free to report them here: &lt;a href="https://github.com/scottlepp/fire-drill/issues" rel="noopener noreferrer"&gt;FireDrill Issues&lt;/a&gt;. Thanks and happy drilling!&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>angular</category>
      <category>productivity</category>
      <category>tools</category>
    </item>
    <item>
      <title>Creating a Search app with Vue.js + Parcel + TypeScript: Part 3 of 3</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Sun, 24 Jun 2018 17:43:38 +0000</pubDate>
      <link>https://dev.to/scottlepp/creating-a-search-app-with-vuejs--parcel--typescript-part-3-of-3-1j5f</link>
      <guid>https://dev.to/scottlepp/creating-a-search-app-with-vuejs--parcel--typescript-part-3-of-3-1j5f</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/scottlepp/creating-a-search-app-with-vuejs--parcel--typescript-part-2-2ega"&gt;Part 2&lt;/a&gt; we added bootstrap-vue and set up a basic layout for a search app. Now we will create components and fetch/display our search results.&lt;/p&gt;

&lt;p&gt;In Part 2 we put all our html in our app component. This provided a quick prototype to view our layout, but a real working app will have separate components. Some advantages of separate components are to encapsulate the complexity of each component and in some cases provide reuse of components.&lt;/p&gt;

&lt;p&gt;Here is the original template from our app component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-navbar&lt;/span&gt; &lt;span class="na"&gt;toggleable=&lt;/span&gt;&lt;span class="s"&gt;"md"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-navbar-toggle&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"nav_collapse"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/b-navbar-toggle&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-navbar-brand&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Zerch&lt;span class="nt"&gt;&amp;lt;/b-navbar-brand&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-navbar&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container-fluid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row mx-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Search input section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-sm-12 pt-3 px-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;b-form&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex justify-content-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- Bug in bootstrap-vue - need div around input or button disappears --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-6 col-8 pl-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;b-input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-100 mr-sm-2"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter Search Term"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-2 my-sm-0"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Search&lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/b-form&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Results section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-columns"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-img-top"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://dummyimage.com/mediumrectangle/222222/eeeeee"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Card image cap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;h5&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Card title that wraps to a new line&lt;span class="nt"&gt;&amp;lt;/h5&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our new app component will now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-navbar&lt;/span&gt; &lt;span class="na"&gt;toggleable=&lt;/span&gt;&lt;span class="s"&gt;"md"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- &amp;lt;b-navbar-toggle target="nav_collapse"&amp;gt;&amp;lt;/b-navbar-toggle&amp;gt; --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-navbar-brand&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Zerch&lt;span class="nt"&gt;&amp;lt;/b-navbar-brand&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-navbar&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container-fluid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row mx-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Search input section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-sm-12 pt-3 px-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;vs-input&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;search=&lt;/span&gt;&lt;span class="s"&gt;"onSearch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/vs-input&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Results section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;vs-results&lt;/span&gt; &lt;span class="na"&gt;:data=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/vs-results&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice we now have vs-input and vs-results tags. Lets create these new components.&lt;/p&gt;

&lt;p&gt;We'll create a file called vs-input.vue and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;b-form&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex justify-content-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Bug in bootstrap-vue - need div around input or button disappears --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-6 col-8 pl-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-input&lt;/span&gt; &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"term"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-100 mr-sm-2"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter Search Term"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-2 my-sm-0"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"search()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Search&lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/b-form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Provide&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-property-decorator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VsInput&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;term&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what is this component doing? Capturing input for our search and providing an event to the app component to indicate the user wants to search.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;b-input element contains the &lt;em&gt;v-model&lt;/em&gt; directive. This will bind the input to the "term" variable&lt;/li&gt;
&lt;li&gt;b-button element has the &lt;em&gt;&lt;a class="mentioned-user" href="https://dev.to/click"&gt;@click&lt;/a&gt;&lt;/em&gt; directive to fire the "search" function on click of the button.&lt;/li&gt;
&lt;li&gt;In our script tag we have our typescript code to declare the &lt;em&gt;term&lt;/em&gt; variable and the &lt;em&gt;search&lt;/em&gt; function. The search function just emits an event with the term, so the app knows when to perform the search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's create a results component to show our results. Add a new file called vs-results.vue with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-columns"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"item in results"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"item.id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"item.thumb"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-img-top"&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"item.thumb"&lt;/span&gt; &lt;span class="na"&gt;:alt=&lt;/span&gt;&lt;span class="s"&gt;"item.title"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;error=&lt;/span&gt;&lt;span class="s"&gt;"error(item)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h5&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{item.name}}&lt;span class="nt"&gt;&amp;lt;/h5&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-text"&lt;/span&gt; &lt;span class="na"&gt;v-html=&lt;/span&gt;&lt;span class="s"&gt;"truncate(item.description || item.abstract, 50)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Provide&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-property-decorator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VsResults&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;results&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thumb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;$forceUpdate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's first focus on the html above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;v-for will iterate over our &lt;em&gt;results&lt;/em&gt; array, which is passed in from the app component, as we will see later.&lt;/li&gt;
&lt;li&gt;The img tag uses the v-if directive to conditionally display a thumbnail, which is bound to the item.thumb property of our search result item.&lt;/li&gt;
&lt;li&gt;The card title is bound to item.title&lt;/li&gt;
&lt;li&gt;The card body is bound to item.description or item.abstract. Note here we use the v-html directive since this content may be html and we want to render it as html not just text. We also call a &lt;em&gt;truncate&lt;/em&gt; method to keep the text limited.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's look closely at the typescript code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have a property called &lt;em&gt;data&lt;/em&gt;. The app component will pass this in.&lt;/li&gt;
&lt;li&gt;We have a computed function called &lt;em&gt;results&lt;/em&gt;. This is what we reference in our template v-for to iterate over the results.&lt;/li&gt;
&lt;li&gt;The truncate function will keep our description limited to 50 words.&lt;/li&gt;
&lt;li&gt;The error function will handle result images that fail to download. This is reference on our img element with the &lt;a class="mentioned-user" href="https://dev.to/error"&gt;@error&lt;/a&gt; directive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The app.vue component needs to be changed now to handle the event from the vs-input component, perform the search, then pass the results to the vs-results component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;b-navbar&lt;/span&gt; &lt;span class="na"&gt;toggleable=&lt;/span&gt;&lt;span class="s"&gt;"md"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- &amp;lt;b-navbar-toggle target="nav_collapse"&amp;gt;&amp;lt;/b-navbar-toggle&amp;gt; --&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-navbar-brand&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Zerch&lt;span class="nt"&gt;&amp;lt;/b-navbar-brand&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-navbar&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container-fluid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row mx-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Search input section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-sm-12 pt-3 px-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;vs-input&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;search=&lt;/span&gt;&lt;span class="s"&gt;"onSearch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/vs-input&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Results section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;vs-results&lt;/span&gt; &lt;span class="na"&gt;:data=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/vs-results&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Provide&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue-property-decorator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VsResults&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./search-results/vs-results.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VsInput&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./search-input/vs-input.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;voyagerApi&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./search-results/search-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;VsResults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;VsInput&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Provide&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
  &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;voyagerApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Notice in the template above the vs-input uses the @search directive to bind the onSearch function. This will fire our onSearch function above when vs-input emits the event.&lt;/li&gt;
&lt;li&gt;The onSearch function will call an api to fetch results. We'll look at this next.&lt;/li&gt;
&lt;li&gt;Also notice in the template the vs-results :data attribute. This is where the app component will pass the results variable to the vs-results component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Almost done. Notice above we import voyagerApi. We need to create that. Add a file called search-api.ts with the following which will fetch search results from a solr index.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;term&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// solr endpoint&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://voyagerdemo.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;daily/solr/v0/select&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id,name:[name],thumb:[thumbURL],abstract,description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// fields we want returned&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?q=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;fl=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;wt=json&amp;amp;rows=20`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code above defines an "api" which is the url to a solr index. The &lt;em&gt;fields&lt;/em&gt; define which fields we would like returned. The &lt;em&gt;q&lt;/em&gt; param passes the "term" the user input and wants to filter the results on. We use the built in "fetch" function to make an ajax call to the api and "await" the results which is a Promise.&lt;/p&gt;

&lt;p&gt;You may also notice the search function is an &lt;em&gt;async&lt;/em&gt; function. This allows us to use &lt;em&gt;await&lt;/em&gt; to make the code appear more synchronous, rather than using Promise.then() syntax.&lt;/p&gt;

&lt;p&gt;That's it! Now if you check the browser you should be able to enter a search term and click "Search" an see something like:&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%2Ffdirqphov1ut8ggnrqh3.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%2Ffdirqphov1ut8ggnrqh3.PNG" alt="Run" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If something went wrong you can grab the full working version from here: &lt;a href="https://github.com/scottlepp/search-vue-parcel-typescript/tree/final" rel="noopener noreferrer"&gt;https://github.com/scottlepp/search-vue-parcel-typescript/tree/final&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>solr</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Creating a Search app with Vue.js + Parcel + TypeScript: Part 2 of 3</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Fri, 22 Jun 2018 12:54:12 +0000</pubDate>
      <link>https://dev.to/scottlepp/creating-a-search-app-with-vuejs--parcel--typescript-part-2-2ega</link>
      <guid>https://dev.to/scottlepp/creating-a-search-app-with-vuejs--parcel--typescript-part-2-2ega</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/scottlepp/creating-a-search-app-with-vue--parcel--typescript-part-1-52f9"&gt;Part 1&lt;/a&gt; we setup basic tooling (vue, parcel and typescript) to develop the app. Here we will add one more tool, bootstrap, to layout our app and make it look more professional and responsive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;. Add bootstrap-vue to our project - from your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;npm install bootstrap-vue --save
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;. Import bootstrap vue components and css &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open &lt;em&gt;main.ts&lt;/em&gt; and add the following after line 2:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;BootstrapVue&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootstrap-vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootstrap/dist/css/bootstrap.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bootstrap-vue/dist/bootstrap-vue.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BootstrapVue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;. Add a simple Navbar &lt;/p&gt;

&lt;p&gt;Open our &lt;em&gt;app.vue&lt;/em&gt; file.  Under &lt;code&gt;div id="app"&lt;/code&gt; paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;b-navbar&lt;/span&gt; &lt;span class="na"&gt;toggleable=&lt;/span&gt;&lt;span class="s"&gt;"md"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="na"&gt;variant=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-navbar-toggle&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"nav_collapse"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/b-navbar-toggle&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;b-navbar-brand&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Zerch&lt;span class="nt"&gt;&amp;lt;/b-navbar-brand&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/b-navbar&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we've used the b-navbar component from bootstrap-vue. See &lt;a href="https://bootstrap-vue.js.org/docs/components/navbar/" rel="noopener noreferrer"&gt;navbar&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;. Add our search input and results.&lt;/p&gt;

&lt;p&gt;Here we're going to add a section with an input with a search button, then a section for the results. This is temporary just to check our layout. Later we will convert these sections into components. Add the following below our navbar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container-fluid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row mx-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Search input section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-sm-12 pt-3 px-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;b-form&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"d-flex justify-content-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="c"&gt;&amp;lt;!-- Bug in bootstrap-vue - need div around input or button disappears --&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-6 col-8 pl-0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;b-input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-100 mr-sm-2"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Enter Search Term"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;b-button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-2 my-sm-0"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Search&lt;span class="nt"&gt;&amp;lt;/b-button&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/b-form&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Results section --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-columns"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-img-top"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://dummyimage.com/mediumrectangle/222222/eeeeee"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Card image cap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;h5&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Card title that wraps to a new line&lt;span class="nt"&gt;&amp;lt;/h5&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
              &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in your browser you should see: &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%2Ftk3exrsbpiaf0mnxb782.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%2Ftk3exrsbpiaf0mnxb782.PNG" alt="Run" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If something went wrong, you can get the full working version here: &lt;a href="https://github.com/scottlepp/search-vue-parcel-typescript/tree/part-2" rel="noopener noreferrer"&gt;https://github.com/scottlepp/search-vue-parcel-typescript/tree/part-2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our layout is set! In &lt;a href="https://dev.to/scottlepp/creating-a-search-app-with-vuejs--parcel--typescript-part-3-of-3-1j5f"&gt;Part 3&lt;/a&gt; we separate the sections into components and create a working app fetching results from a solr index.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>parcel</category>
      <category>bootstrap</category>
      <category>html</category>
    </item>
    <item>
      <title>Creating a Search app with Vue.js + Parcel + TypeScript: Part 1 of 3</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Tue, 19 Jun 2018 21:41:54 +0000</pubDate>
      <link>https://dev.to/scottlepp/creating-a-search-app-with-vue--parcel--typescript-part-1-52f9</link>
      <guid>https://dev.to/scottlepp/creating-a-search-app-with-vue--parcel--typescript-part-1-52f9</guid>
      <description>&lt;p&gt;In my previous article &lt;a href="https://dev.to/scottlepp/extending-traditional-software-with-serverless-microservices-442m"&gt;here&lt;/a&gt; I showed how to "watch" an AWS S3 bucket for changes and update our search index real-time. Now lets write an app to show our search results using: &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;vue.js&lt;/a&gt;, &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;parcel&lt;/a&gt;, &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;typescript&lt;/a&gt; and &lt;a href="https://sass-lang.com/" rel="noopener noreferrer"&gt;sass&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Part 1: App setup
&lt;/h3&gt;

&lt;p&gt;In this part, I'll show how we can quickly get our app set up thanks mostly in part to parcel.js: &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;https://parceljs.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prerequisite:  Install Node.js: &lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;https://nodejs.org/en/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we'll install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;parcel - our "zero configuration" bundler&lt;/li&gt;
&lt;li&gt;vue.js - our ui framework&lt;/li&gt;
&lt;li&gt;typescript - our coding language we'll use here&lt;/li&gt;
&lt;li&gt;sass - our styling language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get ready to rock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;. Create a local directory to put your project (I'll assume you know how).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;. Install Parcel&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open a terminal/command prompt and cd to your new directory&lt;/li&gt;
&lt;li&gt;paste or type the following:  &lt;code&gt;npm install -g parcel-bundler&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;. Setup dependencies. Create a package.json file in your new folder. This defines all of our dependencies. Parcel can attempt to do this for you (though it didn't quite work for me). Instead here is the file you need. Just copy the contents into your new package.json:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;. Install dependencies. Run the command: &lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;. Add entry point.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5.1 Add index.html file as below

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;div id="app"&lt;/code&gt; is the "main" element (top level component of the component hierarchy)&lt;/li&gt;
&lt;li&gt;script tag references our main.ts which sets up our Vue app as we will see&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;5.2 Add main.ts file (this will reference our &lt;code&gt;div id="app"&lt;/code&gt; element). This initializes Vue.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;5.3 Add App.vue file. ".vue" files are called "single file components" where we can put the html, javascript, and css all in one file. I prefer separating concerns so I'll show an alternative approach.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As we see above, we're importing our typescript from a separate file. This has some advantages on larger components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If I need to change the javascript code, I don't have to scroll through the html&lt;/li&gt;
&lt;li&gt;In regards to "clean code" this feels cleaner to me keeping html, javascript and styling in separate files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For smaller components, doing it all inline seems perfectly acceptable. Again, it's really just a matter of preference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5.4 Add our typescript file: app-class.ts (app.ts and App.vue seem to collide in the bundler so I name the ts files [component]-class.ts to avoid collision)&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you probably noticed, I'm using "vue-property-decorator" which allows annotation of our class properties. This will come in handy later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;. Run it! Run the command: &lt;code&gt;parcel index.html&lt;/code&gt;&lt;br&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%2Fqdnvzeicj731m1exc91c.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%2Fqdnvzeicj731m1exc91c.png" alt="Run" width="376" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Done! Open your browser at: &lt;a href="http://localhost:1234/" rel="noopener noreferrer"&gt;http://localhost:1234/&lt;/a&gt;&lt;br&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%2F2z790qk8zgrllirggxl6.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%2F2z790qk8zgrllirggxl6.png" alt="Run" width="397" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If it didn't work for some reason you can clone the full project here:  &lt;a href="https://github.com/scottlepp/search-vue-parcel-typescript" rel="noopener noreferrer"&gt;https://github.com/scottlepp/search-vue-parcel-typescript&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/scottlepp/creating-a-search-app-with-vuejs--parcel--typescript-part-2-2ega"&gt;Part 2&lt;/a&gt; we'll layout our app and add a popular css framework: bootstrap. Bootstrap will make our app look professional and responsive.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>parcel</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Http Request with AWS Lambda, Node.js 8.10, and Standard http library</title>
      <dc:creator>Scott Lepper</dc:creator>
      <pubDate>Sun, 10 Jun 2018 16:44:58 +0000</pubDate>
      <link>https://dev.to/scottlepp/http-request-with-aws-lambda-nodejs-810-and-standard-http-library-2cee</link>
      <guid>https://dev.to/scottlepp/http-request-with-aws-lambda-nodejs-810-and-standard-http-library-2cee</guid>
      <description>&lt;p&gt;In my previous article I showed an approach to extend a "traditional" (monolithic architecture) app using AWS Lambda:  &lt;a href="https://dev.to/scottlepp/extending-traditional-software-with-serverless-microservices-442m"&gt;https://dev.to/scottlepp/extending-traditional-software-with-serverless-microservices-442m&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take a closer look at the Lambda function and how to make an http request using Node.js 8.10 and the standard http library.&lt;/p&gt;

&lt;p&gt;In previous versions of node.js, the handler function contained a callback argument like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when your http request finished you would execute the callback to indicate the function was finished:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And yet even older version of node.js didn't have a callback function, instead you would use "context.succeed" like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;succeed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, in node.js 8.10 this has changed. The callback argument is again not needed. Now you just wrap your function returning a Promise. Then instead of executing the callback function, you execute the Promise resolve function (or reject function if it fails) as so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ec2-18-191-89-162.us-east-2.compute.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/repos/r1639420d605/index?delta=true&amp;amp;clear=false&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// send the request&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! These changes between versions of node.js tripped me up a bit so I wanted to share the latest method. Hope this helps someone!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>node</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
