<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Giovanni's Blog]]></title><description><![CDATA[Giovanni's Blog]]></description><link>https://blog.giovannijoao.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 13 May 2026 06:32:22 GMT</lastBuildDate><atom:link href="https://blog.giovannijoao.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to use an AWS Lambda function to invite users to GitHub private repositories]]></title><description><![CDATA[Probably you've faced a situation where you were applying to a new job and they asked to publish your code into GitHub so they can analyze it. I'm not judging them for doing this, it's a lot easier to read the code online than download some zip (that...]]></description><link>https://blog.giovannijoao.dev/how-to-use-an-aws-lambda-function-to-invite-users-to-github-private-repositories</link><guid isPermaLink="true">https://blog.giovannijoao.dev/how-to-use-an-aws-lambda-function-to-invite-users-to-github-private-repositories</guid><category><![CDATA[GitHub]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[AWS]]></category><category><![CDATA[lambda]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Joao Giovanni]]></dc:creator><pubDate>Thu, 19 Aug 2021 01:19:02 GMT</pubDate><content:encoded><![CDATA[<p>Probably you've faced a situation where you were applying to a new job and they asked to publish your code into GitHub so they can analyze it. I'm not judging them for doing this, it's a lot easier to read the code online than download some zip (that can't always be trusted) and open it on their local machine. But it's not nice to have your application public accessible. Don't worry, there's an easy and chip solution for that. </p>
<h1 id="requirements">Requirements</h1>
<ul>
<li>GitHub Account</li>
<li>AWS Account (also possible with free-tier)</li>
<li>NPM/YARN installed in your machine</li>
</ul>
<h1 id="creating-your-lambda">Creating your lambda</h1>
<p>First things first, what is a Lambda? Well, a Lambda is a piece of code that can be executed dynamically when necessary. It runs on a secure shared server and you'll only pay for what you use of RAM, execution time, and invocations. If you don't use it, you don't need to pay. Simple as that.</p>
<p>Let's search for it in AWS Console.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629243215712/EKOSqpB8C.png" alt="image.png" /></p>
<p>Now, it's time to create your Lambda function. Just click "Create function"</p>
<p>Just a small observation: I'm using the Ohio region just to keep everything organized in my account, but you can use whatever you want.  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629243288604/T1w0YPN4p.png" alt="image.png" /></p>
<p>On the opened screen, fill the field function name, and also make sure the Runtime field has the value "Node.js 14x". Once finished, just click "Create function" again.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629243553755/Bu4O36utc.png" alt="image.png" /></p>
<p>Now, you can see the function overview and also a code source. We'll be using it soon, but first, we need to create our layer containing the package module we'll use in this lambda. </p>
<blockquote>
<p>A Lambda layer is a .zip file archive that can contain additional code or data. A layer can contain libraries, a custom runtime, data, or configuration files. Layers promote code sharing and separation of responsibilities so that you can iterate faster on writing business logic. (<a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html">Creating and sharing Lambda layers
</a>)</p>
</blockquote>
<p>In your computer, create a folder with any name you want (I'll use <code>github-invite-users-endpoint</code>), and inside this folder, create another one named <code>nodejs</code>. The name of the inside folder needs to be exactly as mentioned before.</p>
<p>Enter to the <code>nodejs</code> folder and add the module <code>@octokit/rest</code> using the package manager of your choice. <code>yarn add @octokit/rest</code> or <code>npm install @octokit/rest</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629244090702/K5VRYIgJk.png" alt="image.png" /></p>
<p>Once the package was installed, <strong>zip</strong> the <code>nodejs</code> folder. You'll need it. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629246535753/GelhwWL3K.png" alt="image.png" /></p>
<p>Now, go back to the lambda page and scroll to the bottom to click <strong>Add layer</strong>, and 
 on the opened screen, click on the link <strong>create a new layer</strong> right above "Specify an ARN".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629244406575/cBHFvCbFc.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629244496525/kKYxwKcsC.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629246330910/wv6zP3J-p.png" alt="image.png" /></p>
<p>Now, enter the name of the layer (I'll use <code>github-invite-users-endpoint-node-modules</code>) and upload the .zip file we've created before. Select <strong>Node.js 14.x</strong> in <strong>Compatible runtimes</strong>. When you finish, create the layer.</p>
<p>Once created the layer, go back into the Lambda by clicking <strong>Functions</strong> in the left menu, and then open the previously created function.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629247310540/NaGOobBc_.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629247012086/JWVfrw1bx.png" alt="image.png" /></p>
<p>Now, let's add the layer we created into the function. Scroll to the bottom, and click <strong>Add a layer</strong>. Then, select <strong>Custom layers</strong> and in the Custom layers field, select the layer you created before. Once you selected, a version field will appear, select the first value of the field, and then click <strong>Add</strong> to conclude.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629247070151/BYou98w3t.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629247349181/DgtnXINYc.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629247449825/hus5Csq5z.png" alt="image.png" /></p>
<h1 id="addying-a-trigger-to-the-lambda">Addying a trigger to the Lambda</h1>
<p>Lambda functions are called by some triggers. Let's configure one called <em>API Gateway</em> that will provide us an endpoint to use the function. In the <strong>Function overview</strong>, click at <strong>Add trigger</strong> and then select <strong>API Gateway</strong> at the next screen field.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629249915678/XoKH9FAqT.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629249950126/Gc6EQZNt4.png" alt="image.png" /></p>
<p>In the trigger configuration, opt to <strong>Create an API</strong> of <strong>type HTTP API</strong>, and <strong>security Open</strong>. To conclude, click <strong>Add</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629250036578/TQ-LFTVnU.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629250205344/ZlV66jD-3.png" alt="image.png" /></p>
<p>If you click <strong>API Gateway</strong> right above the <strong>Add trigger button</strong>, you'll see the Triggers. Click at <strong>Details</strong> to get the <strong>API ENDPOINT</strong> for your Lambda. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629250447089/_eqYh7zRU.png" alt="image.png" /></p>
<p>Let's suppose your <strong>API ENDPOINT</strong> is <code>https://XXXXXXXXXX.execute-api.us-east-2.amazonaws.com/default/github-invite-users-endpoint</code>. To send an invite, you'll 
need to add two query params to the URL: <code>usernameToAuthorize</code> and <code>repository</code>. </p>
<p>Here is an example of the full URL: <code>https://XXXXXXXXXX.execute-api.us-east-2.amazonaws.com/default/github-invite-users-endpoint?usernameToAuthorize=YOUR_GITHUB_NAME&amp;repository=REPOSITORY_NAME</code>. You'll just need to replace the repository name and the username you want to authorize.</p>
<h1 id="environment-variables-configuration">Environment Variables configuration</h1>
<p>We need to use environment variables for better
We're going to configure two variables in the Lambda.</p>
<ul>
<li>github_owner_id: your github username</li>
<li>github_auth_token: a personal access token with the permission <code>repo: invite</code>. If you have any doubts, see  <a target="_blank" href="https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token">Creating a token</a>.</li>
</ul>
<p>To configure these variables, click in <strong>Configuration</strong> right above the Code source, then <strong>Environment variables</strong> and <strong>Edit</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629286749353/fzqiSNf5K.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629286895754/4SW0PXN9N.png" alt="image.png" /></p>
<p>Now, add the variables I mentioned before and <strong>Save</strong> them.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629333424497/dOooyqAIJ.png" alt="image.png" /></p>
<h1 id="coding-the-function">Coding the function</h1>
<p>After we finished all the configuration, let's code!</p>
<p>In the code source, put the following code:</p>
<pre><code><span class="hljs-keyword">const</span> { Octokit } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@octokit/rest"</span>);

<span class="hljs-keyword">const</span> { 
    github_owner_id,
    github_auth_token
} = process.env;

<span class="hljs-keyword">const</span> octokit = <span class="hljs-keyword">new</span> Octokit({
  <span class="hljs-attr">auth</span>: github_auth_token,
});

<span class="hljs-keyword">const</span> formatResponse = <span class="hljs-function">(<span class="hljs-params">statusCode, body</span>) =&gt;</span> ({
    statusCode,
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(body)
})

<span class="hljs-keyword">const</span> ALLOW_AUTHORIZATION_TO_REPOS = [<span class="hljs-string">"test-repository"</span>];

<span class="hljs-keyword">const</span> REPOSITORY_PERMISSION = <span class="hljs-string">"pull"</span>;
<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event) =&gt; {

    <span class="hljs-keyword">const</span> missingLambdaConfiguration = !github_owner_id || !github_auth_token;
    <span class="hljs-keyword">if</span> (missingLambdaConfiguration) <span class="hljs-keyword">return</span> formatResponse(<span class="hljs-number">500</span>, {
        <span class="hljs-attr">message</span>: <span class="hljs-string">'Missing lambda configuration'</span>
    });

    <span class="hljs-keyword">const</span> { queryStringParameters } = event;

    <span class="hljs-keyword">const</span> missingUsernameToAuthorize = !queryStringParameters?.usernameToAuthorize;
    <span class="hljs-keyword">if</span> (missingUsernameToAuthorize) <span class="hljs-keyword">return</span> formatResponse(<span class="hljs-number">400</span>, {
        <span class="hljs-attr">message</span>: <span class="hljs-string">'Missing query param: usernameToAuthorize'</span>
    });

    <span class="hljs-keyword">const</span> missingRepositoryName = !queryStringParameters?.repository;
    <span class="hljs-keyword">if</span> (missingRepositoryName) <span class="hljs-keyword">return</span> formatResponse(<span class="hljs-number">400</span>, {
        <span class="hljs-attr">message</span>: <span class="hljs-string">'Missing query param: repository'</span>
    });

    <span class="hljs-keyword">const</span> { <span class="hljs-attr">repository</span>: selectedRepository, usernameToAuthorize } = queryStringParameters

    <span class="hljs-keyword">const</span> selectedRepositoryNotAllowed = !ALLOW_AUTHORIZATION_TO_REPOS.includes(selectedRepository);
    <span class="hljs-keyword">if</span> (selectedRepositoryNotAllowed) <span class="hljs-keyword">return</span> formatResponse(<span class="hljs-number">401</span>, {
        <span class="hljs-attr">message</span>: <span class="hljs-string">'Unauthorized'</span>
    });

    <span class="hljs-keyword">await</span> octokit.repos.addCollaborator({
      <span class="hljs-attr">owner</span>: github_owner_id,
      <span class="hljs-attr">repo</span>: selectedRepository,
      <span class="hljs-attr">username</span>: usernameToAuthorize,
      <span class="hljs-attr">permission</span>: REPOSITORY_PERMISSION
    });

    <span class="hljs-keyword">return</span> formatResponse(<span class="hljs-number">200</span>, {
        <span class="hljs-attr">message</span>: <span class="hljs-string">`Sent invite to <span class="hljs-subst">${usernameToAuthorize}</span> access <span class="hljs-subst">${selectedRepository}</span>.`</span>
    });
};
</code></pre><h1 id="code-review">Code Review</h1>
<ul>
<li>You can access environment variables at <code>process.env</code>.</li>
<li>A Lambda function must export a constant function called <em>handler</em>: <code>exports.handler = async (event) =&gt; {</code>. </li>
<li><ul>
<li>This function should return an object with <strong>statusCode</strong> and a string <strong>body</strong>. To help with that, I created the function <code>formatResponse</code> that basically creates this object.</li>
</ul>
</li>
<li>The constant <code>ALLOW_AUTHORIZATION_TO_REPOS</code> is used to list all repository names that can have invitations sent. If you want to, you can change the value for a new environment variable you can create, and split the variable to make a list. Just like this: <code>const ALLOW_AUTHORIZATION_TO_REPOS = process.env.allow_authorization_to_repos.split(';')</code></li>
<li>If you missed some configuration, you'll get a message <code>Missing lambda configuration</code> as the response.</li>
<li>If the query params <em>usernameToAuthorize</em> or <em>repository</em> are not sent, a Bad Request error will be returned as a response.</li>
<li>If the received repository name is not included in the constant <code>ALLOW_AUTHORIZATION_TO_REPOS</code>, you'll get an Unauthorized error.</li>
<li>If everything is correct and worked properly, you'll receive a message informing you that the invite has been sent.</li>
</ul>
<h1 id="thats-it">That's it!</h1>
<p>From now on, you can send to whoever you want the endpoint and the instructions on how to use it. <strong>But</strong>, be careful with who or where you share it. It should never be public published. The reason for that is not only that <strong>ANYONE</strong> can access the repositories you allow but also the danger of receiving a lot of requests and exceeding the free tier usage in AWS.</p>
<p>Thanks very much. I hope you liked this tutorial. If you have any doubts or suggestions, please let me know! 
🚀</p>
]]></content:encoded></item><item><title><![CDATA[Who am I?]]></title><description><![CDATA[Hello, everyone.
I'm Giovanni, a Brazilian web developer. I'm currently working with JavaScript, Typescript in NodeJS, but I also have experience with front-end development using React and some other technologies you'll probably see in future posts.
...]]></description><link>https://blog.giovannijoao.dev/who-am-i</link><guid isPermaLink="true">https://blog.giovannijoao.dev/who-am-i</guid><category><![CDATA[introduction]]></category><dc:creator><![CDATA[Joao Giovanni]]></dc:creator><pubDate>Fri, 27 Nov 2020 01:34:20 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1606440789790/r_GXdbuf7.png" alt="imageedit_4_3341039871.png" /></p>
<h1 id="hello-everyone">Hello, everyone.</h1>
<p>I'm Giovanni, a Brazilian web developer. I'm currently working with JavaScript, Typescript in NodeJS, but I also have experience with front-end development using React and some other technologies you'll probably see in future posts.</p>
<p>If you want to contact me, you can find me on  <a target="_blank" href="https://github.com/giovannijoao">Github </a> or  <a target="_blank" href="https://www.linkedin.com/in/giovannijoao/">LinkedIn</a> by looking for giovannijoao. </p>
]]></content:encoded></item></channel></rss>