<?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: Thomas Sarmis</title>
    <description>The latest articles on DEV Community by Thomas Sarmis (@sarmis).</description>
    <link>https://dev.to/sarmis</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%2F2289%2F6316982.jpeg</url>
      <title>DEV Community: Thomas Sarmis</title>
      <link>https://dev.to/sarmis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarmis"/>
    <language>en</language>
    <item>
      <title>How I automated typing for coding tutorials with C# (introducing ChoreoTyper)</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Wed, 20 Aug 2025 08:04:26 +0000</pubDate>
      <link>https://dev.to/sarmis/how-i-automated-typing-for-coding-tutorials-with-c-introducing-choreotyper-2f6f</link>
      <guid>https://dev.to/sarmis/how-i-automated-typing-for-coding-tutorials-with-c-introducing-choreotyper-2f6f</guid>
      <description>&lt;p&gt;When recording coding tutorials, I found myself spending more time editing out typing errors than teaching.&lt;br&gt;&lt;br&gt;
So I built &lt;strong&gt;ChoreoTyper&lt;/strong&gt;, a free utility that automates keystrokes from a text file into the active window.&lt;/p&gt;

&lt;p&gt;✨ Use cases:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coding tutorials (no typos, repeatable scripts)
&lt;/li&gt;
&lt;li&gt;Live demos (type pre-written commands/code)
&lt;/li&gt;
&lt;li&gt;Reducing editing work for creators
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🛠 Built in C#, works with .NET 10 preview.&lt;br&gt;&lt;br&gt;
📹 &lt;a href="https://youtu.be/1MzlD425b0w" rel="noopener noreferrer"&gt;Short demo video&lt;/a&gt;&lt;br&gt;
📖 &lt;a href="https://greekdeveloper.com/projects/choreo-typer" rel="noopener noreferrer"&gt;Project Page&lt;/a&gt;&lt;br&gt;
💻 &lt;a href="https://github.com/greek-developer/choreotyper" rel="noopener noreferrer"&gt;Source code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love feedback from other tutorial creators!&lt;/p&gt;

</description>
      <category>automation</category>
    </item>
    <item>
      <title>Observability: Metrics in the .NET Ecosystem</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Thu, 14 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarmis/observability-metrics-in-the-net-ecosystem-4kp3</link>
      <guid>https://dev.to/sarmis/observability-metrics-in-the-net-ecosystem-4kp3</guid>
      <description>&lt;h2&gt;
  
  
  Monitoring .NET Web API Metrics with Prometheus and Grafana on Windows
&lt;/h2&gt;

&lt;p&gt;In this post, we learn how to instrument a .NET Web API with OpenTelemetry Metrics, how to collect them using Prometheus, and how to visualize them in Grafana.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;: &lt;a href="https://github.com/greek-developer-courses/observability-intro-metrics" rel="noopener noreferrer"&gt;Code Repository&lt;/a&gt; |&lt;a href="https://youtu.be/WCPAgxp2KQY" rel="noopener noreferrer"&gt;YouTube video&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Solution Layout
&lt;/h3&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%2Fx01t2j4bw8g4gq5m7jqt.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%2Fx01t2j4bw8g4gq5m7jqt.png" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The solution we will build has three major components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A C# application instrumented with OpenTelemetry Metrics.&lt;/li&gt;
&lt;li&gt;Prometheus, which scrapes (collects) and stores the metrics exposed by the application.&lt;/li&gt;
&lt;li&gt;Grafana, which retrieves, transforms, and visualizes the metrics stored in Prometheus.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before we start implementing the solution, we should understand what a metric is and how it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are Metrics?
&lt;/h3&gt;

&lt;p&gt;In the context of observability, a metric is a &lt;strong&gt;named&lt;/strong&gt;, &lt;strong&gt;labeled&lt;/strong&gt;, &lt;strong&gt;timestamped&lt;/strong&gt;, numerical measurement that captures some property of system behavior. Let’s break that down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Named&lt;/strong&gt; : All measurements of the same logical thing share the same metric name. For example, &lt;code&gt;http_server_request_count&lt;/code&gt; represents the number of HTTP requests the server handled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labeled&lt;/strong&gt; : We differentiate series with the same name using label key/value pairs (dimensions). For example, &lt;code&gt;http_server_request_count&lt;/code&gt; might include &lt;code&gt;http_route&lt;/code&gt; and &lt;code&gt;response_code&lt;/code&gt; labels so we can slice by endpoint and status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamped&lt;/strong&gt; : Each measurement corresponds to a point in time (explicitly or implicitly when scraped).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Prometheus exposition format (text-based and most common) expresses each sample as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;metric_name{label1="value1",label2="value2"} value [optional_timestamp]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If the timestamp is missing, Prometheus uses the scrape time.&lt;/p&gt;

&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http_server_request_count{http_route="/weatherforecast",response_code="200"} 15
http_server_request_count{http_route="/health/ready",response_code="200"} 200

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells us &lt;code&gt;/weatherforecast&lt;/code&gt; has been called 15 times and &lt;code&gt;/health/ready&lt;/code&gt; 200 times since the process started.&lt;/p&gt;

&lt;p&gt;Our application only exposes the &lt;em&gt;current cumulative totals&lt;/em&gt;. Prometheus stores the time series history across scrapes. From that history we can derive &lt;em&gt;rates&lt;/em&gt; by diffing successive samples. Because we record cumulative counts, values should monotonically increase—except when the process restarts (the counter resets).&lt;/p&gt;

&lt;h3&gt;
  
  
  Measuring Durations
&lt;/h3&gt;

&lt;p&gt;Measuring durations is trickier than counting invocations. A naive approach would record only the &lt;em&gt;total&lt;/em&gt; cumulative duration and divide by total calls to get an average. That hides latency spikes and distribution shape: we can’t tell whether all calls are uniformly slow or a few outliers skew the mean.&lt;/p&gt;

&lt;p&gt;OpenTelemetry (and Prometheus histograms) address this by tracking counts of observations falling into pre-defined latency buckets. With bucketed counts plus the total count and sum, we can approximate latency distributions and compute quantiles (e.g., 50th / 95th percentile) with reasonable accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h2&gt;
  
  
  DotNet WebAPI application
&lt;/h2&gt;

&lt;p&gt;Now that we have some background, let’s build the solution. We’ll start by creating a new .NET Web API project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;gt; dotnet new webapi --name weatherapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s add the required package references:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;gt; cd weatherapp
\&amp;gt; dotnet add package OpenTelemetry.Extensions.Hosting
\&amp;gt; dotnet add package OpenTelemetry.Instrumentation.AspNetCore
\&amp;gt; dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore --prerelease
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we add code to &lt;code&gt;Program.cs&lt;/code&gt;. We need three blocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;using&lt;/code&gt; statements at the top.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;builder.Services.AddOpenTelemetry()&lt;/code&gt; configuration for metrics (resource, meters, instrumentation, exporter).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app.MapPrometheusScrapingEndpoint();&lt;/code&gt; to expose the &lt;code&gt;/metrics&lt;/code&gt; endpoint.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+using System.Diagnostics.Metrics;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();

+builder.Services.AddOpenTelemetry()
+ .ConfigureResource(resourceBuilder =&amp;gt; resourceBuilder
+ .AddService("weatherapp", "1.0.0"))
+ .WithMetrics(metricsProviderBuilder =&amp;gt; metricsProviderBuilder
+ .AddMeter("weatherapp").AddAspNetCoreInstrumentation()
+ .AddPrometheusExporter());                

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();

+ app.MapPrometheusScrapingEndpoint();

var summaries = new[]
... 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a good practice, let’s also edit &lt;code&gt;appsettings.json&lt;/code&gt; to pin the server’s port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
+ "urls":"http://localhost:7070"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our application is now ready. It exposes metrics at &lt;a href="http://localhost:7070/metrics" rel="noopener noreferrer"&gt;http://localhost:7070/metrics&lt;/a&gt;; we can confirm this by opening the endpoint in a browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prometheus: Setup &amp;amp; Run
&lt;/h2&gt;

&lt;p&gt;Prometheus scrapes (reads) the &lt;code&gt;/metrics&lt;/code&gt; endpoint and stores the time series. We set it up as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the latest binary (&lt;a href="https://dev.to/dl/prometheus"&gt;version 3.5&lt;/a&gt; at the time of writing) from the &lt;a href="https://github.com/prometheus/prometheus/releases" rel="noopener noreferrer"&gt;Prometheus Releases&lt;/a&gt; page.&lt;/li&gt;
&lt;li&gt;Extract the contents to a folder.&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;prometheus.yml&lt;/code&gt; and append our scrape job (watch the indentation):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
scrape_configs:
  # The job name is added as a label `job=&amp;lt;job_name&amp;gt;` t...
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]
       # The label name is added as a label `label_name=&amp;lt;label_value&amp;gt;` to ...
        labels:
          app: "prometheus"

+ - job_name: "weatherapp"
+ static_configs:
+ - targets: ["localhost:7070"]
+ labels:
+ app: "weatherapp"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run Prometheus (&lt;code&gt;prometheus.exe&lt;/code&gt;) and visit &lt;a href="http://localhost:9090/targets" rel="noopener noreferrer"&gt;http://localhost:9090/targets&lt;/a&gt; to verify it’s running and scraping our app.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Grafana: Setup &amp;amp; Run
&lt;/h2&gt;

&lt;p&gt;Grafana lets us explore and visualize the collected metrics:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download the latest OSS standalone binaries (&lt;a href="https://dev.to/dl/grafana"&gt;version 12.11&lt;/a&gt; at the time of writing) from the &lt;a href="https://grafana.com/grafana/download?platform=windows&amp;amp;edition=oss" rel="noopener noreferrer"&gt;Grafana downloads&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Extract the contents to a folder.&lt;/li&gt;
&lt;li&gt;(Optional) Remove authentication: create &lt;code&gt;conf/custom.ini&lt;/code&gt; and add:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[security]
allow_embedding = true
cookie_samesite = none
cookie_secure = false

[auth.anonymous]
enabled = true
org_role = Admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run Grafana (&lt;code&gt;bin/grafana-server.exe&lt;/code&gt;) and visit &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to verify it’s running.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Grafana: Add Prometheus Data Source
&lt;/h2&gt;

&lt;p&gt;Before we can query metrics we add Prometheus as a data source:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="http://localhost:3000/connections/datasources/new" rel="noopener noreferrer"&gt;Grafana's New Datasource UI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Find and Select Prometheus&lt;/li&gt;
&lt;li&gt;Set the "Prometheus server URL" to &lt;code&gt;http://localhost:9090&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save &amp;amp; Test&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Grafana: Create a Dashboard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="http://localhost:3000/dashboards" rel="noopener noreferrer"&gt;Dashboards UI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Create a new dashboard: &lt;strong&gt;New → Dashboard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add a panel: &lt;strong&gt;Add → Visualization&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Switch the query editor to “Code” mode using the builder/code toggle.&lt;/li&gt;
&lt;li&gt;Enter a PromQL query (e.g., &lt;code&gt;rate(http_server_request_duration_seconds_count[1m])&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Save the dashboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can add additional panels using more queries. Here are a few starting points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// HTTP Request Rate (Requests/Second)
rate (http_server_request_duration_seconds_count[1m])

// HTTP Request Duration (Seconds, 95th Percentile) 
histogram_quantile(
    0.95, 
    sum(rate(http_server_request_duration_seconds_bucket[1m])) by (le)`

// HTTP Request Duration (Seconds, 50th Percentile) 
histogram_quantile(
    0.50, 
    sum(rate(http_server_request_duration_seconds_bucket[1m])) by (le))

// HTTP Request Duration (Seconds, 75th Percentile, by route) 
histogram_quantile(
    0.75, 
    sum(rate(http_server_request_duration_seconds_bucket[1m])) by (le, http_route))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;We now know how to add metrics to our .NET Web API, collect them with Prometheus, and visualize them in Grafana. Next steps could include adding traces, correlating metrics with logs, or exporting metrics to a long-term storage backend.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>obervability</category>
      <category>opentelemetry</category>
      <category>metrics</category>
    </item>
    <item>
      <title>AutoInvoke (Runtime method resolving and invoking)</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Sun, 29 Dec 2019 09:25:33 +0000</pubDate>
      <link>https://dev.to/sarmis/autoinvoke-runtime-method-resolving-and-invoking-3cph</link>
      <guid>https://dev.to/sarmis/autoinvoke-runtime-method-resolving-and-invoking-3cph</guid>
      <description>&lt;p&gt;During my years of programming I often run into the issue of dynamically (during runtime) deciding which function to call.&lt;/p&gt;

&lt;p&gt;This may happen while developing a CLI tool where you need to call a different method based on some command line parameter or when building a command processing/routing module for some backend system.&lt;/p&gt;

&lt;p&gt;Typically I would just solve this issue per case, but I decided to build a library for that.&lt;/p&gt;

&lt;p&gt;I present to you &lt;strong&gt;AutoInvoke&lt;/strong&gt; it is a static class and its usage is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;AutoInvoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"add"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or for resolving overloaded methods&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PayloadHandler&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;AHandledCount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;BHandledCount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APayload&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;AHandledCount&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BPayload&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;BHandledCount&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;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Invoke_WithSpecificType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_handler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PayloadHandler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;AutoInvoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Handle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;APayload&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AHandledCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;AutoInvoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Handle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BPayload&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;AutoInvoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Handle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;BPayload&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AHandledCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BHandledCount&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;It is in alpha stage but the basic functionality is there.&lt;/p&gt;

&lt;p&gt;The main open issues are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cannot handle extension methods&lt;/li&gt;
&lt;li&gt;Cannot await async methods (will return Task)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Greekdev.AutoInvoke/" rel="noopener noreferrer"&gt;nuget&lt;/a&gt;&lt;br&gt;
&lt;a href="https://gitlab.com/greekdev/greekdev.autoinvoke" rel="noopener noreferrer"&gt;repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>showdev</category>
    </item>
    <item>
      <title>What if Business Requirments where semi-structured files in project's repo</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Sun, 22 Sep 2019 15:21:39 +0000</pubDate>
      <link>https://dev.to/sarmis/what-if-business-requirments-where-semi-structured-files-in-project-s-repo-1imc</link>
      <guid>https://dev.to/sarmis/what-if-business-requirments-where-semi-structured-files-in-project-s-repo-1imc</guid>
      <description>&lt;p&gt;During my dev career, I have many times witness the case that business requirements (and thus, consequently, functional specifications) were not treated with the same scrutiny as the source code. The source code usually passes at-least from one human reviewer that actively tries to improve the quality of it, there are standards defined regarding the quality of the code, and formal tools (usually git) are used to track the changes and the reason behind them.&lt;/p&gt;

&lt;p&gt;Business Requirements are ofter described in issues on a separate issue management system and are usually incomplete and separated from source code. Thus it is easier for the code and requirements to fall out of sync (ie a new issue is created for an updated Business Requirement, but the old commits still point to the old issue that holds an outdated business requirement).&lt;/p&gt;

&lt;p&gt;To handle both the issue of sub-par business requirements and their "distance" from the codebase I'm thinking of defining a "standard" that would require the business requirements to be stored in markdown files inside the source code repository.&lt;/p&gt;

&lt;p&gt;My current approach is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Possibly&lt;/strong&gt; split business requirements from functional requirements (functional requirements are written based on business requirements but are more technical)&lt;/li&gt;
&lt;li&gt;Allow hierarchical structures to be defined by referencing &lt;em&gt;parent&lt;/em&gt; requirements.&lt;/li&gt;
&lt;li&gt;Provide meta-data (like front-matter in static site generators)&lt;/li&gt;
&lt;li&gt;Link acceptance criteria to test cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A command-line utility will be built that will: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enforce &lt;em&gt;Definition of Ready&lt;/em&gt; by presenting requirements without acceptance criteria as not ready&lt;/li&gt;
&lt;li&gt;provide the current status of the project based on the state of each business requirement (not ready, no tests are written, tests are written but fail, tests are written and passing)&lt;/li&gt;
&lt;li&gt;be able to run on pre-commit or pre-merge to either indicate issues or fail the commit/merge&lt;/li&gt;
&lt;li&gt;be able to run on ci/cd to generate and publish the current state of the project &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, what are your opinions about this? Do you see any value on this approach? What would you require to use this approach instead of what you are already using?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>requirements</category>
      <category>git</category>
    </item>
    <item>
      <title>Modern WebApps - Style: Vuetify</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Wed, 08 May 2019 19:45:35 +0000</pubDate>
      <link>https://dev.to/sarmis/vuetifing-the-web-app-aoo</link>
      <guid>https://dev.to/sarmis/vuetifing-the-web-app-aoo</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/sarmis/single-page-progressive-web-applications-with-vue-js-2op8"&gt;(PWA Infrastructure: Vue, Parcel &amp;amp; Workbox)&lt;/a&gt; we build a template for single-page, progressive web applications using &lt;a href="https://vuejs.org" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt; and &lt;a href="https://developers.google.com/web/tools/workbox/" rel="noopener noreferrer"&gt;Workbox&lt;/a&gt;. The result was funtional, but not good-looking, in this article we will work on that.&lt;/p&gt;

&lt;p&gt;There are many options for making a webapp good-looking, from hand-writting your css, using a css framework, or since we already use Vue.js, using a vue components library, which has the added benefit of adding functionality. There are various vue component libraries, one of the most promising is &lt;a href="https://vuetifyjs.com" rel="noopener noreferrer"&gt;Vuetify.js&lt;/a&gt; which implements google's material design and, thus, looks almost native in android phones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;The code for this tutorial can be found in &lt;a href="https://github.com/sarmis/greekdev-template-vue-spa" rel="noopener noreferrer"&gt;project's github repo&lt;/a&gt; in the &lt;strong&gt;vuetify branch&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating vuetify
&lt;/h2&gt;

&lt;p&gt;Integrating vuetify is actually very simple.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install vuetify and material design icons by running the following commands:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\&amp;gt;npm i -s vuetify
\&amp;gt;npm i -s material-design-icons-iconfont
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Import the scripts and add them to Vue by adding the following in &lt;code&gt;src\web\index.js&lt;/code&gt; (just after &lt;code&gt;import Vue from 'vue'&lt;/code&gt;)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vuetify from 'vuetify';
import 'vuetify/dist/vuetify.min.css' // Ensure you are using css-loader
import 'material-design-icons-iconfont'
Vue.use(Vuetify);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using vuetify
&lt;/h2&gt;

&lt;p&gt;There is a lot of documentation and examples in &lt;a href="https://vuetifyjs.com" rel="noopener noreferrer"&gt;Vuetify.js site&lt;/a&gt; but let's build a very simple web app layout.&lt;/p&gt;

&lt;p&gt;We will only touch the &lt;code&gt;.vue&lt;/code&gt; files&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;src\web\app.vue&lt;/code&gt; we will add a permanent toolbat at the top and a navigation drawer at the left side of the page. We will configure the toolbar to be always visible, and the navigation drawer open by default in pages wider that 640 pixels.&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&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"pug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    v-app
        v-toolbar(app)
            v-toolbar-side-icon(@click="drawer=!drawer")
            v-toolbar-title Home
            v-spacer
            v-toolbar-items
                v-btn(flat to="/") home
                v-btn(flat to="/profile") profile
                v-btn(flat to="/about") about    

        v-navigation-drawer(v-model="drawer" app fixed mobile-break-point=640)
            v-list()               
                v-list-tile( to="/profile")
                    v-list-tile-action
                        v-icon account_circle
                    v-list-tile-title Profile

                v-divider

                v-list-tile(to="/" exact)
                    v-list-tile-action
                        v-icon home
                    v-list-tile-title Home

                v-list-tile(to="/about" exact)
                    v-list-tile-action
                        v-icon notes
                    v-list-tile-title about 

        v-content           
            router-view

&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vue&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;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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&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;./_router.js&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;store&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;./_store.js&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;{&lt;/span&gt; 
            &lt;span class="na"&gt;drawer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;router&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;In &lt;code&gt;src\web\vues\profile.vue&lt;/code&gt; we add a (dummy) login form using a &lt;code&gt;v-card&lt;/code&gt; 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&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"pug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    v-container(fluid fill-height)
        v-layout(justify-center)
            v-flex(xs12 sm8 md4)

                v-card(class="elevation-12")

                    v-toolbar( dark color="primary")
                        v-toolbar-title Login

                    v-card-text
                        v-form
                            v-text-field(prepend-icon="person" name="login" label="Username" type="text")
                            v-text-field(id="password" prepend-icon="lock" name="password" label="Password" type="password")

                    v-card-actions
                        v-spacer
                        v-btn(color="primary") Login

&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;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="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;We make some minor alterations in &lt;/p&gt;

&lt;p&gt;&lt;code&gt;src\web\vues\home.vue&lt;/code&gt;&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&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"pug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    v-container(fluid) 
        h1 Home
        p Welcome: {{$store.state.name}}

&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;script&amp;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="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;Finally we modify &lt;code&gt;src\web\vues\about.vue&lt;/code&gt;&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&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"pug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    v-container(fluid fill-height)
        v-layout(justify-center)
            v-flex(xs12 sm8 md4)

                v-card(class="elevation-12")

                    v-toolbar( dark color="primary")
                        v-toolbar-title About

                    v-card-text
                        p Let's Vuetify the template!                       

                    v-card-actions
                        v-spacer                        

&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;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="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;h2&gt;
  
  
  Pug ?
&lt;/h2&gt;

&lt;p&gt;We opted to use &lt;code&gt;pug&lt;/code&gt; instead of &lt;code&gt;html&lt;/code&gt; as our markup language, it's a personal preference mainly due to being more compact ( a well structured page in pug should have about half the lines versus the html version - due to not having closing tags).&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;Time to see the end-result, in desktop browser:&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/.%2Fresult-desktop.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/.%2Fresult-desktop.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and in mobile (with the navigation drawer closed, and with navigation drawer open):&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/.%2Fresult-mobile.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/.%2Fresult-mobile.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;In the next post we will add some real functionality in our web app.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="https://greekdeveloper.com/2019/vuetify-template/" rel="noopener noreferrer"&gt;Original post at greek developer&lt;/a&gt;
&lt;/h6&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>vuetify</category>
    </item>
    <item>
      <title>Modern WebApps - Infrastructure: Vue, Parcel &amp; Workbox</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Wed, 17 Apr 2019 06:46:54 +0000</pubDate>
      <link>https://dev.to/sarmis/single-page-progressive-web-applications-with-vue-js-2op8</link>
      <guid>https://dev.to/sarmis/single-page-progressive-web-applications-with-vue-js-2op8</guid>
      <description>&lt;p&gt;Single Page Applications (SPA) are web applications that are contained in a single web page, providing a seamless navigation experience due to not having to download and parse the html for each page. Progressive Web Applications (PWA) are web applications that, using a service worker "proxy" and a manifest file, provide the necessary infrastructure to allow cashing of the application by the browser in order to be usable in poor, or no-network conditions. All modern browsers and OSes allow PWAs to be "installed" locally and, thus, provide for a native-like user experience.&lt;/p&gt;

&lt;p&gt;A PWA is often a viable alternative to building a native application, especially for small teams, since most app stores now accept PWAs and all major OSes (Windows, Android, iOS) allow PWAs to be installed and appear on the desktop. PWAs open instantly and the browser can be directed to hide it's controls providing a native-like look and feel. &lt;/p&gt;

&lt;p&gt;Modern tooling can simplify development but setting it up can be a time-consuming task. Let's see how to setup a SPA &amp;amp; PWA project. The scope of this tutorial is to describe the setup and not each framework/tools specifically - Each of these tools has extended documentation that explains how each works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Framework &amp;amp; Tools
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Vue.js
&lt;/h4&gt;

&lt;p&gt;We will use the Vue ecosystem for the heavylifting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt;&lt;/strong&gt; will handle our views by providing a declarative approach in defining them and seperating the code in single-file-components, &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://vuex.vuejs.org" rel="noopener noreferrer"&gt;VueX&lt;/a&gt;&lt;/strong&gt; will be used for state management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://router.vuejs.org/" rel="noopener noreferrer"&gt;Vue Router&lt;/a&gt;&lt;/strong&gt; will be used to handle the SPA routes&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Node.js
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;node.js&lt;/a&gt;&lt;/strong&gt; will provide support for the bundling utilities and all other utilities that might be required&lt;/p&gt;

&lt;h4&gt;
  
  
  Parcel.js
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;&lt;/strong&gt; bundler will be used to build and bundle the application&lt;/p&gt;

&lt;h4&gt;
  
  
  Workbox
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://developers.google.com/web/tools/workbox/" rel="noopener noreferrer"&gt;Workbox&lt;/a&gt;&lt;/strong&gt; will handle the service-worker details.&lt;/p&gt;




&lt;h2&gt;
  
  
  Files layout
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./src&lt;/code&gt; will contain all source code for this project.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./src/web&lt;/code&gt; will contain the source code for the web application (the html client).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./src/db&lt;/code&gt; (optional) will contain any database initialization scripts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./src/server&lt;/code&gt; (optional) will contain any server-side projects&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;./dist&lt;/code&gt; will contain all generated artifacts and should be ignored in git

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;./dist/web&lt;/code&gt; will contain the builded and bundled web application.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./dist/db&lt;/code&gt; (optional) will contain any artifacts generated by the database scrits&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./dist/server&lt;/code&gt;(optional) will contain any server-side projects (compiled)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;./.cache&lt;/code&gt; will be generated by parcel and should be ignored in git&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;./node_modules&lt;/code&gt; will be generated by npm or parcel and should be ingored in git&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;The code can be found in &lt;a href="https://github.com/sarmis/greekdev-template-vue-spa" rel="noopener noreferrer"&gt;project's github repo&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Javascript dependencies
&lt;/h4&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%2Fgreekdeveloper.com%2F2019%2Fvue-template%2Fdependencygraph.svg" 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%2Fgreekdeveloper.com%2F2019%2Fvue-template%2Fdependencygraph.svg" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Entry point (index.html)
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;./src/web/index.html&lt;/code&gt; is our entry point and just links everything together&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;link rel="manifest" href="./manifest.webmanifest"&amp;gt;&lt;/code&gt; links the &lt;em&gt;.webmanifest&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;div id="vueapp"&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt; defines vue mounting point
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;script src="./index.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt; loads the script that contains the vue application&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;navigator.serviceWorker.register('/service-worker.js');&lt;/code&gt; registers the service worker script
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&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;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"ie=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"./manifest.webmanifest"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Vue.js Single Page Application Template&lt;span class="nt"&gt;&amp;lt;/title&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;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"vueapp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&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;script&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/service-worker.js&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;span class="nt"&gt;&amp;lt;/script&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;h4&gt;
  
  
  Manifest
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;./src/web/manifest.webmanifest&lt;/code&gt; describes the application and is required for the application to be considered a PWA.&lt;br&gt;
It's important to maintain the &lt;em&gt;.webmanifest&lt;/em&gt; extension for parcel compatibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My application name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  
    &lt;/span&gt;&lt;span class="nl"&gt;"short_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nl"&gt;"start_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#3367D6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standalone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#3367D6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/res/app-256.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"256x256"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Service Worker (Workbox)
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;./src/web/service-worker.js&lt;/code&gt; implements the service worker that is requred for considering the application to be a PWA. Google's workbox is used. Workbox defines multiple stategies (network-first, cache-first, and Stale-while-revalidate). In this example all resources are served using the network-first strategy since this is the most responsice approach and maintains the capability to work offline.&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service-worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// import service worker script&lt;/span&gt;
&lt;span class="nf"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://storage.googleapis.com/workbox-cdn/releases/4.2.0/workbox-sw.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Network First&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="c1"&gt;// Index &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="c1"&gt;// Anything in the same host&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="c1"&gt;// Anything in any host &lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mask&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;workbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;workbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NetworkFirst&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cacheName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dynamic&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;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Vue binding
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;./src/web/index.js&lt;/code&gt; is used to bind the vue application and our css (in scss). It imports the Vue framework, our vue application code (&lt;code&gt;app.vue&lt;/code&gt;) and our styles (&lt;code&gt;styles.scss&lt;/code&gt;) - All these files are located in &lt;code&gt;./src/web/&lt;/code&gt; but we are using relative paths in the imports. Finally we mount our vue application to the corresponding div element.&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="nx"&gt;Vue&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&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;App&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;./app.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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./style.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vue&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;$mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#vueapp&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;h4&gt;
  
  
  Vue Application
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;./src/web/app.vue&lt;/code&gt; contains our vue application as a single file component.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;  we construct a simple navigational menu and the &lt;em&gt;router-view&lt;/em&gt; which is the host for our single page application, all other pages are mounted in the router-view element. In this template we are using &lt;code&gt;pug&lt;/code&gt; insteand of html.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; we import the vue framework and two custom modules, the &lt;code&gt;_router.js&lt;/code&gt; and the &lt;code&gt;_store.js&lt;/code&gt; and we create our vue application by extending the default vue application with the &lt;em&gt;store&lt;/em&gt; and &lt;em&gt;router&lt;/em&gt; modules we just loaded.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; we providing some local (scoped) styling for the menu using &lt;em&gt;scss&lt;/em&gt; (which out bundler will convert to css)&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&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"pug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    div
        nav.navbar
            router-link(to="/") home
            router-link(to="/profile") profile
            router-link(to="/about") about
        router-view
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Vue&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;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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&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;./_router.js&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;store&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;./_store.js&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
        &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;router&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;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"scss"&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nc"&gt;.navbar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Router
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;./src/web/_router.js&lt;/code&gt; configures and initializes &lt;em&gt;vue-router&lt;/em&gt; by loading all pages and declaring their routes.&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="nx"&gt;Vue&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;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;VueRouter&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-router&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VueRouter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Import Components&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;home&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;./vues/home.vue&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;about&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;./vues/about.vue&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;profile&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;./vues/profile.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Define some routes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&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;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;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;       &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt;    &lt;span class="p"&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;/profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="p"&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;/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;about&lt;/span&gt;   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Create &amp;amp; Export the router &lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&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;VueRouter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Store
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;./src/web/_store.js&lt;/code&gt;  configures and initializes the vuex store module. It declares the global state and the available mutations. The vuex allows for global state to be modified by all view components (through the mutations) while mainting the reactivity of the framework. (ie commiting a mutation will update all components that are affected by the state change).&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="nx"&gt;Vue&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&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;Vuex&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;vuex&lt;/span&gt;&lt;span class="dl"&gt;'&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Vuex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vuex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// Usege: $store.commit('mutationan', parameter)&lt;/span&gt;
    &lt;span class="na"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&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="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Pages
&lt;/h4&gt;

&lt;p&gt;We have three pages in our example, &lt;em&gt;home&lt;/em&gt; and &lt;em&gt;about&lt;/em&gt; are almost identical, both are rendering the &lt;em&gt;name&lt;/em&gt; property of the store.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;profile&lt;/em&gt; provides an input box where the user an type his name and instantly updates the global state when the value of the input changes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./src/web/vues/about.vue&lt;/code&gt;&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&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"pug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    div 
        h1 About
        p  Welcome: {{$store.state.name}}
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;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="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;&lt;code&gt;./src/web/vues/home.vue&lt;/code&gt;&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&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Home&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt; 
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt; Welcome: {{$store.state.name}}&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;/template&amp;gt;&lt;/span&gt;


&lt;span class="nt"&gt;&amp;lt;script&amp;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="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;&lt;code&gt;./src/web/profile.vue&lt;/code&gt;&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&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"pug"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  div 
    h1 Profile
    p  Welcome: {{$store.state.name}}
    div.form 
      table
        tr
          td Name
          td
            input(:value="$store.state.name" @input="$store.commit('setName',$event.target.value)")
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;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="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;span class="nt"&gt;&amp;lt;style &lt;/span&gt;&lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"scss"&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nc"&gt;.form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&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;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Developing
&lt;/h2&gt;

&lt;p&gt;The following steps are required in order to develop on this template&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Download or clone the &lt;a href="https://github.com/sarmis/greekdev-template-vue-spa" rel="noopener noreferrer"&gt;code&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install parcel &lt;code&gt;npm i -g parcel-bundler&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install project dependencies &lt;code&gt;npm install&lt;/code&gt; (in project root)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the dev script &lt;code&gt;npm run dev&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h6&gt;
  
  
  &lt;a href="https://greekdeveloper.com/2019/vue-template/" rel="noopener noreferrer"&gt;Original post at greek developer&lt;/a&gt;
&lt;/h6&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>vue</category>
    </item>
    <item>
      <title>Webapp Templates - What would you like to see?</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Sat, 06 Apr 2019 09:19:24 +0000</pubDate>
      <link>https://dev.to/sarmis/webapp-templates-what-would-you-like-to-see-3bnm</link>
      <guid>https://dev.to/sarmis/webapp-templates-what-would-you-like-to-see-3bnm</guid>
      <description>&lt;p&gt;I am setting up some web app templates - Will use Vue for the client and would have three implementations for the server: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.net core&lt;/li&gt;
&lt;li&gt;node.js&lt;/li&gt;
&lt;li&gt;firebase firestore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not sure if i should build just a template or include some sample funtionality&lt;br&gt;
What's your opinion on this? &lt;/p&gt;

&lt;p&gt;This is the repo of the first sample (contains only client-side currently)&lt;br&gt;
&lt;a href="https://github.com/sarmis/greekdev-template-vue-spa" rel="noopener noreferrer"&gt;https://github.com/sarmis/greekdev-template-vue-spa&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Hugo 102: Setting Up</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Fri, 15 Mar 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/sarmis/hugo-102-setting-up-1fo9</link>
      <guid>https://dev.to/sarmis/hugo-102-setting-up-1fo9</guid>
      <description>&lt;p&gt;Hugo is a static site generator that converts markdown files (.md) to html pages. Let’s discover how we can build a blog using it, and discuss some configuration options that will make the experience better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 - "Install" Hugo
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Download the latest extended version for your OS from 
&lt;a href="https://github.com/gohugoio/hugo/releases" rel="noopener noreferrer"&gt;Hugo Releases&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Extract Hugo in a folder of your choice&lt;/li&gt;
&lt;li&gt;(&lt;em&gt;Optional&lt;/em&gt;) Add Hugo folder to path - &lt;strong&gt;if not&lt;/strong&gt;, then replace the &lt;code&gt;hugo&lt;/code&gt; command with the full path to Hugo, ie &lt;code&gt;"c:\program files\hugo\hugo"&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2 - Create a new blog
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Launch a new command prompt and navigate to the folder which will contain the new site.
&lt;/li&gt;
&lt;li&gt;Create a new hugo using &lt;code&gt;hugo new site my-blog&lt;/code&gt;. A folder named "my-blog" will be created.&lt;/li&gt;
&lt;li&gt;Download a new theme, preferably &lt;a href="https://github.com/halogenica/beautifulhugo/archive/master.zip" rel="noopener noreferrer"&gt;beatiful hugo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Extract and copy the new theme inside &lt;code&gt;my-blog/themes/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Rename theme folder from &lt;code&gt;beautifulhugo-master&lt;/code&gt; to &lt;code&gt;beautifulhugo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open the config file (&lt;code&gt;my-blog/config.toml&lt;/code&gt;) and edit the theme line &lt;code&gt;theme = "beatifulhugo"&lt;/code&gt; to select the new theme &lt;/li&gt;
&lt;li&gt;Create a new post &lt;code&gt;hugo new post/first-post.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run the deveopment server (&lt;code&gt;hugo serve -D -w&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Preview the &lt;a href="http://localhost:1313" rel="noopener noreferrer"&gt;new site on http://localhost:1313&lt;/a&gt; - unless port 1313 is already used, in which case the new port will be listed in the development server's output&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3 - Content Management
&lt;/h2&gt;

&lt;p&gt;Although there are a lot of ways to organize content in hugo, I prefer to place each post in a separate folder named &lt;code&gt;yyyy-mm-dd title&lt;/code&gt;, also I like to group these folders by year. Finally since each post lies in each own folder it should be named as &lt;code&gt;index.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So the full path for each post is &lt;code&gt;my-blog/content/post/yyyy/yyyy-mm-dd title/index.md&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-blog
│
├── cv
│   └── index.md
│
├── content
│   ├── post
│   │   ├── 2018
│   │   │   ├── 2018-03-15 Hugo Gallery Shortcode
│   │   │   │   ├── index.md
│   │   │   │   └── feature.png  
│   │   │   │   
│   │   │   └── 2018-09-11 Hugo 101 Static Site Generators
│   │   │       ├── index.md
│   │   │       └── feature.png  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That approach both keeps all post files together in a folder and isolates them from the rest posts allowing for easier editing as each time we have to handle only one folder containing exactly what we need.&lt;/p&gt;

&lt;p&gt;By default hugo will use the post title to create the url of each posts but this approach might create issues if we decide to change the title of the post or if we don't necessarily like to match the url with the post's title. Also since post images will be under the same url, changing post title will require changing all image urls. A better approach is to use "slugs" a slug is a custom url part that we assign to each post.&lt;/p&gt;

&lt;p&gt;First we need to instruct hugo to use the slugs so we edit &lt;code&gt;my-blog\confg.toml&lt;/code&gt; and add the following lines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[permalinks]
  post = "/:year/:slug/"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs hugo to create a url that consists of the year and the slug for each post.&lt;/p&gt;

&lt;p&gt;Now in order to set the slug for each post we need to understand what these index.md files contain.&lt;br&gt;
Each post file (&lt;code&gt;index.md&lt;/code&gt; in our case) contains the text of the post and some meta-data, called &lt;code&gt;frontmatter&lt;/code&gt;. The frontmatter must appear in the beggining of each file between &lt;code&gt;---&lt;/code&gt; and consists of &lt;em&gt;name&lt;/em&gt;: &lt;em&gt;value&lt;/em&gt; pairs. For example the frontmatter of this page is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
title: "Hugo 102: Setting Up"
date: 2019-03-15
tags: ["hugo"]
slug: "hugo-setup"
image: "2019/hugo-setup/featured.png"
---
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the year of the date and the slug are used to construct the url of each post.&lt;br&gt;
Note that the image field here will be used in the home page of the post and, thus, it's value must be relative to the root of blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - Content
&lt;/h2&gt;

&lt;p&gt;The actual content of each post starts after the front matter. Everything we write bellow the frontmatter will appear in the post's body. The content should be formatted in &lt;a href="https://daringfireball.net/projects/markdown/" rel="noopener noreferrer"&gt;&lt;em&gt;markdown&lt;/em&gt;&lt;/a&gt; but also regular html will work.&lt;/p&gt;

&lt;p&gt;As a very short intro to markdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prefixing any line with &lt;code&gt;#, ##, ###, ####, #####, ######&lt;/code&gt; will create headers&lt;/li&gt;
&lt;li&gt;enclosing any text in &lt;code&gt;* or **&lt;/code&gt; will render it using &lt;em&gt;italic&lt;/em&gt; or &lt;strong&gt;bold&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;prexiing any lines with &lt;code&gt;-&lt;/code&gt; will create lists&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5 - Generating
&lt;/h2&gt;

&lt;p&gt;By defaut hugo marks all new posts as drafts, before generating check that no post has a &lt;br&gt;
&lt;code&gt;draft: true&lt;/code&gt; field in it's frontmatter&lt;/p&gt;

&lt;p&gt;To generate the site use the &lt;code&gt;hugo&lt;/code&gt; command. It will create a folder named &lt;code&gt;public&lt;/code&gt; with all the content of the site. You can publish your site by uploading the contents of the &lt;code&gt;public&lt;/code&gt; to any host.&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="https://greekdeveloper.com/2018/static-site-generators/" rel="noopener noreferrer"&gt;Original post at greek developer&lt;/a&gt;
&lt;/h6&gt;

</description>
      <category>hugo</category>
    </item>
    <item>
      <title>Hugo #1 - Intro to Static Site Generators</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Tue, 11 Sep 2018 15:59:42 +0000</pubDate>
      <link>https://dev.to/sarmis/hugo-1---intro-to-static-site-generators-jp0</link>
      <guid>https://dev.to/sarmis/hugo-1---intro-to-static-site-generators-jp0</guid>
      <description>&lt;p&gt;A static site generator (SSG) is an application that converts simple text files (with minimal markup) to static sites using predifend templates (Themes). They provide a middle ground between fully fledged Content Managment Systems (like wordpress or drupal) and statically build sites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No server-side infrastructure except a simple http server (no database, no server-side application).&lt;/li&gt;
&lt;li&gt;Simple deployment procedure (just copy the generated files). &lt;/li&gt;
&lt;li&gt;Every deployment is a full deployment (no differences between local and remote site)&lt;/li&gt;
&lt;li&gt;Easily to track content and theme via source code versioning system&lt;/li&gt;
&lt;li&gt;Content consistency (every page looks the same due to template usage)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The application is required in order to alter content (ie. cannot publish frpm mobile)&lt;/li&gt;
&lt;li&gt;No advanced content management or authorization model&lt;/li&gt;
&lt;li&gt;There is a learning curve for doing more complex tasks (but this is true for every other option)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use case
&lt;/h3&gt;

&lt;p&gt;SSGs are a fitting option for personal or small teams, blog-style sites where any commenting is either disabled or off-loaded to another tool (ie facebook comments) - If the team member are familiar with text editing, markdown and git (like developers are) even better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operation &amp;amp; Setup
&lt;/h3&gt;

&lt;p&gt;The typical steps to setup an SSG are: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download SSG binaries or script&lt;/li&gt;
&lt;li&gt;Create a new folder (project)&lt;/li&gt;
&lt;li&gt;Download a theme&lt;/li&gt;
&lt;li&gt;Configure site parametes (title, url, theme, etc)&lt;/li&gt;
&lt;li&gt;Create content (simple ascii files, using markdown)&lt;/li&gt;
&lt;li&gt;Build static files&lt;/li&gt;
&lt;li&gt;Deploy static files&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Which one?
&lt;/h3&gt;

&lt;p&gt;There are multiple SSGs to select from, but the 3 most used are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; is the most used (partailly due to being used by github pages), but setting up RoR on a windows machine could be intimidating..&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; is build in "Go" but ships as a single executable and is a nice option&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexo.io/"&gt;Hexo&lt;/a&gt; is build in node.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My personal preference is Hugo&lt;/p&gt;

&lt;h6&gt;
  
  
  &lt;a href="https://greekdeveloper.com/2018/static-site-generators/"&gt;Origin post at greek developer&lt;/a&gt;
&lt;/h6&gt;

</description>
      <category>hugo</category>
      <category>staticsitegenerators</category>
    </item>
    <item>
      <title>Building a folder-based gallery for Hugo</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Thu, 30 Aug 2018 05:58:22 +0000</pubDate>
      <link>https://dev.to/sarmis/building-a-folder-based-gallery-for-hugo-4i8c</link>
      <guid>https://dev.to/sarmis/building-a-folder-based-gallery-for-hugo-4i8c</guid>
      <description>&lt;p&gt;A custom hugo shortcode that builds an image gallery from images inside a folder&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;Image Discovery &amp;amp; Gallery Layout&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="c"&gt;&amp;lt;!-- "site root"\layouts\shortcodes\foldergallery.html --&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.gallery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;flex-wrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.gallery&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;flex-grow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="nc"&gt;.gallery&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;            
            &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;object-fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;flex-grow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;/style&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.4.0/jquery.fancybox.min.css"&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;"gallery"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        {{ $path := print "content\\" .Page.Dir (.Get "src") }}
        {{ $url  := print .Page.URL (.Get "src") }}
        {{ range (readDir $path)  }}            
            {{ $src := print $url "/" .Name }}                
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;data-fancybox=&lt;/span&gt;&lt;span class="s"&gt;"gallery"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ $src }}"&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;src=&lt;/span&gt;&lt;span class="s"&gt;"{{ $src }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="nt"&gt;&amp;lt;br/&amp;gt;&lt;/span&gt;            
            &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        {{ end }}
    &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;Fancy Box&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="c"&gt;&amp;lt;!--
1. Replace theme's jquery with 3.3.1 version
2. Add the fancybox3 script
--&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;"https://code.jquery.com/jquery-3.3.1.min.js"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&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;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.4.0/jquery.fancybox.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  { {&amp;lt; foldergallery src="imgs" &amp;gt;} }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will create an image gallery with the images in the imgs folder&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample
&lt;/h2&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%2Flpq3yd03vnachxbdrbj0.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%2Flpq3yd03vnachxbdrbj0.jpg" width="746" height="813"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Originally posted at &lt;a href="https://greekdeveloper.com/post/2018/2018-08-29-hugo-gallery-shortcode" rel="noopener noreferrer"&gt;greek developer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>gallery</category>
    </item>
    <item>
      <title>Hidden Boards - "copy/paste via http" for non-sensitive text</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Thu, 23 Nov 2017 11:31:20 +0000</pubDate>
      <link>https://dev.to/sarmis/hidden-boards---copypaste-via-http-for-non-sensitive-text-c1i</link>
      <guid>https://dev.to/sarmis/hidden-boards---copypaste-via-http-for-non-sensitive-text-c1i</guid>
      <description>&lt;p&gt;Hi everyone, I just build &lt;a href="http://hiddenboards.com" rel="noopener noreferrer"&gt;http://hiddenboards.com&lt;/a&gt; It's intent is to enable transferring private (but not too sensitive) text from one pc to another, without installing anything or going through lengthy registration processes. (ie copy/paste a serial number to a new machine)&lt;/p&gt;

&lt;p&gt;You can create a board and the contents of the board are only available to those who have the (random) board ID (and me as the administrator). &lt;/p&gt;

&lt;p&gt;I will add a layer of (simple) encryption before posting the data to the firebase.&lt;/p&gt;

&lt;p&gt;I would appreciate any comments.&lt;/p&gt;

&lt;p&gt;The stack is firebase, vue.js &amp;amp; bulma.io (currently runs on the free tier so it should have a limit on about 100 simultaneous connections)&lt;/p&gt;

</description>
      <category>showdev</category>
    </item>
    <item>
      <title>My "RAD" stack for small projects</title>
      <dc:creator>Thomas Sarmis</dc:creator>
      <pubDate>Mon, 21 Aug 2017 11:29:56 +0000</pubDate>
      <link>https://dev.to/sarmis/my-rad-stack-for-small-projects</link>
      <guid>https://dev.to/sarmis/my-rad-stack-for-small-projects</guid>
      <description>&lt;p&gt;The last 10 years I work with Delphi &amp;amp; MS-SQL Server for building desktop apps and back-end systems in Windows. The last years I have considered some web-based projects but the distance from the tools I 'm familiar with to a decent stack for web apps is huge - sure I can build the back-end in Delphi but then deployment get's complicated, especially for small projects that do not worth setting app a complicated CI/CD tool chain. &lt;/p&gt;

&lt;p&gt;So I was in search for a more suitable stack, my requirements were more or less the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small set of new languages&lt;/li&gt;
&lt;li&gt;Easy setup of the development environment &lt;/li&gt;
&lt;li&gt;Deployment options.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After some consideration I decided to go with the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MS SQL Server&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;HTML &amp;amp; Javascript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;HTML &amp;amp; Javascript is a no-brainer, a website can target most of the devices that are used today.&lt;/p&gt;

&lt;p&gt;Node.js was also an easy choice, it uses javascript so I can leverage what ever I learn for the front-end, and is popular enough to be deployable in almost all cloud platforms as well as both in Apache &amp;amp; IIS and both under Windows and Linux. Also as it is a new, hot technology there are a lot of guides and has a very active community. Setting up the developing environment is also very simple.&lt;/p&gt;

&lt;p&gt;MS SQL Server is a rather strange choice but in my case it has some advantages, first of all it's a tool that I'm familiar with, I have it already available in my dev machines and I can re-use a lot of tooling that I have available. There also many options for deploying MS SQL Server (Managed in Azure, unmanaged in Google Cloud, Local installation in windows &amp;amp; linux)&lt;/p&gt;

&lt;p&gt;This stack allows me to develop and deploy either to my dev-machine or completely managed in azure. &lt;/p&gt;

&lt;p&gt;Sure some tools of this stack may not be optimal for large-scale web projects but the issue of this post is to describe a stack for small projects which may or may-not move forward to become large-scale enough...&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
