<?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: Antonio Sánchez</title>
    <description>The latest articles on Forem by Antonio Sánchez (@asanchez).</description>
    <link>https://forem.com/asanchez</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%2F26346%2Fa6f71330-5485-4cfe-b920-bd8aad0ef89a.jpeg</url>
      <title>Forem: Antonio Sánchez</title>
      <link>https://forem.com/asanchez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/asanchez"/>
    <language>en</language>
    <item>
      <title>KasusKnacker: Building and Shipping a German Practice App with AI</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Mon, 02 Feb 2026 17:34:47 +0000</pubDate>
      <link>https://forem.com/asanchez/kasusknacker-building-and-shipping-a-german-practice-app-with-ai-287i</link>
      <guid>https://forem.com/asanchez/kasusknacker-building-and-shipping-a-german-practice-app-with-ai-287i</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built &lt;strong&gt;&lt;a href="https://kasusknacker.com" rel="noopener noreferrer"&gt;an app for practicing German&lt;/a&gt;&lt;/strong&gt; called KasusKnacker, available on Web, iOS and Android. What’s interesting isn’t the app, but the process: I built the entire product mostly with AI, including software, learning content generation, translations, copywriting, and visual assets. AI made it possible to create ~30,000 exercises with explanations/translations in a few hours for under 150€ (including QA passes). It also drastically reduced the time to prototype and ship multi-platform features. However, AI is not a magic bullet: it tends to generate duplicated code, fragile architecture, and naïve implementations around security and costs. In Firebase-style stacks, this can easily translate into real billing risk. AI multiplies productivity — and mistakes. &lt;strong&gt;Human review is not optional&lt;/strong&gt;.&lt;/p&gt;




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

&lt;p&gt;I previously wrote about AI-assisted coding in &lt;a href="https://asanchez.dev/blog/ai-in-coding-my-two-cents/" rel="noopener noreferrer"&gt;AI in Coding: My Two Cents&lt;/a&gt;. This post is the practical follow-up: building and shipping a real product mostly with AI tools, and what worked vs. what didn’t. It’s basically my “what actually happened” log.&lt;/p&gt;

&lt;p&gt;KasusKnacker is a &lt;strong&gt;&lt;a href="https://kasusknacker.com" rel="noopener noreferrer"&gt;German practice app&lt;/a&gt;&lt;/strong&gt;. It focuses on the grammar gaps many expats keep for years (der/die/das, cases, prepositions, conjugation), and it also includes flashcards for vocabulary and idioms.&lt;/p&gt;

&lt;p&gt;I originally built it as a hobby project for myself. I’m Spanish, I’ve lived in Germany for over 12 years, and since I never formally studied German, I still hit the same grammar potholes. The app is basically training for exactly that.&lt;/p&gt;

&lt;p&gt;But I also used the app as an experiment: could I build an entire product end-to-end using AI, not only for coding, but for everything else that usually blocks solo projects (content, translations, design assets, marketing copy, etc.)? I wanted a real answer, not a demo.&lt;/p&gt;




&lt;h2&gt;
  
  
  My AI Tool Usage
&lt;/h2&gt;

&lt;p&gt;For coding, I used a mix of Antigravity (Gemini 3 Flash and Gemini 3 Pro), Claude Code (Opus 4.5) and Codex (GPT-5.2). I’d usually ask two models the same question and see where they disagreed.&lt;/p&gt;

&lt;p&gt;For generating exercises and translations, I used Gemini 3 Flash and Gemini 3 Pro.&lt;/p&gt;

&lt;p&gt;For app and website copy, I used GPT-5.2.&lt;/p&gt;

&lt;p&gt;For images (logo, avatars and social media visuals), I used nano banana.&lt;/p&gt;

&lt;p&gt;My workflow was to run multiple models for the same task and compare results. This is especially useful for higher-level code changes (architecture, refactoring, SDK usage), where models often disagree. When they disagree, it’s a signal that the task has complexity that deserves careful review.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’ve Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Software development has already changed
&lt;/h3&gt;

&lt;p&gt;I don’t know if AGI will come soon or not. The label is not important.&lt;/p&gt;

&lt;p&gt;What matters is that for software development, the paradigm already changed. For an experienced developer (especially someone used to working across dev + ops), AI tools drastically reduce the time required to build and ship products. I felt that shift almost immediately.&lt;/p&gt;

&lt;p&gt;Not by replacing engineering, but by compressing the execution layer: writing boilerplate, generating repetitive code, composing UI pieces, writing tests, producing documentation, etc.&lt;/p&gt;

&lt;p&gt;But the real effect is scope. A solo developer can now deliver projects that used to require a team, mainly because the AI tools reduce friction across multiple disciplines (coding, content, design, copywriting). That’s the part that still surprises me.&lt;/p&gt;

&lt;h3&gt;
  
  
  The biggest impact wasn’t coding. It was content.
&lt;/h3&gt;

&lt;p&gt;For educational apps, content generation is the real bottleneck. The code is the easy part.&lt;/p&gt;

&lt;p&gt;KasusKnacker includes roughly 30,000 exercises, each with translations and explanations. Without AI, producing that amount of content would have been unrealistic for a solo developer. It would require either a long timeline or a paid team (and still plenty of QA work).&lt;/p&gt;

&lt;p&gt;With AI, it became an engineering process: define a schema, generate in batches, validate automatically, then apply QA passes.&lt;/p&gt;

&lt;p&gt;I generated the entire set in a few hours for under 150€ including QA passes. That number felt unreal when I saw the final bill.&lt;/p&gt;

&lt;p&gt;That was the first moment I felt AI doesn’t just make you faster. It changes what is possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI removed most of the design bottleneck
&lt;/h3&gt;

&lt;p&gt;Design and assets are another classic blocker for solo apps. You can ship functional software, but without coherent visuals it will look unfinished.&lt;/p&gt;

&lt;p&gt;Without AI, I would have needed a designer for logo, avatars, and a lot of visual content used across the app, website and social media. With AI, I produced enough assets to ship a coherent product. Some of them I iterated on for days, but at least I could iterate at all.&lt;/p&gt;

&lt;p&gt;No, it’s not a substitute for good professional design. But it’s sufficient for shipping a real app.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI productivity is real, but quality control is mandatory
&lt;/h3&gt;

&lt;p&gt;AI makes you more productive, but it is not a magic tool. It definitely fooled me a couple of times.&lt;/p&gt;

&lt;p&gt;It generates a lot of code quickly, but it also generates duplicated implementations, wrong assumptions, weak error handling, unnecessary complexity, and subtle bugs that are easy to miss.&lt;/p&gt;

&lt;p&gt;Even if your prompts are good.&lt;/p&gt;

&lt;p&gt;It is also common for AI-generated code to work while still being structurally wrong: mixing SDKs, bypassing best practices, or creating a fragile architecture that becomes hard to maintain. I saw this happen more than once.&lt;/p&gt;

&lt;p&gt;AI is good at execution, not at engineering judgment. You still need to review, refactor, and simplify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Limitations (Real Examples)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. AI-generated content still needs QA
&lt;/h3&gt;

&lt;p&gt;Around 2% of generated exercises contained actual errors (wrong answer, wrong translation, wrong explanation). That’s not huge, but with 30,000 items it becomes a lot of broken content.&lt;/p&gt;

&lt;p&gt;More importantly, around 18% of exercises needed improvements. Not because they were objectively wrong, but because they were not aligned with the intended level/topic. Common issues were overly long questions, ambiguity (multiple answers could be correct), unnecessary complexity, or translations that were technically correct but confusing.&lt;/p&gt;

&lt;p&gt;This is where most AI content generation projects fail. Generating content is easy. Generating good content at scale is not.&lt;/p&gt;

&lt;p&gt;My solution was to use AI against itself: run review passes that validate the content and request corrections. This improved quality massively, and reduced the amount of manual cleanup needed. It also made the remaining fixes feel manageable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Context handling issues lead to duplication and fragile codebases
&lt;/h3&gt;

&lt;p&gt;The app went from idea to working product very quickly. Multi-platform support (web + native) was not a big problem.&lt;/p&gt;

&lt;p&gt;But despite good prompting, AI still tends to introduce duplication and architectural drift. It frequently forgets existing abstractions and re-implements things. It also tends to patch forward: if something fails, it adds another layer rather than simplifying the original code.&lt;/p&gt;

&lt;p&gt;A concrete example: mixing approaches between Firebase web SDK and the Capacitor-native SDK for auth. It can be made to work, but the result becomes fragile and hard to reason about. I ended up standardizing just to keep my sanity.&lt;/p&gt;

&lt;p&gt;So after the first “it works” stage, you should expect a deliberate cleanup stage: consolidating duplicated code, removing incorrect approaches, enforcing one architecture, and adding error handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Security and costs are the most dangerous blind spots
&lt;/h3&gt;

&lt;p&gt;For real products, this is the most important limitation.&lt;/p&gt;

&lt;p&gt;AI tends to generate naïve implementations around security boundaries and abuse scenarios. It can generate code that works perfectly and at the same time creates billing risks, privilege escalation paths, or insecure data flows. This is the part that worried me most.&lt;/p&gt;

&lt;p&gt;In Firebase-style architectures, abuse becomes billing. If your database rules and backend architecture are not designed with abuse in mind, vibe coding can literally become expensive.&lt;/p&gt;

&lt;p&gt;During review I found and fixed multiple risk patterns: inefficient backend usage, possible billing attacks against Firestore/Functions, and client-side logic that should never be trusted (e.g. users being able to modify critical fields such as premium subscription state).&lt;/p&gt;

&lt;p&gt;After manually reviewing and optimizing the architecture, I reduced projected hosting costs by roughly 95%. Not because I changed infrastructure, but because the initial AI-generated approach was inefficient and too permissive. That was an expensive lesson I got to avoid.&lt;/p&gt;

&lt;p&gt;This is a strong warning for anyone shipping AI-generated production systems: if you don’t understand your stack deeply, AI will happily generate something that becomes a future liability.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Use AI Effectively (My Current Process)
&lt;/h2&gt;

&lt;p&gt;AI works best when the developer acts as an architect.&lt;/p&gt;

&lt;p&gt;The model should not be the driver. It should be the execution engine. Your job is to define the structure, constraints, and quality bar.&lt;/p&gt;

&lt;p&gt;In practice that means you break work into smaller tasks, you provide explicit constraints (stack, libraries, patterns), and you review every output critically. I treat it like delegating to a smart but rushed teammate.&lt;/p&gt;

&lt;p&gt;You should treat AI output like code written by a junior developer: it can be useful, but it must be checked, simplified, and tested.&lt;/p&gt;

&lt;p&gt;And importantly: when building production systems, always consider security and billing early. AI will not protect you there. If anything, it increases your exposure because it makes it so easy to ship quickly without thinking about abuse scenarios.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vibecoding</category>
      <category>claude</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Synthetic Product Data Generator</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Sun, 24 Mar 2024 18:49:12 +0000</pubDate>
      <link>https://forem.com/asanchez/synthetic-product-data-generator-103h</link>
      <guid>https://forem.com/asanchez/synthetic-product-data-generator-103h</guid>
      <description>&lt;p&gt;Today, I'm sharing a new project: a &lt;a href="https://github.com/antsanchez/synthetic-product-data-generator" rel="noopener noreferrer"&gt;Jupyter Notebook for synthetic product data generation&lt;/a&gt; using Large Language Models (LLMs) through the &lt;a href="https://python.langchain.com/docs/get_started/introduction" rel="noopener noreferrer"&gt;LangChain framework&lt;/a&gt;. This tool is designed to aid in the creation of synthetic yet realistic product listings for e-commerce and other domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;The notebook facilitates the generation of synthetic data, including vendor names, product categories, and detailed product information such as titles, descriptions, and prices. It is built with flexibility in mind, allowing for the integration of different LLMs and customization of data generation parameters to suit various needs.&lt;/p&gt;

&lt;p&gt;Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integration with multiple LLMs, including examples for Ollama and OpenAI.&lt;/li&gt;
&lt;li&gt;Customizable data generation to adjust shop type, categories, vendors, and product details.&lt;/li&gt;
&lt;li&gt;Interactive environment for real-time monitoring and adjustments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I Started This Project
&lt;/h2&gt;

&lt;p&gt;I started working on this project because I'm really interested in a few key areas:&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploration of LangChain
&lt;/h3&gt;

&lt;p&gt;The main reason I began this project was to gain more hands-on experience with the LangChain framework. I was curious to see not only how it operates but what possibilities it unlocks when integrated with various LLMs. &lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt Engineering
&lt;/h3&gt;

&lt;p&gt;Another important part of this project is learning how to make good prompts. Figuring out the right way to ask questions or give commands to LLMs can really change the results you get. This project is like a testing ground where I try out different kinds of prompts to see how small changes can make a big difference in what kind of information or data we can generate.&lt;/p&gt;

&lt;p&gt;I quickly found out the trickiest part was getting the prompts just right. It’s amazing how a single word change can totally switch up the results, especially with the smaller LLMs. This part of the project was where I spent most of my time, going through many iterations to get it perfect&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Synthetic Data
&lt;/h3&gt;

&lt;p&gt;A big part of why I'm doing this is to have a tool that lets me easily create customized synthetic data for some others of machine learning and retrieval-augmented generation (RAG) projects I'm working on. Having the ability to produce realistic yet artificial data means I can test, develop, and analyze different projects more effectively. &lt;/p&gt;

&lt;h2&gt;
  
  
  Crafting My Own Tools
&lt;/h2&gt;

&lt;p&gt;So, why didn't I just use some tool that's already out there? Because building my own would is more fun and a great way to practics. Plus, I wanted something that fit exactly what I needed, which I couldn't really find with the tools available. &lt;/p&gt;

&lt;p&gt;This notebook is a practical tool that embodies these motivations. It demonstrates the power of combining custom prompts with different LLMs to produce synthetic data that is not only diverse but also closely tailored to specific project requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;For those interested in using or contributing to this project, the notebook is available on my GitHub repository. Installation instructions and details on how to customize and run the notebook are provided in the README. The project also supports running in Google Colab for ease of access and use.&lt;/p&gt;

&lt;p&gt;Dependencies can be installed via pip, and the project is ready to run in a local environment or in Google Colab with minimal setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://colab.research.google.com/github/antsanchez/synthetic-product-data-generator/blob/main/Generator.ipynb" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcolab.research.google.com%2Fassets%2Fcolab-badge.svg" alt="Open In Colab" width="117" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Join the Effort
&lt;/h2&gt;

&lt;p&gt;I welcome contributions and ideas to enhance this tool. Whether you have suggestions for new features or improvements, feel free to reach out through GitHub.&lt;/p&gt;

</description>
      <category>python</category>
      <category>llm</category>
      <category>langchain</category>
      <category>ai</category>
    </item>
    <item>
      <title>Relaunching after Pivoting</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Thu, 20 Jan 2022 11:00:10 +0000</pubDate>
      <link>https://forem.com/asanchez/relaunching-after-pivoting-blp</link>
      <guid>https://forem.com/asanchez/relaunching-after-pivoting-blp</guid>
      <description>&lt;p&gt;On March 2021, and after almost two years of development, I launched found.dev. I had a lot of expectations on it, but as it happens with most of the new launches, it was a complete failure.&lt;/p&gt;

&lt;p&gt;Even though the software was working good and had enough features, it seemed like absolutely no one had any kind of interest in the product, at all. After several months of trying different approaches on how to get users to try and use the product, I gave up.&lt;/p&gt;

&lt;p&gt;I didn't work on the project for a few months and stopped to think about all errors I had done: no marketing plan at all, no clear roadmap, diffuse sales pitch, etc... The product was, in my opinion, good, but I just didn't know how to sell it. Or worse, the product was good, but no one needed it.&lt;/p&gt;

&lt;p&gt;In December 2021, I decided to take on the project again and completely transform it into a new thing. Something way simpler, based on the feedback I got from my first try: a job board. But not a normal job board like the other thousands of job boards out there, based on a specific language, framework, or tool, but a job board for having them all!&lt;/p&gt;

&lt;p&gt;Today, after a few weeks of work, I have completely remade the product and I have just launched it on Product Hunt: &lt;a href="https://www.producthunt.com/posts/found-dev-2-0" rel="noopener noreferrer"&gt;https://www.producthunt.com/posts/found-dev-2-0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would appreciate any feedback :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Download a copy of an entire website</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Sat, 11 Dec 2021 19:08:36 +0000</pubDate>
      <link>https://forem.com/asanchez/download-a-copy-of-an-entire-website-487m</link>
      <guid>https://forem.com/asanchez/download-a-copy-of-an-entire-website-487m</guid>
      <description>&lt;p&gt;I have created a simple yet powerful command-line application to easily download an entire copy of any online website. &lt;/p&gt;

&lt;p&gt;It is coded with Go and it is available on my GitHub: &lt;a href="https://github.com/antsanchez/go-download-web" rel="noopener noreferrer"&gt;https://github.com/antsanchez/go-download-web&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I normally use it to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Back up any website (at least the rendered part of the website)&lt;/li&gt;
&lt;li&gt;Create copies of websites for accessing them offline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also allows you to change the URL of the downloaded website, which can be very helpful when moving an existing static website from a domain to another. &lt;/p&gt;

&lt;p&gt;Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.&lt;/p&gt;

&lt;p&gt;If you need any help or have any questions, do not hesitate to contact me. &lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Deploy a Go API to AWS Lambda and API Gateway</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Sun, 07 Nov 2021 20:51:17 +0000</pubDate>
      <link>https://forem.com/asanchez/deploy-a-go-api-to-aws-lambda-and-api-gateway-5fkj</link>
      <guid>https://forem.com/asanchez/deploy-a-go-api-to-aws-lambda-and-api-gateway-5fkj</guid>
      <description>&lt;p&gt;In this article, I am going to describe the process of deploying an API coded with Go to AWS Lambda and Amazon API Gateway, a process that takes only a few minutes if you know how to do it. &lt;/p&gt;

&lt;p&gt;We will see how easy it is to deploy a Lambda function and validate requests and API keys via the API Gateway. &lt;/p&gt;

&lt;p&gt;I will assume that you have a basic knowledge of Go and an AWS account. If you don’t know anything about AWS Lambda and API Gateway, don’t worry, all you need to know is explained here. If you already have a good knowledge of Lambda and API Gateway, feel free to skip some of the explanations. &lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Lambda function using Go
&lt;/h2&gt;

&lt;p&gt;As you probably know already, with AWS Lambda you can “run code without provisioning or managing infrastructure. Simply write and upload code as a .zip file or container image.”&lt;/p&gt;

&lt;p&gt;Creating a Lambda function is pretty easy. You need some code and a Lambda function where to upload it. We will start with the code. &lt;/p&gt;

&lt;p&gt;All you need to know is that you need to import &lt;code&gt;github.com/aws/aws-lambda-go/lambda&lt;/code&gt; and create a “handler”. There is already a very good explanagion about which arguments can this function take, so it is not worth repeating it:&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Let’s create a very simple function that will only get a team name and an array with the members, and return the name of the team and the number of members. You can find the code here: &lt;a href="https://github.com/antsanchez/go-lambda-apigateway-example" rel="noopener noreferrer"&gt;https://github.com/antsanchez/go-lambda-apigateway-example&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="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&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;TeamRequest&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt;   &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;Members&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"members"`&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;Response&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;         &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;MembersCount&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="s"&gt;`json:"members_count"`&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;GetTeamAndMembers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="n"&gt;TeamRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Response&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="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;MembersCount&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Members&lt;/span&gt;&lt;span class="p"&gt;),&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;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GetTeamAndMembers&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;Once the function has been created, you have to compile the code and create a ZIP for uploading it to AWS Lambda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build main.go
% zip &lt;span class="k"&gt;function&lt;/span&gt;.zip main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s go to the AWS console, search for Lambda, and click on create a new function. Pick whatever name you like, select Go, leave the rest as it is for now and click on create. &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%2Fb7c5sxjewcxv8dumrvcd.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%2Fb7c5sxjewcxv8dumrvcd.png" alt="create lambda function" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next screen, make sure to change the handler to “main”, as on the following screenshot:&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%2Fkdh68vgsdm2jg0choldp.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%2Fkdh68vgsdm2jg0choldp.png" alt="lambda handler" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now upload the ZIP folder and that’s it, your function is ready to use! Before switching to API Gateway, let’s test it. Go to the “Test” tab and create a request, like this:&lt;/p&gt;

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

&lt;p&gt;You should see this response:&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;"Example Team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"members_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&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;h2&gt;
  
  
  Create the API Gateway trigger for the Lambda function
&lt;/h2&gt;

&lt;p&gt;Ok, the Lambda function exists now, but how can you access it? We need to create a trigger, in this case, an API Gateway trigger. This is going to create an API Endpoint for us that we can use to call our Lambda function. &lt;/p&gt;

&lt;p&gt;Click on Add Trigger:&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%2Fiab1355thz64qhqh0fsz.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%2Fiab1355thz64qhqh0fsz.png" alt="lambda add trigger" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then select API Gateway, REST API as an API type, “Open” as a security mechanism for now, and hit on “add”.&lt;br&gt;
Our screens should now look like this, and there it is our API Endpoint. &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%2F9r4cd4ixa88arkd9k9q6.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%2F9r4cd4ixa88arkd9k9q6.png" alt="API Gateway endpoint" width="800" height="583"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Validate JSON Request with API Gateway
&lt;/h2&gt;

&lt;p&gt;We can validate the request before it even hits our Lambda function thanks to API Gateway. Like this, we will save some money by not executing the function on bad requests. &lt;/p&gt;

&lt;p&gt;Click on the link to the API Gateway (not the API Endpoint) to open the settings of our Gateway, you will see something like this:&lt;/p&gt;

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

&lt;p&gt;Now click on Models and “create”. Here, we can add a JSON Schema to describe how the request should be done. Going in deep about how to define the JSON Schema and its possibilities is out of the scope of this tutorial, but you can find more documentation here: &lt;a href="http://json-schema.org/" rel="noopener noreferrer"&gt;http://json-schema.org/&lt;/a&gt; All you need to know for now is that you need to define the schema of the request like this:&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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://json-schema.org/draft/2020-12/schema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Team and members request"&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;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"name"&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;"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;"string"&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;"members"&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;"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;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"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;"string"&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;span class="nl"&gt;"required"&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="s2"&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;"members"&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;p&gt;As you can see, we are defining an object with two fields, &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;members&lt;/code&gt;. The first one of type string, the second one of type array, with its items of type string. If the request doesn't follow this schema, the Lambda function won’t be executed. &lt;/p&gt;

&lt;p&gt;Paste the JSON, insert a name, and set the &lt;code&gt;Content-type&lt;/code&gt; to &lt;code&gt;application/json&lt;/code&gt;, like this:&lt;/p&gt;

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

&lt;p&gt;Once the model has been created, go to "Resources", click on "Actions" and "Create Method". Select any HTTP Verb you may like, like POST or GET. Once created, select the Lambda Function we just created and create the method. Now Click on &lt;code&gt;Method Request&lt;/code&gt;, select &lt;code&gt;Validate body&lt;/code&gt; on &lt;code&gt;Request Validator&lt;/code&gt; and select the model we just created on &lt;code&gt;Request Body&lt;/code&gt;, as on the screenshot:&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%2Fcdpfpc5yjr4f1zxw72ac.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%2Fcdpfpc5yjr4f1zxw72ac.png" alt="API Gateway validation" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, click again on "Actions" and deploy the API. Wait a few seconds, and now if you try to do a bad request, you will get an error. &lt;/p&gt;

&lt;h2&gt;
  
  
  Add API Keys to Amazon API Gateway
&lt;/h2&gt;

&lt;p&gt;Our API is working and open to the world. We may want to limit this by accepting only requests which include an API Key. &lt;/p&gt;

&lt;p&gt;But first, we need to create usage plans. For this, just go to "Usage Plans", hit on create, select a name, and the options about throttling and quotas that you would like to have. &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%2Fy76zlr99sobzztf4f57p.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%2Fy76zlr99sobzztf4f57p.png" alt="API Gateway usage plan" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can go to "API Keys", create an API Key and assign it to the usage plan. Of course, we can create many different API Keys and usage plans, and of course, we can also do this programmatically using the AWS CLI or the SDK for Go. &lt;/p&gt;

&lt;p&gt;Once API Key is created and assigned to an usage plan, we only need to pass it on our request as a header with the name &lt;code&gt;x-api-key&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;And that’s all ;) &lt;/p&gt;

</description>
      <category>go</category>
      <category>aws</category>
      <category>lambda</category>
      <category>apigateway</category>
    </item>
    <item>
      <title>Using the Amazon S3 SDK v2 for Go</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Sun, 26 Sep 2021 17:32:28 +0000</pubDate>
      <link>https://forem.com/asanchez/using-the-amazon-s3-sdk-v2-for-go-18km</link>
      <guid>https://forem.com/asanchez/using-the-amazon-s3-sdk-v2-for-go-18km</guid>
      <description>&lt;p&gt;Two years ago, I published the article "&lt;a href="https://asanchez.dev/blog/upload-to-amazon-s3-with-golang/" rel="noopener noreferrer"&gt;Upload files to Amazon S3 with Golang&lt;/a&gt;" explaining very briefly how to do some basic operations with Go and Amazon S3. Surprisingly, the article was very successful and two years later it still gets over one thousand visits monthly.&lt;/p&gt;

&lt;p&gt;Since Amazon upgraded the SDK for Go to version 2 some time ago, I have decided to create a new article adapting the examples to the new SDK. You can find here all information about the &lt;a href="https://aws.amazon.com/sdk-for-go/" rel="noopener noreferrer"&gt;AWS SDK v2 for Go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As well as on the first article, I am going to show here how to connect to S3 using Go, upload a file from a form to an AWS S3 bucket, download it, and list all items saved on this bucket.&lt;/p&gt;

&lt;p&gt;I am going to suppose that you have already:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;created an AWS account&lt;/li&gt;
&lt;li&gt;created an Amazon Simple Storage (S3) Bucket&lt;/li&gt;
&lt;li&gt;generated the credentials to access it (Access key ID and Secret access key)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am not going to explain those things here since there are plenty of good tutorials on the Internet about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your credentials
&lt;/h2&gt;

&lt;p&gt;If you haven't done it yet, remember to save your credentials using the &lt;code&gt;configure&lt;/code&gt; command of the AWS CLI or just creating a file like described below on ~/.aws/credentials on Linux, macOS, or Unix. If using windows, create it on C:\Users\USERNAME.aws\credential. &lt;/p&gt;

&lt;p&gt;Contents of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[default] 
aws_access_key_id = your_access_key_id 
aws_secret_access_key = your_secret_access_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install the SDK
&lt;/h2&gt;

&lt;p&gt;Create your Go project by executing:&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;go&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And retrieve the standard set of SDK modules by doing:&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;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;You can skip all explanations below and check the code directly on &lt;a href="https://github.com/antsanchez/goS3example/" rel="noopener noreferrer"&gt;https://github.com/antsanchez/goS3example/&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I have tried to keep the code as minimal as possible. &lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to AWS S3
&lt;/h2&gt;

&lt;p&gt;Connecting Go with Amazon S3 is easy.&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;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/config"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/service/s3"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="c"&gt;// Region&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="c"&gt;// Bucket&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// We will be using this client everywhere in our code&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;awsS3Client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;configS3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// configS3 creates the S3 client&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;configS3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadDefaultConfig&lt;/span&gt;&lt;span class="p"&gt;(&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;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithRegion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_REGION&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;awsS3Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFromConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&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;If for some reason you want to programmatically introduce the credentials (if for instance you have to easily switch credentials, or you need to use in your code different buckets from different accounts), you can do it like this:&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;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/credentials"&lt;/span&gt;

&lt;span class="c"&gt;// ...&lt;/span&gt;

&lt;span class="c"&gt;// configS3 creates the S3 client&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;configS3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStaticCredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;your_access_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;your_secret_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadDefaultConfig&lt;/span&gt;&lt;span class="p"&gt;(&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;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithCredentialsProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithRegion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;awsS3Client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFromConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&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;h2&gt;
  
  
  Upload a file from a form to Amazon S3
&lt;/h2&gt;

&lt;p&gt;Let's see how upload a file form a form to Amazon S3 using Go and the http package.&lt;/p&gt;

&lt;p&gt;First, get the file from the Form:&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="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Do your error handling here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Filename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, upload it to S3:&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;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/service/s3"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/feature/s3/manager"&lt;/span&gt;

&lt;span class="c"&gt;// ...&lt;/span&gt;

&lt;span class="n"&gt;uploader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewUploader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsS3Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uploader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Upload&lt;/span&gt;&lt;span class="p"&gt;(&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;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;file&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;h2&gt;
  
  
  Download a file from Amazon S3
&lt;/h2&gt;

&lt;p&gt;Downloading a file from AWS S3 using Go requires only a few lines of code:&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;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/service/s3"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/feature/s3/manager"&lt;/span&gt;

&lt;span class="c"&gt;// ...&lt;/span&gt;

&lt;span class="c"&gt;// Name of the file where you want to save the downloaded file&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="c"&gt;// Key to the file to be downloaded&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; 

&lt;span class="c"&gt;// Create the file&lt;/span&gt;
&lt;span class="n"&gt;newFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;newFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;downloader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDownloader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsS3Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;numBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;downloader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Download&lt;/span&gt;&lt;span class="p"&gt;(&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;TODO&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;newFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetObjectInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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;h2&gt;
  
  
  List the items of an AWS S3 Bucket
&lt;/h2&gt;

&lt;p&gt;Listing an entire S3 bucket or the contents of a folder is very easy using Go.&lt;/p&gt;

&lt;p&gt;About folders, there aren't really any folders in S3, but we can emulate them by using "/" in the key names of the objects. In case we want to list the contents of a "folder" in S3, what we really need to do is to list all objects which have a certain prefix.&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;import&lt;/span&gt; &lt;span class="s"&gt;"github.com/aws/aws-sdk-go-v2/service/s3"&lt;/span&gt;

&lt;span class="c"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;       &lt;span class="c"&gt;// For instance, list all ojbects under /bar/foo/ ...&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;delimeter&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="c"&gt;// ... using "/" as delimeter&lt;/span&gt;

&lt;span class="n"&gt;paginator&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewListObjectsV2Paginator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsS3Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListObjectsV2Input&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Delimiter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delimeter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;paginator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasMorePages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;paginator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NextPage&lt;/span&gt;&lt;span class="p"&gt;(&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;TODO&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Error handling goes here&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contents&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Do whatever you need with each object "obj"&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s all.&lt;/p&gt;

</description>
      <category>go</category>
      <category>aws</category>
      <category>s3</category>
    </item>
    <item>
      <title>Launching Found.dev</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Fri, 23 Apr 2021 07:11:56 +0000</pubDate>
      <link>https://forem.com/asanchez/launching-found-dev-1ilk</link>
      <guid>https://forem.com/asanchez/launching-found-dev-1ilk</guid>
      <description>&lt;p&gt;For over a year, I have been working on a side-project in my spare time. It all started as a way to keep improving my knowledge and expertise with &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;Go&lt;/a&gt; by coding, but at some point, I decided I could give it a try and make a business of this project. &lt;/p&gt;

&lt;p&gt;At the end of March 2021, I decided the project was ready to see the light, so I launched &lt;a href="https://found.dev" rel="noopener noreferrer"&gt;Found.dev&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Found.dev?
&lt;/h2&gt;

&lt;p&gt;As described on the FAQ of the site, it is much more than simply a job board for developers. It is a place where developers can have a public developer profile, find jobs and apply to them, while companies can post jobs, have a public profile too, look for developers, and manage applications. &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%2Fofilhmfigcsi327xbogm.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%2Fofilhmfigcsi327xbogm.png" alt="Company Dashboard Found.dev" width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why another job board?
&lt;/h2&gt;

&lt;p&gt;It was never my intention to do a job board. It just happened. &lt;/p&gt;

&lt;p&gt;In the beginning, I was doing something like a database of developers: somewhere, where developers could have a profile and a recruiter could search for them. &lt;/p&gt;

&lt;p&gt;But during the process of idea validation, I changed the direction a few times, until I ended up creating yet another job board.&lt;/p&gt;

&lt;p&gt;At this moment, I can’t tell if I’ll keep the project in this direction or if it will again pivot as I see how the users interact with it and the feedback I receive. &lt;/p&gt;

&lt;h2&gt;
  
  
  How is it going?
&lt;/h2&gt;

&lt;p&gt;A few weeks after launch, it is going as expected: not so good. &lt;/p&gt;

&lt;p&gt;This is a product with a lot of well-established competitors who have been for a long time on the market and who have a lot of users already. Furthermore, it is difficult to get companies to post without having enough developers, but developers won’t register without enough job posts. &lt;/p&gt;

&lt;p&gt;But this was all expected. I continue writing to some hand-picked companies and developers every day to invite them to register, and I don’t know if I am getting better and better and writing or what, but the percentage of users that are registering is continually increasing. &lt;/p&gt;

&lt;p&gt;The big problem is the lack of time. Due to my job at &lt;a href="https://kokoen.net" rel="noopener noreferrer"&gt;Kokoen&lt;/a&gt;, I can only use a few hours a day (and not every day) for this project, so it will be a slow process. &lt;/p&gt;

&lt;h2&gt;
  
  
  What things have I done wrong?
&lt;/h2&gt;

&lt;p&gt;Many things. Even though I am experienced enough with launching products/startups, I have made some huge mistakes that I shouldn’t have done, since I knew that those are typical launch mistakes... &lt;/p&gt;

&lt;p&gt;The bigger mistake has been developing the whole product without building an audience first. This is a beginner's mistake, and I am afraid (and ashamed) that I have done it again. I completely neglected the social aspect needed to successfully launch a product: have enough audience or distribution channels to make it visible to enough potential users without having to spend money on ads. &lt;/p&gt;

&lt;p&gt;Now I am trying to catch up with lost time by being very active in some communities like Indie Hackers, Reddit, some developer's websites, etc... But to do this well, I need more time than I have. Twitter is working the best: I am not only posting all jobs there but also engaging with the community by answering tweets, sending DMs, etc... And even though the number of followers isn’t growing much, it is driving way more traffic than expected. Maybe it is working so well because I am using some tools to &lt;a href="https://www.exportdata.io/" rel="noopener noreferrer"&gt;export Twitter data&lt;/a&gt; to address exactly the users I want to. &lt;/p&gt;

&lt;h2&gt;
  
  
  Do you want to join?
&lt;/h2&gt;

&lt;p&gt;Right now, only developers and companies with an invitation code can join in. This is to assure a certain level of quality. If you think you qualify for this, please contact me!&lt;/p&gt;

</description>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Which are the running costs of an App?</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Wed, 11 Nov 2020 14:50:30 +0000</pubDate>
      <link>https://forem.com/asanchez/which-are-the-running-costs-of-an-app-3jl5</link>
      <guid>https://forem.com/asanchez/which-are-the-running-costs-of-an-app-3jl5</guid>
      <description>&lt;p&gt;An app's running costs can span from zero to infinite, depending on many factors like the functions the app offers, the number of active users it has, the required server capacity, or the services from third party suppliers it relies on. I will examine here the most commons of those factors and their costs. &lt;/p&gt;

&lt;p&gt;For every requirement or function an app has, there are multiple options on how that need can be covered: some of those solutions are open-source and free but require more developing time and costs or to manage your own server; some of them are subscriptions or pay-as-you-go fully managed services offered by third-party providers. &lt;/p&gt;

&lt;p&gt;There are mainly five types of running costs that almost every app has: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App Stores fees.&lt;/li&gt;
&lt;li&gt;File Storage, for storing/uploading/downloading all needed files.&lt;/li&gt;
&lt;li&gt;Database, for keeping all data that the app needs to run.&lt;/li&gt;
&lt;li&gt;Server, for having one or many APIs to connect to.&lt;/li&gt;
&lt;li&gt;Third-Party APIs, like Google Maps API, Payments APIs, Chats, etc..&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But before I continue, there is some good news you will be happy to hear: nowadays, there are a lot of serverless and cloud providers like Firebase, Netlify, Heroku, or AWS that offer all services your app may need for very little money. If your app has only a few thousand users, the cost of using those services, maybe even zero.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does my app need any Server or API at all?
&lt;/h3&gt;

&lt;p&gt;If your app doesn't offer any user account that needs to be synced betIen devices, nor any content that must be downloaded or uploaded from/to one specific central location, you may need no infrastructure or APIs at all. In that case, everything can be done directly within the app or even with no internet connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Foreseeable costs vs. Pay on demand
&lt;/h3&gt;

&lt;p&gt;When deciding which provider or solution to use, the main question is if you prefer to have foreseeable fixed costs or choose to pay based on demand (depending on the app's real usage).   &lt;/p&gt;

&lt;p&gt;Foreseeable costs can be achieved by traditional means: having a contract for a fixed amount of time with any hosting provider and managing yourself everything. Sure, you will have more security about your costs, since they are already set. Still, suppose your app starts growing beyond your expectations. In that case, it is difficult and costly to scale since you will need to renegotiate the contract with your provider or manually add more servers to your infrastructure.  &lt;/p&gt;

&lt;p&gt;Foreseeable costs do not always mean feIr costs since you may be paying for capacity that you are not using: maybe you book a 2 Terabyte server, but your users are only using a few Gigabytes from that storage!  Furthermore, foreseeable costs typically imply that you are in charge of managing your infrastructure yourself, which means more costs for this management. &lt;/p&gt;

&lt;p&gt;By choosing to pay on demand, you will have a broad spectrum of serverless or PaaS options available that reduce or even eliminate the complexity when scaling is needed, making the process even wholly automatic. You will pay based only on what you consume so that there won't be any need to plan how many resources you need and not pay for resources you are not using. &lt;/p&gt;

&lt;p&gt;Also, services offered on-demand typically imply that you have little or no infrastructure to manage at all since your provider will take care of everything. You only have to use the service, which means feIr or no costs for those managing tasks. &lt;/p&gt;

&lt;p&gt;I believe paying on demand is the option that makes more sense in most cases, if not all cases, and for this reason, I will base our article only on this option. &lt;/p&gt;

&lt;h2&gt;
  
  
  App Stores Fees
&lt;/h2&gt;

&lt;p&gt;How much does it cost to have an app in the App Stores?&lt;br&gt;
The running costs of having an app in the Apple App Store and Google Play Store are almost zero. Neither Google nor Apple charges any recurrent amount of money for making your app available in their stores. HoIver, they charge a developer fee to create an account that you can use to upload as many Apps as you want. &lt;/p&gt;

&lt;p&gt;Apart from this developer fee, you may also take a commission from the money users pay you for abonnements or in-app-purchases if you offer this in your app. Those commissions are only for digital purchases (goods or services consumed within the app, like premium features or any other kind or content of function offered in the app only after paying for it). Non-digital purchases (all those goods or services consumed outside the app, like any physical products being sent to the user after buying them) are exempt from this commission. &lt;/p&gt;

&lt;p&gt;The price guidelines of both Apple and Google are very similar. &lt;/p&gt;

&lt;h3&gt;
  
  
  What are the costs of having an App in the Apple App Store?
&lt;/h3&gt;

&lt;p&gt;The needed developer account has a yearly fee of 99 USD.  In case you want to distribute your app only within your company or organization, you would need to apply for the Developer Enterprise Program, which cost 299 USD yearly. With one account, you can upload as many apps as you want. &lt;/p&gt;

&lt;p&gt;For digital subscriptions or purchases, Apple takes a 30% commission. This commission will be loIred to 15% after the first subscription year, per subscriber in case of auto-renewable subscriptions. &lt;/p&gt;

&lt;h3&gt;
  
  
  What are the costs of having an App in the Google Play Store?
&lt;/h3&gt;

&lt;p&gt;The needed developer account at Google is a one-time payment of 25 USD. With one account, you can upload as many apps as you want.&lt;br&gt;
Exactly as for Apple, subscriptions and digital purchases have a 30% commission. After the first subscription year, the commission decreases to 15%. &lt;/p&gt;

&lt;h2&gt;
  
  
  File Storage costs
&lt;/h2&gt;

&lt;p&gt;Your app users may most probably need to upload or download some files, like images, videos, or documents. Those must be stored somewhere on the Internet to be uploaded and downloaded at any given time and synced with different devices.  &lt;/p&gt;

&lt;p&gt;The most used service and almost industry-standard is Amazon S3. This service offers a highly, scalability, availability, security, and performance, starting from just a few cents. Here you pay for what you use. &lt;/p&gt;

&lt;p&gt;Let's say your app is a kind of social network that has 20.000 users. Each user has an average of 10 pictures of 4MB each. Your daily active user is 4.000. I calculate 800 GB of storage needed, 2.400 GB of data transfer, and 6.000.000 requests based on those numbers. This scenario would cause an estimated cost of about 38 USD monthly. You can calculate yourself the price here: &lt;a href="https://calculator.aws/#/createCalculator" rel="noopener noreferrer"&gt;https://calculator.aws/#/createCalculator&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Database costs
&lt;/h2&gt;

&lt;p&gt;Your app's data lives on a database: your user's accounts, all the information that they save, or all information that they see in the app.&lt;br&gt;
Database costs depend on the database capacity (how much info needs to be saved), the poIr of the database (CPU performance and read replicas), and the availability (backups, clones, and so forth..). The more users your app has, or the more data needs to be fit in the database, the more it will cost. &lt;/p&gt;

&lt;p&gt;For tiny apps with just about 2.000 users, the free tiers that most providers offer may be more than enough.&lt;/p&gt;

&lt;p&gt;For small and medium apps with less than 20.000 users, I estimate a cost of betIen 50 USD and 100 USD a month based on our experience. &lt;br&gt;
But again, as I said above, this may vary a lot from app to app. Some Shops may have only a thousand users (or visitors but have a database with millions of products on it. In this case, a potent server is needed to query those products and quickly deliver the results, increasing the costs. Or this shop may also need a very high availability to avoid losing any purchase, which may require some extra measures like having redundant databases, which can also multiply the costs. &lt;/p&gt;

&lt;h2&gt;
  
  
  Server Costs
&lt;/h2&gt;

&lt;p&gt;Not all functions that an app offers are run in the app. Some of these functions are done server-side like login/registering users or sending the queries to the database for searching and filtering. &lt;/p&gt;

&lt;p&gt;The number of users is not significant here, but the usage: how many concurrent active users your app has and how much CPU poIr it needs.&lt;br&gt;
There is also a multitude of options here. To avoid costs related to the server's maintenance and avoid scalability or security issues, it is recommendable to choose a cloud provider, no matter if serverless or PaaS. Due to its simplicity, I usually recommend Heroku or Firebase. Depending on your app's needs, you may also choose the serverless services from any of the big players like AWS, Google Cloud, Microsoft Azure, etc. &lt;/p&gt;

&lt;p&gt;Suppose you chose Heroku, the cost of running your API start at 7 USD. And this may be more than enough for small apps with just a few thousand users.&lt;/p&gt;

&lt;p&gt;If your active user base starts to grow or the functions that your API needs to run require more poIr, you may upgrade to a monthly cost of 25 USD to 50 USD, which should be enough for apps that less than 10.000 daily active users. That should be enough for the social media app I describe in our first example. &lt;/p&gt;

&lt;p&gt;For more than 10.000 daily active users, I would calculate a cost of above 100 USD monthly if you want your app to run quickly and smoothly enough. &lt;/p&gt;

&lt;p&gt;These costs described for Heroku are very similar to all other providers. &lt;br&gt;
Again, this may vary a lot from app to app, depending on its usage. &lt;/p&gt;

&lt;h2&gt;
  
  
  Third-party APIs
&lt;/h2&gt;

&lt;p&gt;Coding everything from scratch makes no sense: it is costly, challenging to keep up to date, and requires much effort for the maintaining. It is better to take the advantages that certain providers already offer for quickly implementing some functions in your app. &lt;/p&gt;

&lt;h3&gt;
  
  
  Payments
&lt;/h3&gt;

&lt;p&gt;If your app accepts payments, you may need to use the payment solutions I already discussed on the first point of this article, so there is no need to repeat it.&lt;br&gt;&lt;br&gt;
But in some other cases, you want or need to implement your payment solution. For this, you will need to use providers like PayPal, Stripe, or Adyen. They offer all your app needs to receive and send money from/to your users.&lt;br&gt;&lt;br&gt;
Those APIs are mostly free to use, and you only have to pay them a percentual commission for every transaction. Those commissions vary depending on the plan you subscribed to with the provider, the volume of transactions, and the features you are using, but mostly they span betIen 1,4% and 3% per transaction.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Push Notifications
&lt;/h3&gt;

&lt;p&gt;A must-have in most apps are push notifications. As for all other functions, there are different ways of how this can be implemented. &lt;br&gt;
Push notifications are sent from a server and received by the app. That means that at least a specific software and a server are needed to send the apps' notifications. &lt;/p&gt;

&lt;p&gt;A few open source solutions can be used for free to implement push notifications, like socket.io. HoIver, you would still need some server to host this software and dedicate some efforts to maintain that server. I recommend using a provider like OneSignal or Firebase Cloud Messaging to avoid unnecessary costs, reduce complexity, and increase stability, scalability, and security. &lt;/p&gt;

&lt;p&gt;The prices here vary depending on the number of notifications needed to be sent or the number of users receiving push messages. For most of the apps, those services can be used for free, and you only have to pay if you reach a large number of users or want particular features like Analytics or Intelligent delivery. &lt;/p&gt;

&lt;h3&gt;
  
  
  Google Maps and Google Places
&lt;/h3&gt;

&lt;p&gt;Most apps need to include at least one of those features:&lt;br&gt;
A Google map.&lt;br&gt;
Geolocation to see where the user is located.&lt;br&gt;
Some form where the user can insert a street or any other kind of location.&lt;br&gt;
For all those services, the API of Google Map or Google Places is required. The prices vary depending on the number of requests your apps do to Google. &lt;/p&gt;

&lt;p&gt;Using a Google Map on your app, you will pay 2 USD for every 1.000 loaded maps (this is 1.000 users, loading the map once). &lt;/p&gt;

&lt;p&gt;If you need some search or filter by location on your app, you will need to use the Geocoding functionality of Google Places for converting addresses to and from geographic coordinates. For every 1.000 requests, you will have 5 USD costs. &lt;/p&gt;

&lt;p&gt;If you want to give the user the ability to insert any address on a form, your app will need the Autocomplete function, which costs 2.83 USD for every 1.000 requests. &lt;/p&gt;

&lt;p&gt;The good news is that Google offers a monthly 200 USD in free usage, so most of the small apps with only a few thousand active users won't have to pay a single cent since with this 200 USD, you will have enough for having 100.000 API request. That is the equivalent of more than 3.300 active users loading a map every day.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Chat
&lt;/h3&gt;

&lt;p&gt;Similar to push notifications, there are enough open-source solutions hosted in almost any server to make your chat run. The problems with those solutions are mainly three:&lt;br&gt;
You will need to manage a server that can cope with all users your app has.&lt;br&gt;
Almost everything must be developed from scratch on the app.&lt;br&gt;
If you want some advanced features like video chat, you will have to implement it extra. &lt;br&gt;
For saving unnecessary costs of self-managing a server as Ill as reduce the time and cost of development, the usage of a provider like CometChat is recommendable. &lt;/p&gt;

&lt;p&gt;Again, the prices here depend on usage. Using CommetChat, the price starts at 49 USD per month for 1.000 monthly active uses and 50 concurrent users. If you have more users or want to add some features like video calls or chatbots, the price will also go up. You can check yourself all possible combinations here: &lt;a href="https://www.cometchat.com/pro-pricing" rel="noopener noreferrer"&gt;https://www.cometchat.com/pro-pricing&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Yes, this price may sound a lot if you have many thousand users. But it is still very cheap compared to all the money you save by not having to manage your chat server and not having to develop most of the functions like Video Chat or Chat Bots that are already available with a single click by using such a provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I know, even for an experienced developer, the number of options, prices, advantages, and disadvantages can be overwhelming. So don't feel bad if you still don't know how much running cost your app may have or if you didn't even thoroughly read the article. I will try to give you here a basic orientation based on the number of users and functions. &lt;/p&gt;

&lt;p&gt;Please, note that this is only an example for orientation purposes. Your app's real cost may vary depending on the functions and the grad of security, availability, and speed your app or infrastructure offers. &lt;/p&gt;

&lt;p&gt;Let's suppose an app that requires file storage, database, server, google APIs, and push notifications, as described above. I won't consider any discounts like the AWS Free Tier or the Free Usage offered by Google. Chat will be calculated separately. Chat is not considered in this example. &lt;/p&gt;

&lt;p&gt;The prices indicated are the average of our estimations: neither the loIr nor, the higher estimated price. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Estimation for a few hundred active users: from $5 to $15.&lt;/li&gt;
&lt;li&gt;Estimation for about ~2.000 monthly active users: from $15 to $50. &lt;/li&gt;
&lt;li&gt;Estimation for about ~5.000 monthly active users: from $25 to $70.&lt;/li&gt;
&lt;li&gt;Estimation for about ~10.000 monthly active users: from $50 to $100. &lt;/li&gt;
&lt;li&gt;Estimation for about ~20.000 monthly active users: from $70 to $150.&lt;/li&gt;
&lt;li&gt;Estimation for about ~50.000 monthly active users: from $100 to $250.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also read this article in German here: &lt;a href="https://kokoen.net/blog/wie-hoch-sind-die-laufenden-kosten-einer-app/" rel="noopener noreferrer"&gt;Wie hoch sind die laufenden Kosten einer App?&lt;/a&gt;&lt;/p&gt;

</description>
      <category>app</category>
    </item>
    <item>
      <title>How we develop apps reducing time and costs</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Wed, 29 Jul 2020 15:50:21 +0000</pubDate>
      <link>https://forem.com/asanchez/how-we-develop-apps-reducing-time-and-costs-3mec</link>
      <guid>https://forem.com/asanchez/how-we-develop-apps-reducing-time-and-costs-3mec</guid>
      <description>&lt;p&gt;During the last years, at &lt;a href="https://kokoen.net" rel="noopener noreferrer"&gt;Kokoen&lt;/a&gt;, we have specialized in creating mobile applications while reducing to the minimum time and costs of the development. Here is an overview of how we have achieved it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the client needs
&lt;/h2&gt;

&lt;p&gt;After receiving a client request, the first thing we want to define is what the client really needs to build. This may be very different from what he wants to create, and it is for sure, very, very different from what he says he wants to create.&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%2F44htk6qbywr75e2yaf6b.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%2F44htk6qbywr75e2yaf6b.png" alt="how to develop apps" width="800" height="579"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;Copyright © &lt;a href="http://www.projectcartoon.com" rel="noopener noreferrer"&gt;www.projectcartoon.com&lt;/a&gt; under the Creative Commons Attribution 3.0 Unported License&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The best thing here is not to waste time with non-ending conversations and documents with a lot of technical specifications. Writing the requirements is a good idea, but mostly only to define the "big picture" of what the app should be able to do and some specific desires that should figure on the contract. However, writing all minor technical details in a paper is inefficient since it is impossible to estimate all needs, questions, and problems that developers may have just imagining how the app should be. &lt;/p&gt;

&lt;p&gt;Instead, we try to build a click dummy to help the customer and our self visualize what we are trying to do and specify all technical details and requirements working on this click-dummy in an on-going way. &lt;/p&gt;

&lt;p&gt;Building a click dummy will help define the requirements better and discover problems or questions that, without a click-dummy, would have remained hidden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working Agile – Defining the Increments
&lt;/h2&gt;

&lt;p&gt;Requirements may change and will undoubtedly change. So, why to focus now on elements that may never be coded or, even worse, that may be recoded soon? That's why we prefer to work agile!&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%2Fekvllbwuo8b7zl5lp1zb.gif" 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%2Fekvllbwuo8b7zl5lp1zb.gif" alt="changing software requirements" width="900" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;Copyright © Andrews McMeel Syndication - &lt;a href="https://dilbert.com/" rel="noopener noreferrer"&gt;dilbert.com&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Agile's concept turns the traditional way of working of a company entirely upside down - in the most real sense of the word. Agile companies distribute responsibility away from management to the developer teams. Those teams take responsibility for their own actions. The management gets a completely different role. Control and guidance become support and encouragement for employees (keyword: Servant Leadership). The pyramid is reversed, what is now needed is a broad platform supported from below on which employees can work successfully. Clear goals and guidelines replace small-scale planning, enabling a rapid response to changes and unexpected events.&lt;/p&gt;

&lt;p&gt;The control function is taken over by the team itself. Not only does the team monitor the work progress, but it also continually tries to improve, remove obstacles, and distribute tasks sensibly. This is successful when all team members know the goals or visions they are working towards. Ideally, they have even participated in the formulation of these goals or visions.&lt;/p&gt;

&lt;p&gt;Three steps quickly lead to more agility:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Organize subprojects:
&lt;/h3&gt;

&lt;p&gt;The overall project is divided into sub-projects, each of which generates added value for the customer. It is important that the sub-projects do not overlap so that they can be pursued independently. Each sub-project must be broken down into concrete tasks before it can be processed.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Short-term planning:
&lt;/h3&gt;

&lt;p&gt;Only the next and the subsequent subproject is planned since these are the only subprojects for which the necessary details are usually available. For future sub-projects, the available information is often too imprecise; the risk of changes and replanning is high.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Take the customer's view:
&lt;/h3&gt;

&lt;p&gt;The partial and overall project result is formulated from the customer's point of view. This way, all team members do not lose sight of the goal and can react independently in the customer's mind if problems arise, whereby customers can be external or internal.&lt;/p&gt;

&lt;p&gt;The sum of all completed tasks at the end of a chosen time period (the so-called sprint) is called the Done Increment. The increment is the integration of the currently completed tasks with all tasks completed in previous sprints . We prefer to work with weekly increments. In a new project, there are usually still many uncertainties and unsolved problems/questions. These represent risks for the development because the effort of individual requirements can be estimated only with difficulty. Therefore, it is obvious to work in the smallest possible units, especially if the project is still in an early phase. Do you wonder what the advantages of this way of working are? Here they come:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regular reflection of the development process&lt;/li&gt;
&lt;li&gt;Use software productively as early as possible and then expand incrementally&lt;/li&gt;
&lt;li&gt;Present regularly running software to stakeholders&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reducing risks with MVPs
&lt;/h2&gt;

&lt;p&gt;These advantages are also the reason why we work with the so-called "Minimum Viable Product" (MVP), a product with the minimum requirements and characteristics that still makes the product worth using. When developing an MVP, the basic idea is to create a product as quickly as possible, with only the most necessary features. This is then published immediately, and feedback is first sought from (potential) customers. This feedback is used in the following to expand and improve the MVP.&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%2Fqqpdo4pobxw7k2habjuy.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%2Fqqpdo4pobxw7k2habjuy.jpg" alt="minimum viable product" width="609" height="411"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;Copyright © Henrik Kniberg - &lt;a href="https://blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp" rel="noopener noreferrer"&gt;Making sense of MVP (Minimum Viable Product)&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Any further functions should be left out of the MVP. Only those functions that are necessary to enable the actual purpose of the product will be included. This saves a lot of time, work, and money.&lt;/p&gt;

&lt;p&gt;The main reason for developing an MVP is to minimize risk. If you choose the path of developing and releasing a complete product, many problems can arise.&lt;/p&gt;

&lt;p&gt;For example, developing such a full-fledged product takes a lot of time and money. Far more than it would be with the development of an MVP.&lt;/p&gt;

&lt;p&gt;Another risk is the development of ignoring the real market and customer needs. If months of development are invested in a product, but nobody wants to have it after release, everyone has just wasted its time and money on building something no one needs. &lt;/p&gt;

&lt;p&gt;The development of an MVP, on the other hand, reduces the risk for companies by developing only a prototype with the most necessary functions and initially saves the company a lot of time and money. Besides, early market entry quickly evaluates whether the idea and the product have any chance at all on the market.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the best-fitting technologies
&lt;/h2&gt;

&lt;p&gt;Before we start developing, we spent a lot of time discussing which technologies we should use and how the application should be developed - entirely in the spirit of "Weeks of coding can save you hours of planning." The fundamental question in app development is, of course, the requirements and the desired platforms. Combined with the available budget, this simple calculation results in the final development strategy.&lt;/p&gt;

&lt;p&gt;There are nowadays different ways of coding an app: Native, Hybrid, Compiled, and PWA. Each of them has its advantages and disadvantages, and choosing the correct one can make you save tons of money and time. You can read more about this here: &lt;a href="https://asanchez.dev/blog/app-development-options-overview/" rel="noopener noreferrer"&gt;Overview of App Development Options&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, it is not possible to make a generally valid assessment for individual projects. Simple requirements can indeed be implemented web-based and distributed in the app stores via a native framework. Often, however, the current technological limitations push the project to its limits. In this case, a hybrid solution is usually an excellent alternative from a cost/benefit point of view. However, if the requirements are too complicated or elaborate or if you enter the terrain of game development, you can hardly avoid a native development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you have any questions or need consultancy about transforming your idea into an app? Do not hesitate to contact me.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>app</category>
      <category>hybrid</category>
      <category>native</category>
      <category>agile</category>
    </item>
    <item>
      <title>CORS and OPTIONS problems with Go and Angular</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Sat, 01 Feb 2020 18:15:17 +0000</pubDate>
      <link>https://forem.com/asanchez/cors-and-options-problems-with-go-and-angular-4k98</link>
      <guid>https://forem.com/asanchez/cors-and-options-problems-with-go-and-angular-4k98</guid>
      <description>&lt;p&gt;Lately, I have been working on some web app projects which were combining Go for the Back End and Angular for the Front End. Although both are very widely used, it looks like CORS is still a problem for many people when using this combination of Go and Angular. &lt;/p&gt;

&lt;p&gt;After searching on Google for some questions or problems I had, I saw many no answered questions or - what it is worse - bad responded to questions about this. &lt;/p&gt;

&lt;p&gt;I won't explain in detail here what CORS is or how it works since there is already a lot of good literature about this on the internet like this &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS&lt;/a&gt;. All you have to know in case you are not familiar with CORS is: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When running Angular and the Go API each in a different domain, protocol, or port, some headers must be set on the API to allow the Cross-Origin Resource Sharing (CORS) - this is, to allow your browser access the API. &lt;/li&gt;
&lt;li&gt;Angular - or better said, your browser - will not directly send a POST or GET request to this API, but it will first send an OPTIONS request. This OPTIONS request is just a "preflight" request soliciting which HTTP methods are supported by the API and check if the proper headers for CORS are set. This OPTIONS response is supposed to return an HTTP 200 Status Code as well as the headers required for CORS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Point one seems to be clear for almost anyone if you take a search about it on Google. But most people seem to ignore point two. &lt;/p&gt;

&lt;p&gt;How to handle this CORS and OPTIONS problems when working with Angular and Go? Well, you don't have to do anything in Angular, only in Go. Here is a middleware example for using the Framework Gin:&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom CORS Middleware with Gin:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// CORS Middleware&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&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="p"&gt;{&lt;/span&gt;

    &lt;span class="c"&gt;// First, we add the headers with need to enable CORS&lt;/span&gt;
    &lt;span class="c"&gt;// Make sure to adjust these headers to your needs&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Origin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Methods"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Headers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Second, we handle the OPTIONS problem&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"OPTIONS"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c"&gt;// Everytime we receive an OPTIONS request, &lt;/span&gt;
        &lt;span class="c"&gt;// we just return an HTTP 200 Status Code&lt;/span&gt;
        &lt;span class="c"&gt;// Like this, Angular can now do the real &lt;/span&gt;
        &lt;span class="c"&gt;// request using any other method than OPTIONS&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AbortWithStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&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;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// We use our custom CORS Middleware&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

    &lt;span class="c"&gt;// ... here the routes &lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loginController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&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;h2&gt;
  
  
  Custom CORS Middleware with Gorilla Mux:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// CORS Middleware&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&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;http&lt;/span&gt;&lt;span class="o"&gt;.&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;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c"&gt;// Set headers&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Headers:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Origin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Access-Control-Allow-Methods"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"OPTIONS"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Next&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeHTTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// We use our custom CORS Middleware&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// ... here the routes &lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loginController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&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;Note that you could use this "standard" Middelware package for Gorilla &lt;a href="https://www.gorillatoolkit.org/pkg/handlers" rel="noopener noreferrer"&gt;https://www.gorillatoolkit.org/pkg/handlers&lt;/a&gt; and this for Gin &lt;a href="https://github.com/gin-contrib/cors" rel="noopener noreferrer"&gt;https://github.com/gin-contrib/cors&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>go</category>
      <category>angular</category>
    </item>
    <item>
      <title>Upload files to Amazon S3 with Golang</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Fri, 01 Nov 2019 17:27:27 +0000</pubDate>
      <link>https://forem.com/asanchez/upload-files-to-amazon-s3-with-golang-13fl</link>
      <guid>https://forem.com/asanchez/upload-files-to-amazon-s3-with-golang-13fl</guid>
      <description>&lt;p&gt;Connecting your Go application with &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; is pretty easy and requires only a few lines of code. But if you have no experience with Amazon Web Services, it can get pretty tricky at the beginning to figure out even the most straightforward task. &lt;/p&gt;

&lt;p&gt;We are going to see here how to connect to S3 with Golang, upload a file from a form to an S3 bucket, download it, and list all items saved on this bucket. &lt;/p&gt;

&lt;p&gt;I am going to suppose that you have already:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;created an AWS account&lt;/li&gt;
&lt;li&gt;created an Amazon Simple Storage (S3) Bucket&lt;/li&gt;
&lt;li&gt;generated the credentials to access it (Access key ID and Secret access key)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am not going to explain those things here since there are already plenty of good tutorials on the Internet about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your credentials
&lt;/h2&gt;

&lt;p&gt;After having created the S3 Bucket, the very first thing that we are going to do is to save the credentials. &lt;/p&gt;

&lt;p&gt;There are a few ways of doing this as described here &lt;a href="https://docs.aws.amazon.com/es_es/sdk-for-go/v1/developer-guide/configuring-sdk.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/es_es/sdk-for-go/v1/developer-guide/configuring-sdk.html&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;We are going to use the easiest one using a shared credential file. For this, create a file like described below on &lt;code&gt;~/.aws/credentials&lt;/code&gt; on Linux, macOS or Unix. If using windows, create it on &lt;code&gt;C:\Users\USERNAME\.aws\credential&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;[default] 
aws_access_key_id = your_access_key_id 
aws_secret_access_key = your_secret_access_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install the SDK
&lt;/h2&gt;

&lt;p&gt;After this, we need to install de AWS SDK for Go:&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;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;/...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and import the AWS packages into your Go application:&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;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt; 
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt; 
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/s3"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;You can skip all explanations below and check the code directly on &lt;a href="https://github.com/antsanchez/goS3example" rel="noopener noreferrer"&gt;https://github.com/antsanchez/goS3example&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I have tried to keep the code as minimal as possible. &lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to AWS S3
&lt;/h2&gt;

&lt;p&gt;I like to use this code on my main.go file or whatever other file where I can share the session (&lt;code&gt;sess&lt;/code&gt; variable) to reuse it.&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;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connectAWS&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;connectAWS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_REGION&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;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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="n"&gt;sess&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upload a file from a form to Amazon S3
&lt;/h2&gt;

&lt;p&gt;First, get the file from the form:&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="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Do your error handling here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Filename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, upload it to S3:&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="n"&gt;uploader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewUploader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uploader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UploadInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c"&gt;// Bucket to be used&lt;/span&gt;
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c"&gt;// Name of the file to be saved&lt;/span&gt;
    &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="c"&gt;// File&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Do your error handling here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Download a file from Amazon S3
&lt;/h2&gt;

&lt;p&gt;First, create a temporary file where to store the downloaded file:&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="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Do your error handling here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, download the file and do whatever you want with it (&lt;code&gt;f&lt;/code&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="n"&gt;downloader&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDownloader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;downloader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetObjectInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Do your error handling here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  List the items of an AWS S3 Bucket
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListObjectsInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListObjects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&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;if&lt;/span&gt; &lt;span class="n"&gt;aerr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awserr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;aerr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrCodeNoSuchBucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrCodeNoSuchBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aerr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aerr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Print the error, cast err to awserr.Error to get the Code and&lt;/span&gt;
        &lt;span class="c"&gt;// Message from an error.&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// Do your error handling here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could now list the items showing the name like this:&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="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contents&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;li&amp;gt;File %s&amp;lt;/li&amp;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;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&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;h2&gt;
  
  
  More information
&lt;/h2&gt;

&lt;p&gt;There are plenty of tutorials on internet, but not all of them are updated or following the best practices. &lt;br&gt;
I would recommend you to check those links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/es_es/sdk-for-go/v1/developer-guide/setting-up.html" rel="noopener noreferrer"&gt;Getting Started with the AWS SDK for Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/es_es/sdk-for-go/v1/developer-guide/configuring-sdk.html" rel="noopener noreferrer"&gt;Configuring the AWS SDK for Go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that's all. &lt;/p&gt;

</description>
      <category>go</category>
      <category>aws</category>
      <category>s3</category>
    </item>
    <item>
      <title>The power of defer in Go: on entry</title>
      <dc:creator>Antonio Sánchez</dc:creator>
      <pubDate>Tue, 23 Apr 2019 08:36:00 +0000</pubDate>
      <link>https://forem.com/asanchez/the-power-of-defer-in-go-on-entry-43ol</link>
      <guid>https://forem.com/asanchez/the-power-of-defer-in-go-on-entry-43ol</guid>
      <description>&lt;p&gt;Here is a small tip I wish I would have learned earlier when I started coding with Golang:  using defer in both the start and exit of any function.  &lt;/p&gt;

&lt;p&gt;Although this is a very basic concept that can be extremely helpful when debugging code, it is difficult to find any information about it in most of the Golang tutorials you can find on the Internet. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://golang.org/ref/spec#Defer_statements" rel="noopener noreferrer"&gt;Defer&lt;/a&gt; is one of the first things you learn when starting to code with Go. It is pretty straightforward: inside any function, you prefix some other function with &lt;code&gt;defer&lt;/code&gt; to make sure this function is executed right before the parent function exits, no matter what. Even if the parent function panics, this defered function will run. &lt;/p&gt;

&lt;p&gt;In case you are not familiarized with &lt;code&gt;defer&lt;/code&gt;, take a look here before continuing reading this article: &lt;a href="https://gobyexample.com/defer" rel="noopener noreferrer"&gt;Go by Example: Defer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, there is something that you may haven't read on any of the examples or tutorials you have found: you can also use defer to pair code at the start and the end of any function. &lt;/p&gt;

&lt;p&gt;To use this, create a function and make it return another function itself, which will be the real deferred function. Defer this parent function and add extra parenthesis to it when deferring it, like this:&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="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()()&lt;/span&gt; 
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Some code here..."&lt;/span&gt;&lt;span class="p"&gt;)&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;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bye!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c"&gt;// this will be deferred&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which outputs this:&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="n"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="n"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;Bye&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function returned by the deferred function will be the real deferred function. The other code inside the deferred function will be executed right at the start of the function (or wherever you place it). &lt;/p&gt;

&lt;p&gt;Why is this awesome? Well, as you can see in the next example, you can use variables on the deferred function that can be accessed both on the first execution and the deferred execution:&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="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;otherExample&lt;/span&gt;&lt;span class="p"&gt;()&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;example&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"example"&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Some code here"&lt;/span&gt;&lt;span class="p"&gt;)&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;otherExample&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"otherExample"&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Some other code here"&lt;/span&gt;&lt;span class="p"&gt;)&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;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting function %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting function %s after %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which outputs this:&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="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;
&lt;span class="n"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt;
&lt;span class="n"&gt;Exiting&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="n"&gt;Starting&lt;/span&gt; &lt;span class="n"&gt;otherExample&lt;/span&gt;
&lt;span class="n"&gt;Some&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt;
&lt;span class="n"&gt;Exiting&lt;/span&gt; &lt;span class="n"&gt;otherExample&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can imagine, deferring code to use it on the entry and the exit of any function can be extremely useful, especially for debugging your code. &lt;/p&gt;

&lt;p&gt;You can see another example here: &lt;a href="https://github.com/adonovan/gopl.io/blob/master/ch5/trace/main.go" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/adonovan/gopl.io/blob/master/ch5/trace/main.go" rel="noopener noreferrer"&gt;https://github.com/adonovan/gopl.io/blob/master/ch5/trace/main.go&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
    </item>
  </channel>
</rss>
