<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Daniel Mita</title>
    <description>The latest articles on Forem by Daniel Mita (@m_dango).</description>
    <link>https://forem.com/m_dango</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%2F241346%2F9fefdd10-acba-4878-91a4-34808a5d4eef.png</url>
      <title>Forem: Daniel Mita</title>
      <link>https://forem.com/m_dango</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/m_dango"/>
    <language>en</language>
    <item>
      <title>Building and Deploying a New API (Part 3)</title>
      <dc:creator>Daniel Mita</dc:creator>
      <pubDate>Fri, 10 Jan 2025 20:11:31 +0000</pubDate>
      <link>https://forem.com/m_dango/building-and-deploying-a-new-api-part-3-5blm</link>
      <guid>https://forem.com/m_dango/building-and-deploying-a-new-api-part-3-5blm</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/m_dango/building-and-deploying-a-new-api-part-2-5dm"&gt;Previous Post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Time to get into Kubernetes! I'm going to go through this process manually at first, in order to familiarize myself with some of the details, before I start involving tools such as Terraform and Helm.&lt;/p&gt;

&lt;p&gt;I've explored Caddy in the past, and I'm highly fond of its automatic HTTPS and straightforward syntax, so part of this setup is going to be configuring Caddy to be used as a reverse proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;demo-api.dango.space, localhost {
        respond / "Hello, World!"
        respond /health-check 204
        reverse_proxy localhost:8080
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will need to be available to our Caddy container. To do this we'll use the command &lt;code&gt;kubectl create configmap&lt;/code&gt;. The &lt;code&gt;Caddyfile&lt;/code&gt; can be used with the &lt;code&gt;from-file&lt;/code&gt; flag and we can use the &lt;code&gt;output&lt;/code&gt; flag to create our &lt;a href="https://github.com/m-dango/demo-api/blob/1c73051a293863e86bb4d51f400e30f83b2c3eba/manifests/configmap.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;configmap.yaml&lt;/code&gt;&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://github.com/m-dango/demo-api/blob/1c73051a293863e86bb4d51f400e30f83b2c3eba/manifests/deployment.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;deployment.yaml&lt;/code&gt;&lt;/a&gt; file, we'll be adding the Caddy container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;caddy&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;caddy:2.9-alpine&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
  &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;caddyfile-volume&lt;/span&gt;
      &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/caddy/Caddyfile&lt;/span&gt;
      &lt;span class="na"&gt;subPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Caddyfile&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;caddy-data&lt;/span&gt;
      &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;data&lt;/code&gt; directory needs to be persisted, so a &lt;a href="https://github.com/m-dango/demo-api/blob/1c73051a293863e86bb4d51f400e30f83b2c3eba/manifests/pv.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;PersistentVolume&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/m-dango/demo-api/blob/1c73051a293863e86bb4d51f400e30f83b2c3eba/manifests/pv.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;PersistentVolumeClaim&lt;/code&gt;&lt;/a&gt; have been created to handle this.&lt;/p&gt;

&lt;p&gt;In a later post I'll be taking a look at moving this over to an ingress controller.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;demo-api&lt;/code&gt; image has been added to the &lt;a href="https://github.com/m-dango/demo-api/blob/1c73051a293863e86bb4d51f400e30f83b2c3eba/manifests/deployment.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;deployment.yaml&lt;/code&gt;&lt;/a&gt;, and the ports opened up in our &lt;a href="https://github.com/m-dango/demo-api/blob/1c73051a293863e86bb4d51f400e30f83b2c3eba/manifests/deployment.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;service.yaml&lt;/code&gt;&lt;/a&gt;. We have a cluster created on Digital Ocean, so we'll run &lt;code&gt;kubectl apply&lt;/code&gt; to provision our resources. We'll obtain our external IP address with &lt;code&gt;kubectl get services&lt;/code&gt;, add a new record to our domain, and we should be good to go!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://demo-api.dango.space/" rel="noopener noreferrer"&gt;https://demo-api.dango.space/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The state of the repository as of this post can be found &lt;a href="https://github.com/m-dango/demo-api/blob/1c73051a293863e86bb4d51f400e30f83b2c3eba" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A link to the next part will be available once it is written.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>caddy</category>
      <category>dns</category>
      <category>ssl</category>
    </item>
    <item>
      <title>Building and Deploying a New API (Part 2)</title>
      <dc:creator>Daniel Mita</dc:creator>
      <pubDate>Thu, 09 Jan 2025 15:06:43 +0000</pubDate>
      <link>https://forem.com/m_dango/building-and-deploying-a-new-api-part-2-5dm</link>
      <guid>https://forem.com/m_dango/building-and-deploying-a-new-api-part-2-5dm</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/m_dango/building-and-deploying-a-new-api-part-1-6ck"&gt;Previous Post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the server code has been written, we move on to creating and building the &lt;a href="https://github.com/m-dango/demo-api/blob/d35302d1e357c4120f099c85a9e48debbbc43189/api/Dockerfile" rel="noopener noreferrer"&gt;Docker image&lt;/a&gt;. There's nothing particularly special about it yet, it simply runs &lt;code&gt;go build&lt;/code&gt; to create the binary, and copies it over to a scratch image (simple is good)!&lt;/p&gt;

&lt;p&gt;Repository tooling comes next. The first being the workflow to build and push our Docker image. This is another fairly straightforward step, following along with  &lt;a href="https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#publishing-a-package-using-an-action" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt; along with some minor tweaks.&lt;/p&gt;

&lt;p&gt;Another tool we'll be including is Codecov. The &lt;a href="https://github.com/m-dango/demo-api/blob/72f5dfc640f273a25045eb4e33e908121d49ec1f/.github/workflows/tests.yaml" rel="noopener noreferrer"&gt;workflow&lt;/a&gt; is set up to run all the tests, generate a code coverage file, and use an API key to push the results over to Codecov. We also have a &lt;a href="https://github.com/m-dango/demo-api/blob/72f5dfc640f273a25045eb4e33e908121d49ec1f/codecov.yaml" rel="noopener noreferrer"&gt;YAML file&lt;/a&gt; with some settings to exclude the generated code from the results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2kru4hgkh58trkra86h.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%2Fc2kru4hgkh58trkra86h.png" alt="Codecov comment" width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, to check for repository updates, we'll also set up &lt;a href="https://github.com/m-dango/demo-api/blob/346add43eb254b3ea683e4a5f50fc70722c84f53/.github/dependabot.yaml" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt; to look at our GitHub Actions and Docker files.&lt;/p&gt;

&lt;p&gt;Nothing particularly complicated for this post, but all of these tools have their uses, and I definitely believe they're worth trying at the very least!&lt;/p&gt;

&lt;p&gt;The state of the repository as of this post can be found &lt;a href="https://github.com/m-dango/demo-api/tree/346add43eb254b3ea683e4a5f50fc70722c84f53" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Part 3 is now live: &lt;a href="https://dev.to/m_dango/building-and-deploying-a-new-api-part-3-5blm"&gt;Next Post&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>codecov</category>
      <category>testing</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Building and Deploying a New API (Part 1)</title>
      <dc:creator>Daniel Mita</dc:creator>
      <pubDate>Tue, 07 Jan 2025 19:54:05 +0000</pubDate>
      <link>https://forem.com/m_dango/building-and-deploying-a-new-api-part-1-6ck</link>
      <guid>https://forem.com/m_dango/building-and-deploying-a-new-api-part-1-6ck</guid>
      <description>&lt;p&gt;I like to continuously refine my skills and knowledge as a software engineer, dipping my toes into the huge variety of software solutions available, and I've decided to put something together making use of the following tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAPI, for the specification.&lt;/li&gt;
&lt;li&gt;oapi-codegen, to generate a server based off the specification.&lt;/li&gt;
&lt;li&gt;Testify, for straightforward test assertions.&lt;/li&gt;
&lt;li&gt;Codecov.&lt;/li&gt;
&lt;li&gt;Docker.&lt;/li&gt;
&lt;li&gt;PostgreSQL.&lt;/li&gt;
&lt;li&gt;GORM (mostly because I make seldom use of ORMs and I'm curious).&lt;/li&gt;
&lt;li&gt;Flyway, for version controlled database migrations.&lt;/li&gt;
&lt;li&gt;Kubernetes.&lt;/li&gt;
&lt;li&gt;Terraform.&lt;/li&gt;
&lt;li&gt;Caddy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The API Spec
&lt;/h2&gt;

&lt;p&gt;I'm starting off simple, creating an OpenAPI specification for a user. The user will initially have a name, an email address, and an ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;123&lt;/span&gt;
          &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;me@example.com&lt;/span&gt;
          &lt;span class="na"&gt;writeOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alice&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This schema is used for a &lt;code&gt;POST&lt;/code&gt; to &lt;code&gt;/users&lt;/code&gt; with a &lt;code&gt;201&lt;/code&gt; response, and a &lt;a href="https://datatracker.ietf.org/doc/html/rfc7807" rel="noopener noreferrer"&gt;Problem Details JSON Object&lt;/a&gt; is used for a &lt;code&gt;400&lt;/code&gt; response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating The Code
&lt;/h2&gt;

&lt;p&gt;Moving on to &lt;a href="https://github.com/oapi-codegen/oapi-codegen" rel="noopener noreferrer"&gt;oapi-codegen&lt;/a&gt;, a &lt;code&gt;tools.go&lt;/code&gt; file has been created as exemplified in its repository. I have also created the file &lt;a href="https://github.com/m-dango/demo-api/blob/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21/internal/handlers/server.go" rel="noopener noreferrer"&gt;&lt;code&gt;internal/handlers/server.go&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=server.config.yaml ../../openapi.yaml&lt;/span&gt;
&lt;span class="c"&gt;//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=types.config.yaml ../../openapi.yaml&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;handlers&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/m-dango/demo-api/blob/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21/internal/handlers/server.config.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;server.config.yaml&lt;/code&gt;&lt;/a&gt; is configured for a strict &lt;code&gt;net/http&lt;/code&gt; server, and &lt;a href="https://github.com/m-dango/demo-api/blob/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21/internal/handlers/types.config.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;types.config.yaml&lt;/code&gt;&lt;/a&gt; for the models. A &lt;code&gt;go mod init&lt;/code&gt;, followed by a &lt;code&gt;go mod tidy&lt;/code&gt; and &lt;code&gt;go generate ./...&lt;/code&gt; produces most of what we need to get the new API up and running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing and Testing The Handler
&lt;/h2&gt;

&lt;p&gt;The next steps are adding the following to &lt;code&gt;server.go&lt;/code&gt;, creating a server struct which abides by the generated strict server interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;StrictServerInterface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Server&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;Subsequently, a &lt;a href="https://github.com/m-dango/demo-api/blob/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21/internal/handlers/user.go" rel="noopener noreferrer"&gt;&lt;code&gt;user.go&lt;/code&gt;&lt;/a&gt; file is created, where a handler for the POST request is created to match the generated interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;PostUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="n"&gt;PostUserRequestObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PostUserResponseObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;nil&lt;/code&gt; is returned initially, as we're creating &lt;a href="https://github.com/m-dango/demo-api/blob/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21/internal/handlers/user_test.go" rel="noopener noreferrer"&gt;&lt;code&gt;user_test.go&lt;/code&gt;&lt;/a&gt; and using &lt;a href="https://github.com/stretchr/testify" rel="noopener noreferrer"&gt;Testify&lt;/a&gt; to create tests as we shape the behavior of &lt;code&gt;PostUser&lt;/code&gt;. We don't have a DB wired up yet, so the responses will simply be hard-coded structs of what we expect the API to respond with for now to pass the tests.&lt;/p&gt;

&lt;p&gt;I'm going to continue hacking away as I write my next blog post. The next steps will likely be building the &lt;code&gt;main.go&lt;/code&gt; and &lt;code&gt;Dockerfile&lt;/code&gt; to fire up the server, and hooking up Codecov to the repository. The repository can be found here if you would like more context to the snippets given in this post: &lt;a href="https://github.com/m-dango/demo-api/tree/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21" rel="noopener noreferrer"&gt;https://github.com/m-dango/demo-api/tree/1c86f975fc451e50c2bedbfeb6bc4317e6c7be21&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Part 2 is now live: &lt;a href="https://dev.to/m_dango/building-and-deploying-a-new-api-part-2-5dm"&gt;Next Post&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>go</category>
    </item>
    <item>
      <title>Practicing Raku Grammars On Exercism</title>
      <dc:creator>Daniel Mita</dc:creator>
      <pubDate>Thu, 29 Feb 2024 23:20:25 +0000</pubDate>
      <link>https://forem.com/m_dango/practicing-raku-grammars-on-exercism-36i4</link>
      <guid>https://forem.com/m_dango/practicing-raku-grammars-on-exercism-36i4</guid>
      <description>&lt;p&gt;Grammars are a powerful tool in Raku for pattern matching and transformation. This post will cover several exercises from &lt;a href="https://exercism.org/" rel="noopener noreferrer"&gt;https://exercism.org/&lt;/a&gt; which are great for experimenting with this functionality.&lt;/p&gt;

&lt;p&gt;This post is a breakdown of my own use of grammars. If you would like to learn more about them, there is an introduction post from &lt;a class="mentioned-user" href="https://dev.to/jj"&gt;@jj&lt;/a&gt; which can be found here: &lt;a href="https://dev.to/jj/introduction-to-grammars-with-perl6-75e"&gt;https://dev.to/jj/introduction-to-grammars-with-perl6-75e&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phone Number
&lt;/h2&gt;

&lt;p&gt;Check and return a valid phone number with non-digit characters stripped:&lt;br&gt;
&lt;a href="https://exercism.org/tracks/raku/exercises/phone-number" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/phone-number&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This exercise uses the North American Numbering Plan (NANP) as the phone number format, a ten-digit telephone number in the form of NPA-NXX-XXXX, where N represents a digit 2 through 9, and X represents a digit 0 through 9.&lt;/p&gt;

&lt;p&gt;Let's start off with checks for N and X.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token X { &amp;lt;[0..9]&amp;gt; }
token N { &amp;lt;+X - [01]&amp;gt; &amp;lt;!before 11&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;token X&lt;/code&gt; is the simplest here, merely being digits 0-9. &lt;code&gt;token N&lt;/code&gt; is &lt;code&gt;X&lt;/code&gt; with 0 and 1 removed, and an additional check has been added to ensure that this digit can't come before two sequential 1s (known as an N11 code e.g. 911).&lt;/p&gt;

&lt;p&gt;The second and third portions of the number (NXX and XXXX) are known as exchange and station codes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token exchange-code { &amp;lt;.N&amp;gt; &amp;lt;.X&amp;gt; ** 2 }
token station-code  { &amp;lt;.X&amp;gt; ** 4 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NPA (AKA the area code) has some additional rules in the real world. This is not relevant for this exercise so let's just copy &lt;code&gt;exchange-code&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;token area-code { &amp;lt;.exchange-code&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that all the needed parts of the number are defined, let's create a rule called &lt;code&gt;TOP&lt;/code&gt; to bring it all together, with an extra part to check for a country code (a 1, with an optional leading plus sign).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rule TOP { ['+'? 1]? &amp;lt;area-code&amp;gt; &amp;lt;exchange-code&amp;gt; &amp;lt;station-code&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;rule&lt;/code&gt; and a &lt;code&gt;token&lt;/code&gt; differ in how they handle whitespace. See more here: &lt;a href="https://docs.raku.org/language/grammars#ws" rel="noopener noreferrer"&gt;https://docs.raku.org/language/grammars#ws&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, let's alter what is considered to be whitespace. I'll be taking the lazy approach by matching anything that isn't 0-9.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token ws { &amp;lt;-X&amp;gt;* }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All together this looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grammar NANP {
    rule TOP { ['+'? 1]? &amp;lt;area-code&amp;gt; &amp;lt;exchange-code&amp;gt; &amp;lt;station-code&amp;gt; }

    token area-code     { &amp;lt;.exchange-code&amp;gt; }
    token exchange-code { &amp;lt;.N&amp;gt; &amp;lt;.X&amp;gt; ** 2 }
    token station-code  { &amp;lt;.X&amp;gt; ** 4 }

    token N { &amp;lt;+X - [01]&amp;gt; &amp;lt;!before 11&amp;gt; }
    token X { &amp;lt;[0..9]&amp;gt; }

    token ws { &amp;lt;-X&amp;gt;* }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important parts needed to complete this exercise will be named in a &lt;code&gt;Match&lt;/code&gt; object. Let's now write a class which will be used to transform a &lt;code&gt;Match&lt;/code&gt; into the desired format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Cleaner {
    method TOP ($/) {
        make [~] $&amp;lt;area-code exchange-code station-code&amp;gt;;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;TOP&lt;/code&gt; method (which will operate on the &lt;code&gt;TOP&lt;/code&gt; match) will take a &lt;code&gt;Match&lt;/code&gt; object, and concatenate the &lt;code&gt;area-code&lt;/code&gt;, &lt;code&gt;exchange-code&lt;/code&gt;, and &lt;code&gt;station-code&lt;/code&gt; portions of that match into a string.  The &lt;code&gt;make&lt;/code&gt; routine will attach any given payload (the string in this case) to the &lt;code&gt;Match&lt;/code&gt; object, which can be retrieved with the &lt;code&gt;made&lt;/code&gt; routine.&lt;/p&gt;

&lt;p&gt;The following example will return &lt;code&gt;9876543210&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;NANP.parse('+1 (987) 654-3210', :actions(Cleaner)).made;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My published solution to this exercise can be found here: &lt;a href="https://exercism.org/tracks/raku/exercises/phone-number/solutions/m-dango" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/phone-number/solutions/m-dango&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ISBN Verifier
&lt;/h2&gt;

&lt;p&gt;Identify whether given data is a valid ISBN-10:&lt;br&gt;
&lt;a href="https://exercism.org/tracks/raku/exercises/isbn-verifier" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/isbn-verifier&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This exercise uses a simpler grammar than the previous. A set of 9 digits, and a 10th digit or X, separated by dashes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grammar ISBN {
    rule TOP    { &amp;lt;digit&amp;gt; ** 9 [ &amp;lt;digit&amp;gt; | X ] }
    token digit { &amp;lt;[0..9]&amp;gt; }
    token ws    { '-'? }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The class being used for actions however is a bit more involved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Validator {
    method TOP ($/) {
        make ( (|$&amp;lt;digit&amp;gt;, 10) Z* (10...1) ).sum %% 11;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here all the matched digits are multiplied using the zip metaoperator, i.e., the 1st digit is multiplied by 10, the 2nd multiplied by 9, etc. If there were 10 digits in the match, the subsequent 10 (which is there to substitute an &lt;code&gt;X&lt;/code&gt; from the &lt;code&gt;Match&lt;/code&gt;) is ignored. The result of this zip is then added up by the &lt;code&gt;sum&lt;/code&gt; routine, and then that result is checked for divisibility by 11. The final payload will be a &lt;code&gt;Bool&lt;/code&gt; for this check.&lt;/p&gt;

&lt;p&gt;My published solution to this exercise can be found here: &lt;a href="https://exercism.org/tracks/raku/exercises/isbn-verifier/solutions/m-dango" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/isbn-verifier/solutions/m-dango&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wordy
&lt;/h2&gt;

&lt;p&gt;Parse and solve a written mathematical problem:&lt;br&gt;
&lt;a href="https://exercism.org/tracks/raku/exercises/wordy" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/wordy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had a lot of fun with this one! A mathematical problem is given in the form of a question in English, and a numeric solution is expected as a result. The operations are expected to be resolved from left to right.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;What is 3 plus 2 multiplied by 3?&lt;/code&gt; &lt;code&gt;=&lt;/code&gt; &lt;code&gt;15&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First, let's take the expected operations, and associate them with the appropriate functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constant %OPS = 
    'plus'          =&amp;gt; &amp;amp;infix:&amp;lt;+&amp;gt;,
    'minus'         =&amp;gt; &amp;amp;infix:&amp;lt;-&amp;gt;,
    'multiplied by' =&amp;gt; &amp;amp;infix:&amp;lt;×&amp;gt;,
    'divided by'    =&amp;gt; &amp;amp;infix:&amp;lt;÷&amp;gt;,
;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in the grammar, let's use the keys from this hash inside a token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token op { @(%OPS.keys) }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every number in the string will be a positive or negative integer, so let's use something simple to match those.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token number { '-'? &amp;lt;[0..9]&amp;gt;+ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now let's pair these up to create a function with them later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rule func { &amp;lt;op&amp;gt; &amp;lt;number&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the corresponding action, let's now create some methods to build functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;method func ($/) {
    make -&amp;gt; $x { $&amp;lt;op&amp;gt;.made.($x, $&amp;lt;number&amp;gt;) };
}

method op ($/) {
    make %OPS{$/};
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;plus 2&lt;/code&gt; were part of the given text, what would happen is the &lt;code&gt;op&lt;/code&gt; method would fetch the &lt;code&gt;&amp;amp;infix:&amp;lt;+&amp;gt;&lt;/code&gt; routine from the &lt;code&gt;%OPS&lt;/code&gt; hash, and the &lt;code&gt;func&lt;/code&gt; method would create a new function: &lt;code&gt;-&amp;gt; $x { &amp;amp;infix:&amp;lt;+&amp;gt;($x, 2) }&lt;/code&gt;, or more simply &lt;code&gt;-&amp;gt; $x { $x + 2 }&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's now put together the final TOP matcher and method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rule TOP { What is &amp;lt;number&amp;gt; &amp;lt;func&amp;gt;* '?' }

method TOP ($/) {
    make $&amp;lt;number&amp;gt;.&amp;amp;( [R∘] $&amp;lt;func&amp;gt;.map(*.made) );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$&amp;lt;func&amp;gt;&lt;/code&gt; will be an array which can contain 0 or more matches. The &lt;code&gt;map&lt;/code&gt; will retrieve each function created by the &lt;code&gt;func&lt;/code&gt; method, and these functions are then &lt;a href="https://docs.raku.org/type/List#routine_reduce" rel="noopener noreferrer"&gt;reduced&lt;/a&gt; using the &lt;a href="https://docs.raku.org/language/operators#infix_o,_infix_%E2%88%98" rel="noopener noreferrer"&gt;function composition&lt;/a&gt; operator, ultimately creating a single function to call with the first number from the match. The function composition operator usually has the left function called with the result of the right function, so the &lt;code&gt;R&lt;/code&gt; metaoperator is used to reverse this.&lt;/p&gt;

&lt;p&gt;As an example, the phrase &lt;code&gt;What is 1 plus 2 multiplied by 3?&lt;/code&gt; is transformed into the equivalent of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1
==&amp;gt; -&amp;gt; $x { $x + 2 }()
==&amp;gt; -&amp;gt; $x { $x * 3 }();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My published solution to this exercise can be found here: &lt;a href="https://exercism.org/tracks/raku/exercises/wordy/solutions/m-dango" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/wordy/solutions/m-dango&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Meetup
&lt;/h2&gt;

&lt;p&gt;Determine a date from a written description:&lt;br&gt;
&lt;a href="https://exercism.org/tracks/raku/exercises/meetup" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/meetup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This grammar is also a straightforward one, intended to match a description such as &lt;code&gt;Second Friday of December 2013&lt;/code&gt;. Month, Weekday and Week are all set up as enums. 1 to 12 for Jan to Dec, 1 to 7 for Mon to Sun, and Week being the first possible day of each week expected from the description.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enum Week (
    |(&amp;lt;First Second Third Fourth&amp;gt; Z=&amp;gt; (1, 8 ... *)),
    Teenth =&amp;gt; 13,
);

grammar Description {
    rule TOP { &amp;lt;week&amp;gt; &amp;lt;weekday&amp;gt; of &amp;lt;month&amp;gt; &amp;lt;year&amp;gt; }

    token week    { @(Week.keys) | Last }
    token weekday { @(Weekday.keys) }
    token month   { @(Month.keys) }
    token year    { &amp;lt;[0..9]&amp;gt;+ }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Last&lt;/code&gt; is a special case so a specific value has not been assigned to it.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;TOP&lt;/code&gt; method in the action class then has a few steps.&lt;/p&gt;

&lt;p&gt;First, a date object is created for the wanted week.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my Date $week.=new(
    year  =&amp;gt; $&amp;lt;year&amp;gt;,
    month =&amp;gt; ::($&amp;lt;month&amp;gt;),
    |(day =&amp;gt; ::($&amp;lt;week&amp;gt;) if $&amp;lt;week&amp;gt; ne 'Last'),
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A day is not specified if the last week is wanted. Instead, a condition is used to adjust this date to the beginning of the final week.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if $&amp;lt;week&amp;gt; eq 'Last' {
    $week.=later( (months =&amp;gt; 1, weeks =&amp;gt; -1) );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with the date now being at the start of the given week, let's adjust it to match the wanted day of the week.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make .later(days =&amp;gt; (::($&amp;lt;weekday&amp;gt;) - .day-of-week) % Weekday.keys) given $week;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the start of the week is a Friday (5) and the desired day is a Tuesday (2), the date will be advanced by &lt;code&gt;(2 - 5) % 7 = 4&lt;/code&gt; days.&lt;/p&gt;

&lt;p&gt;My published solution to this exercise can be found here: &lt;a href="https://exercism.org/tracks/raku/exercises/meetup/solutions/m-dango" rel="noopener noreferrer"&gt;https://exercism.org/tracks/raku/exercises/meetup/solutions/m-dango&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you've enjoyed these examples of grammars, and I hope to see and experiment with more applications of them in future!&lt;/p&gt;

</description>
      <category>raku</category>
      <category>grammar</category>
      <category>exercism</category>
      <category>regex</category>
    </item>
    <item>
      <title>Raku on asdf</title>
      <dc:creator>Daniel Mita</dc:creator>
      <pubDate>Fri, 23 Dec 2022 03:35:43 +0000</pubDate>
      <link>https://forem.com/m_dango/raku-on-asdf-l19</link>
      <guid>https://forem.com/m_dango/raku-on-asdf-l19</guid>
      <description>&lt;p&gt;I've created a plugin for installing Raku using the asdf version manager 🎉&lt;/p&gt;

&lt;p&gt;Check it out here: &lt;a href="https://github.com/m-dango/asdf-raku" rel="noopener noreferrer"&gt;https://github.com/m-dango/asdf-raku&lt;/a&gt;&lt;/p&gt;

</description>
      <category>asdf</category>
      <category>languages</category>
      <category>opensource</category>
      <category>raku</category>
    </item>
  </channel>
</rss>
