<?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: peetss</title>
    <description>The latest articles on DEV Community by peetss (@peetss).</description>
    <link>https://dev.to/peetss</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%2F571820%2F3d324c7b-9069-4ecd-8779-31872450cc59.jpeg</url>
      <title>DEV Community: peetss</title>
      <link>https://dev.to/peetss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/peetss"/>
    <language>en</language>
    <item>
      <title>Postgres locally and in CI.</title>
      <dc:creator>peetss</dc:creator>
      <pubDate>Mon, 18 Dec 2023 20:40:55 +0000</pubDate>
      <link>https://dev.to/peetss/postgres-locally-and-in-ci-4l2b</link>
      <guid>https://dev.to/peetss/postgres-locally-and-in-ci-4l2b</guid>
      <description>&lt;p&gt;It all started with a little nodejs library called &lt;a href="https://github.com/oguimbal/pg-mem" rel="noopener noreferrer"&gt;pg-mem&lt;/a&gt;.  It promised an in-memory postgres database that would be a great lightweight option for testing in CI. It was advertised as experimental but figured it would be worth it just for its sheer convenience.&lt;/p&gt;

&lt;p&gt;Getting it up and running was easy but I did run into two small issues, certainly not deal breakers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Some queries return a slightly incorrect result (primary key of joined data a bit &lt;a href="https://github.com/oguimbal/pg-mem/issues/339" rel="noopener noreferrer"&gt;mangled&lt;/a&gt; when using &lt;code&gt;GROUP BY&lt;/code&gt; clauses).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The entire suite of functions available in Postgres are not implemented, so depending on your queries you may have to implement them on your own (ie: &lt;code&gt;jsonb_build_object(...)&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A quick pop onto their Github page revealed at least a few pages of unresolved &lt;a href="https://github.com/oguimbal/pg-mem/issues" rel="noopener noreferrer"&gt;issues&lt;/a&gt;.  In any case, tests were running smoothly both locally and in CI so I was happy.&lt;/p&gt;

&lt;p&gt;Sadly, as queries became more complex, &lt;code&gt;pg-mem&lt;/code&gt; became less able to cope and over time it became clear that we had... Outgrown each other.  To be clear, this isn't a knock on &lt;code&gt;pg-mem&lt;/code&gt;, not even a bit.  In fact, the opposite, it's a great library I even attempted to fix the bugs I encountered. Finding enough time to work for free is just the reality of open source development. I have no doubt that with more resources it could truly realize its potential.  The allure of an in-memory Postgres database for testing remains strong but I needed proper query execution and so along came &lt;code&gt;Docker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Docker&lt;/code&gt; is an easy technology to work with.  It's well-documented, ubiquitous, and there are libraries for it in almost every programming language you can imagine.  The only real stumbling block was coming to the realization that a volume needs to be mounted at the absolute path on a Windows system.  Once ChatGPT helped me with that, we had one running database in a Docker container.  I refactored the tests that relied on weird &lt;code&gt;pg-mem&lt;/code&gt; results and we were successfully back to square one. Integrating the nodejs library &lt;a href="https://github.com/apocas/dockerode" rel="noopener noreferrer"&gt;dockerode&lt;/a&gt; to start and stop the container using Jest's global setup/teardown functions further automated the solution.&lt;/p&gt;

&lt;p&gt;One weird issue worth mentioning is that sometimes when Jest would exit it would leave a nasty error in my terminal and the Docker container wouldn't spin down.  After some research, the issue turned out to be a result of not releasing all the client connections to the database pool.  Ensuring &lt;code&gt;client.release()&lt;/code&gt; occurs during the &lt;code&gt;finally&lt;/code&gt; block of a try in &lt;code&gt;client.query(...)&lt;/code&gt; resolved the issue completely.&lt;/p&gt;

&lt;p&gt;At this point I was fairly impressed by just how well this worked, thinking little about how CI would dash all my hopes and dreams.  After naively thinking &lt;code&gt;dockerode&lt;/code&gt; would just work in Gitlab CI, I had to go completely back to the drawing board. After doing some research, Postgres turned out to be pretty simple to setup within the &lt;a href="https://docs.gitlab.com/ee/ci/services/postgres.html" rel="noopener noreferrer"&gt;gitlab-ci.yaml&lt;/a&gt; file.  Unfortunately, it didn't support seeding the database with a schema in any capacity.  Apparently, I'm not the only person to &lt;a href="https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3210" rel="noopener noreferrer"&gt;bemoan&lt;/a&gt; this lack of functionality.  My local solution mounted my schema file at &lt;code&gt;dockerinit.d&lt;/code&gt;.  After some thought I decided to refactor the logic in the &lt;code&gt;globalSetup&lt;/code&gt; Jest function to simply start the container, using the &lt;code&gt;pg&lt;/code&gt; library directly to insert the schema in the &lt;code&gt;beforeAll&lt;/code&gt; method of my test suite.  Importantly, the code to start the container would only run if the &lt;code&gt;CI&lt;/code&gt; env var injected by Gitlab was not present.&lt;/p&gt;

&lt;p&gt;After separating container logic from SQL inserts, we finally had our golden calf.  Tests ran both locally and in CI.  It wasn't particularly easy to accomplish and documentation on this entire effort was oddly lacking.  Hopefully, this post will make it easier for others to accomplish similar outcomes on the future.&lt;/p&gt;

&lt;p&gt;Cheers 🍻!&lt;/p&gt;

</description>
      <category>jest</category>
      <category>postgres</category>
      <category>node</category>
      <category>gitlab</category>
    </item>
    <item>
      <title>AWS Lambda Custom Authorizer</title>
      <dc:creator>peetss</dc:creator>
      <pubDate>Tue, 20 Jun 2023 03:42:40 +0000</pubDate>
      <link>https://dev.to/peetss/aws-lambda-custom-authorizer-4e2n</link>
      <guid>https://dev.to/peetss/aws-lambda-custom-authorizer-4e2n</guid>
      <description>&lt;p&gt;If you are like me you probably struggled to get custom authorizers working in Lambda. It probably isn't because you are too dumb to understand, it is because of magic. Magic is great if everything works as expected.  When it doesn't, though, it makes debugging an absolute nightmare.&lt;/p&gt;

&lt;p&gt;There are four things you absolutely &lt;strong&gt;need&lt;/strong&gt; to get right when working with custom authorizers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Return the correct policy document.&lt;/p&gt;

&lt;p&gt;This one was probably the biggest oversight on my part. In order for your custom authorizer to essentially forward the request to your Lambda it needs to return a valid policy document.  In short, the policy document returned by the authorizer function is evaluated by API Gateway to determine whether the client is authorized to access the requested resource.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html"&gt;example&lt;/a&gt; in the AWS docs works well and you can simplify it as needed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Callbacks vs async/await&lt;/p&gt;

&lt;p&gt;Many of the custom authorizer examples (even more recent ones) were written with callbacks in mind.  For example, you'll see most custom authorizer functions defined with the &lt;code&gt;callback&lt;/code&gt; argument and then a response starting like &lt;code&gt;callback(null...)&lt;/code&gt;, ironically indicating success.  Ok.  Searching about how to convert this to using async/await was oddly time-consuming, especially considering I thought this would be a problem many people would've run into.&lt;/p&gt;

&lt;p&gt;Eventually I found what I was looking for, courtesy of &lt;a href="https://stackoverflow.com/questions/55106223/lambda-authorizer-response-using-async-await-in-node-js/60280752"&gt;stackoverflow&lt;/a&gt;.  The answer was comically simple, just return the policy document object or throw an error.&lt;/p&gt;

&lt;p&gt;Great, no more callbacks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;throw new Error("Unauthorized")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Think you can just return whatever error you want based on your specific domain model?  So naïve.  AWS is very specific in the errors you can return but to their credit it is &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html"&gt;documented&lt;/a&gt;.  Again, this was another area where I spent more time than I care to admit agonizing about why my Lambda wasn't returning the error code I expected.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CORS&lt;/p&gt;

&lt;p&gt;Of course, a "gotchas" list like this wouldn't be complete without an entire section on CORS.  Once I solved 1, 2, and 3, I assumed I'd be off to the races only to be presented with a CORS error (using the one and only &lt;a href="https://swagger.io/tools/swagger-ui/"&gt;Swagger UI&lt;/a&gt; to interact with my API like a true developer).&lt;/p&gt;

&lt;p&gt;Traditional Lambdas allow you to arbitrarily return payloads (including headers) but with custom authorizers this is not possible.  Using &lt;a href="https://www.serverless.com/"&gt;Serverless&lt;/a&gt; (an incredible tool, btw), I learned you need to configure your API Gateway to respond with the proper CORS headers in certain situations, specifically 4xx and 5xx responses from a custom authorizer.  This &lt;a href="https://www.serverless.com/blog/cors-api-gateway-survival-guide/#cors-with-custom-authorizers"&gt;guide&lt;/a&gt; was pivotal to me breaking through the final barrier to a working solution.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, each of these problems on their own don't sound so significant but when you encounter all of them at once and you don't know what you are doing to start with, it can feel like climbing a mountain.  Granted, I probably could've read the documentation more closely but there were a lot of disjointed examples out there that only covered one or two of the four potential pitfalls I ran into.&lt;/p&gt;

&lt;p&gt;After all is said and done I am now the proud owner of a robust authorizer middleware that I can easily add to any of my existing Lambda functions and I was able to share my experiences with everyone here.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>node</category>
      <category>async</category>
    </item>
    <item>
      <title>That joyous moment when you get your first 200 OK building a new app 😌</title>
      <dc:creator>peetss</dc:creator>
      <pubDate>Tue, 13 Jun 2023 04:32:39 +0000</pubDate>
      <link>https://dev.to/peetss/that-joyous-moment-when-you-get-your-first-200-ok-building-a-new-app-ikc</link>
      <guid>https://dev.to/peetss/that-joyous-moment-when-you-get-your-first-200-ok-building-a-new-app-ikc</guid>
      <description></description>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>To VPC or not to VPC...</title>
      <dc:creator>peetss</dc:creator>
      <pubDate>Thu, 03 Nov 2022 00:22:34 +0000</pubDate>
      <link>https://dev.to/peetss/to-vpc-or-not-to-vpc-19cc</link>
      <guid>https://dev.to/peetss/to-vpc-or-not-to-vpc-19cc</guid>
      <description>&lt;p&gt;If you are building on AWS you've probably run into the situation where you have a Lambda that needs to talk to a database.  Easy right... well, it actually requires some non-trivial knowledge of concepts like VPCs, subnets, and security groups.  Probably more than what you initially bargained for.&lt;/p&gt;

&lt;p&gt;Of course, you probably made it through this (there are a plethora of tutorials solving this exact thing) and also realized the importance of restricting access to your database by placing it within the confines of a VPC.&lt;/p&gt;

&lt;p&gt;Then your use case changed and now you need to make a network request from a public source to your Lambda.  Problem is, the Lambda is inside a VPC which is not accessible to the public internet.&lt;/p&gt;

&lt;p&gt;After days spent scouring the internet for solutions to this problem, here is what I found.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Get rid of the VPC, who needs 'em anyway? Seriously though... don't do this.  The most valuable asset to any application is its data.  If you make your database public by taking it out of the VPC, the only thing separating your data from a hacker is an alphanumeric username and password combination.  Sure, you could have a &lt;strong&gt;strong&lt;/strong&gt; password but you have to really be comfortable with that risk.  Most people aren't.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even better, just use a NAT gateway.  A NAT gateway allows a Lambda in a VPC to be assigned a publicly addressable IP address.  Once you make it past the finicky nature of setting up this configuration, it works really well.  The Lambda is publicly accessible, can query the database and return a response to the user, just like you drew it up.  One caveat though, there is no free tier.  To this point, you're likely still paying 0$ for your database and Lambda.  However, the NAT gateway is going to cost you a whopping $30 monthly.  That is steep, especially if you are trying to build out a serverless stack on the cheap.  For me, it just wasn't worth it - there had to be a better way.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where it got difficult.  There didn't &lt;em&gt;seem&lt;/em&gt; to be another way.  It looked fairly certain that I would either have to sacrifice security or pay a prohibitive cost.  However, I dug to the deepest depths of the internet (Stackoverflow) and eventually the heavens opened up revealing a closely guarded secret, &lt;a href="https://stackoverflow.com/a/41559669"&gt;https://stackoverflow.com/a/41559669&lt;/a&gt;, which led me to the following,&lt;/p&gt;

&lt;p&gt;Use a public Lambda to call the private Lambda.  That sounds so obvious in hindsight.  But wait, I thought the VPC Lambda wasn't publicly accessible.  That certainly is true, but apparently the &lt;code&gt;Invoke&lt;/code&gt; API does not adhere to such mortal rules.  Now, you can have your cake and eat it too.  We retain the security of having the database in the VPC, while also keeping costs at zero.  The only downside is you will have to manage several Lambdas which does add some operational overhead.  It also slightly complicates the debugging process, as each Lambda has its own logging group and figuring out which one is behaving poorly can be a tad tedious.&lt;/p&gt;

&lt;p&gt;Anyways, hope this was education and/or helps someone who finds themself in a similar situation.  Curious to see what else the community has to say about VPC, Lambdas, and the like.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>aws</category>
      <category>database</category>
      <category>programming</category>
    </item>
    <item>
      <title>A small contribution</title>
      <dc:creator>peetss</dc:creator>
      <pubDate>Sat, 05 Feb 2022 03:13:00 +0000</pubDate>
      <link>https://dev.to/peetss/a-small-contribution-2633</link>
      <guid>https://dev.to/peetss/a-small-contribution-2633</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;Today I was working on adding some HTTP requests to an existing Rust project.&lt;/p&gt;

&lt;p&gt;I didn't really know anything about Rust (or Cargo) and something like this would've definitely saved me some frustration and allowed me to get moving more quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/peetss/rust-reqwest-example"&gt;https://github.com/peetss/rust-reqwest-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe if you are looking to try out Rust this might be a nice and easy way to get started.  Who doesn't like making HTTP requests?&lt;/p&gt;

&lt;p&gt;To be fair, most of this code came from a &lt;a href="https://stackoverflow.com/questions/66409193/errors-with-reqwest-example-in-rust-playground-and-local-machine"&gt;stackoverflow&lt;/a&gt; post, I just turned it into a project that you could import into your IDE of choice.&lt;/p&gt;

&lt;p&gt;At the end of the day we are all just standing on the shoulders of giants anyways, right?&lt;/p&gt;

</description>
      <category>rust</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Don't give up.</title>
      <dc:creator>peetss</dc:creator>
      <pubDate>Sun, 14 Feb 2021 14:43:33 +0000</pubDate>
      <link>https://dev.to/peetss/don-t-give-up-3gb2</link>
      <guid>https://dev.to/peetss/don-t-give-up-3gb2</guid>
      <description>&lt;p&gt;It may feel disheartening when you continue to throw new ideas at the wall and none of them stick.&lt;/p&gt;

&lt;p&gt;It may feel disheartening when you work your ass off for days and weeks and months and no one seems to notice or care.&lt;/p&gt;

&lt;p&gt;Don't give up hope, do not stop persisting.  Keep throwing ideas at the wall, keep trying to be the change you want to see in the world.&lt;/p&gt;

&lt;p&gt;People will notice. Maybe not today, maybe not tomorrow, but someday, for sure, people will notice.&lt;/p&gt;

</description>
      <category>career</category>
      <category>webdev</category>
      <category>advice</category>
      <category>entrepreneur</category>
    </item>
  </channel>
</rss>
