<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ netlify-functions - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ netlify-functions - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 28 Jul 2026 03:52:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/netlify-functions/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Hide API Keys in Frontend Apps using Netlify Functions ]]>
                </title>
                <description>
                    <![CDATA[ Netlify is a popular web development platform that makes it easier to build, deploy, and manage websites. You can use Netlify to host websites, and it helps you update and release new changes. It also provides additional features such as security, us... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/hide-api-keys-in-frontend-apps-using-netlify-functions/</link>
                <guid isPermaLink="false">66d45edbd1ffc3d3eb89ddd7</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ netlify-functions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Franklin Ohaegbulam ]]>
                </dc:creator>
                <pubDate>Tue, 07 Feb 2023 23:46:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/02/FCC-hide-API-keys-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Netlify is a popular web development platform that makes it easier to build, deploy, and manage websites.</p>
<p>You can use Netlify to host websites, and it helps you update and release new changes. It also provides additional features such as security, user authentication and authorization services, and more.</p>
<p>This guide focuses on showing you how to set up a Netlify serverless function to hide Application Programming Interface (API) keys in a client-side application.</p>
<p>For this lesson, you will create a stock photo search engine web application, deploy it on Netlify, and make an API call to the Pixabay API using Netlify serverless functions.</p>
<p>This is the same process for front-end applications built with ReactJS, NextJS, VueJS, Angular, or other JavaScript frameworks.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>To follow along with this tutorial, you should have the following:</p>
<ul>
<li><p>Netlify account (you can sign up <a target="_blank" href="http://netlify.com">here</a>)</p>
</li>
<li><p>Basic understanding of RESTful APIs, Lambda functions, and async/await concepts.</p>
</li>
</ul>
<p>The final demo app lives in the main branch on GitHub: <code>https://netlify-func-demo.netlify.app</code></p>
<h2 id="heading-what-is-a-netlify-function">What is a Netlify function?</h2>
<p>Netlify functions are serverless or lambda functions provided by Netlify. You use them to deploy server-side code or backend logic without the need for a dedicated server.</p>
<p>The purpose of this Netlify function is to manage serverless event-driven code and send HTTP requests that return a JSON response.</p>
<blockquote>
<p>"Serverless functions, branded as Netlify Functions when running on Netlify, are a way to deploy server-side code as API endpoints" - Netlify Docs</p>
</blockquote>
<p>It securely accesses environment variables behind the scenes via the Amazon Web Services (AWS) lambda function.</p>
<p>Secret credentials such as access tokens or API keys, hidden solely using environment variables, are less secure. This is because they can be easily retrieved from the Developer Tools through the API fetch request in the browser.</p>
<p>The API keys, if hijacked, can be misused by malicious actors, which might affect your app build threshold or cost you more if it's a paid API service.</p>
<p>Other serverless functions used for running code without having to manage servers include AWS Lambda functions, Azure functions, and Google cloud functions.</p>
<h2 id="heading-how-to-set-up-a-client-side-application">How to Set Up a Client-side Application</h2>
<h3 id="heading-how-to-clone-the-demo-app">How to Clone the Demo App</h3>
<p>To get started with this tutorial, you can clone the <strong>stock photo search engine app</strong> <a target="_blank" href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/main">GitHub repository</a>. See the live preview on Netlify at <a target="_blank" href="https://netlify-func-demo.netlify.app">https://netlify-func-demo.netlify.app</a>.</p>
<p>The first step is to clone the repository:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/frankiefab100/netlify-serverless-functions-demo.git
</code></pre>
<p>Next, change to the <strong>netlify-serverless-functions-demo</strong> directory.</p>
<pre><code class="lang-bash"> <span class="hljs-built_in">cd</span> netlify-serverless-functions-demo
</code></pre>
<p>Then you'll need to install dependencies.</p>
<pre><code class="lang-bash">npm install
<span class="hljs-comment">#OR </span>
yarn add
</code></pre>
<p>Now run the development server. Run the following command to start the app on the server:</p>
<pre><code class="lang-bash">netlify dev
</code></pre>
<p>The app will be ready on <code>https://localhost:8888</code>.</p>
<p>Alternatively, you can skip the above steps if you wish to follow along by building the app from scratch. In the next step, you will build a stock photo search engine JavaScript application.</p>
<h3 id="heading-how-to-build-the-demo-app-using-javascript">How to Build the Demo App using JavaScript</h3>
<p>The first step is to set up a front-end app. Open your favorite code editor, such as VS Code.</p>
<p>Then, create a <strong>dist</strong> directory and inside it create an <strong>index.html</strong> file. Populate it with the following code:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">http-equiv</span>=<span class="hljs-string">"X-UA-Compatible"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"IE=edge"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"style.css"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Stock Photos Search Engine<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Search For Stock Photos<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"search-section"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"search"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"search"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter a keyword"</span>
          /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"searchBtn"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"search-btn"</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">"Search"</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"photo-wrapper"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">""</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"photo"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./script.js"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>In the same <code>dist</code> directory, add the following styling to style.css:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* dist/style.css */</span>
* {
  <span class="hljs-attribute">box-sizing</span>: border-box;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#222</span>;
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">"Roboto"</span>, sans-serif;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100vw</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
}

<span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">100vw</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-class">.search-section</span> {
  <span class="hljs-attribute">display</span>: inline;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">310px</span>;
}

<span class="hljs-selector-class">.search</span>,
<span class="hljs-selector-class">.search-btn</span> {
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">50px</span>;
}

<span class="hljs-selector-class">.search</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#d1f3bf</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#222</span>;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">225px</span>;
}

<span class="hljs-selector-class">.search-btn</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#04ab04</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#f6f6f6</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">min-width</span>: <span class="hljs-number">80px</span>;
  <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.3s</span> ease-in-out;
}

<span class="hljs-selector-class">.search-btn</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2dc22d</span>;
}

<span class="hljs-selector-class">.photo-wrapper</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">gap</span>: <span class="hljs-number">15px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">30px</span>;
}

<span class="hljs-selector-class">.photo-wrapper</span> <span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">200px</span>;
}
</code></pre>
<h2 id="heading-sign-up-for-a-free-account-on-pixabay">Sign Up for a Free Account on Pixabay</h2>
<p>The first step to using the <a target="_blank" href="https://pixabay.com/api/docs/">Pixabay API</a> is to sign up for an account with your email.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Pixabay-API-Documentation.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Pixabay API key section</em></p>
<p>As shown in the image above, your API key can be found below the <strong>parameters</strong> section in the <a target="_blank" href="https://pixabay.com/api/docs/">Pixabay API Docs website</a>.</p>
<h3 id="heading-create-an-environment-variable">Create an environment variable</h3>
<p>Environment variables (commonly known as "env") are combinations of key/value pairs that can affect the behavior and processes of an operating system or application.</p>
<p>Using environment variables is recommended to configure third-party services and their credentials during development.</p>
<h3 id="heading-install-dotenv">Install dotenv</h3>
<p>Once you complete the account creation on Pixabay, open your terminal and install <strong>dotenv</strong> as a package. This will enable your app to read environment variables saved in the <strong>.env</strong> file.</p>
<pre><code class="lang-javascript">npm install dotenv
#OR
yarn add -D dotenv
</code></pre>
<p>In the next step, you will save the API key in a <strong>.env</strong> file.</p>
<h3 id="heading-create-the-env-file">Create the .env file</h3>
<p>In the root directory of your app, create a <strong>.env</strong> file and store the API keys copied from your Pixabay Profile.</p>
<pre><code class="lang-plaintext">PIXABAY_API_KEY=123456-7890
</code></pre>
<p>Where <code>PIXABAY_API_KEY=123456-7890</code> represents the API key value.</p>
<p><strong>Note:</strong> Replace this key/value pair with the appropriate value.</p>
<h3 id="heading-create-a-gitignore-file">Create a .gitignore file</h3>
<p>To avoid committing sensitive files and values such as <code>node_modules</code> and <code>secret keys</code> to a public repository, create a <strong>.gitignore</strong> file in the same project root directory and add the following to it:</p>
<pre><code class="lang-plaintext">node_modules
.env
.netlify
</code></pre>
<p>The <strong>.netlify</strong> folder which contains compiled serverless functions together with other files listed will be excluded when the project is pushed to GitHub or any other version control system.</p>
<h3 id="heading-create-a-get-request-function">Create a get request function</h3>
<p>Now, you should add the fetch request logic in the <strong>script.js</strong>. You will adjust the API logic later using Netlify functions.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/* dist/script.js */</span>
<span class="hljs-keyword">const</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

<span class="hljs-keyword">const</span> searchbar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search"</span>);
<span class="hljs-keyword">const</span> submitBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search-btn"</span>);
<span class="hljs-keyword">const</span> photoWrapper = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".photo-wrapper"</span>);

submitBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
  getPhoto(searchbar.value);
  searchbar.value = <span class="hljs-string">""</span>;
});

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.keyCode === <span class="hljs-number">13</span>) {
    getPhoto(searchbar.value);
    searchbar.value = <span class="hljs-string">""</span>;
  }
});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPhoto</span>(<span class="hljs-params">keyword</span>) </span>{
  <span class="hljs-keyword">const</span> apiKey = PIXABAY_API_KEY;
  <span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${apiKey}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>;

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(apiURL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">headers</span>: { <span class="hljs-attr">accept</span>: <span class="hljs-string">"application/json"</span> },
    });
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

    <span class="hljs-keyword">let</span> imageURL = data.hits;

    imageURL.forEach(<span class="hljs-function">(<span class="hljs-params">result</span>) =&gt;</span> {
      <span class="hljs-keyword">let</span> imageElement = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"img"</span>);
      imageElement.setAttribute(<span class="hljs-string">"src"</span>, <span class="hljs-string">`<span class="hljs-subst">${result.webformatURL}</span>`</span>);
      photoWrapper.appendChild(imageElement);
    });
  } <span class="hljs-keyword">catch</span> (error) {
    alert(error);
  }
}
</code></pre>
<p><strong>Note:</strong> As mentioned earlier, if the codebase of this app is published on GitHub. The API key will still be accessible from the client side on a browser, although the <code>.env</code> file that contains the secret key was excluded.</p>
<p>To illustrate this, select the <a target="_blank" href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/testing"><code>testing</code> branch</a> of this app repository. The <a target="_blank" href="https://testing--netlify-func-demo.netlify.app/">Live site preview</a> will display the following Reference Errors in your browser console:</p>
<pre><code class="lang-bash">Uncaught ReferenceError: require is not defined
Uncaught ReferenceError: require is not defined at getPhotos
Uncaught ReferenceError: process is not defined at getPhotos
</code></pre>
<p>This is because there is no way to reference the environment variables specified in the <strong>.env</strong> file, since they weren't committed to the public repository on GitHub.</p>
<p>In the next step, select and clone the <code>[testing](https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/testing)</code> branch on your local machine with the following commands:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/frankiefab100/netlify-serverless-functions-demo.git
<span class="hljs-built_in">cd</span> netlify-serveless-functions-demo
npm install
netlify dev
</code></pre>
<p>The app should launch on your browser via <code>localhost:8888</code>.</p>
<p>Now, go to the <strong>Developer tools,</strong> right-click and select <strong>Inspect</strong>. Alternatively, press the <strong>F12</strong> key. Then, navigate to <strong>Network tab</strong> and tab on the <code>getPhotos.js</code> request URL.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--124----Copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>API key displayed in the Network tab in Developer tools</em></p>
<p>You should see the API key publicly exposed in the <strong>Network tab</strong>'s <strong>Headers</strong> section and return as a response data in your browser.</p>
<p>This is a security issue since the Network tab in the Developer tools is typically responsible to display informations such as the request URL, response status, and response data.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--127----Copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>API key returned as a response data and exposed in the URL of the request</em></p>
<p>In the next section, you will find a way to secure the API key using Netlify functions.</p>
<h2 id="heading-how-to-get-started-with-netlify-functions">How to Get Started with Netlify Functions</h2>
<p>First, you'll need to go into your terminal and install <strong>Netlify CLI</strong> and <strong>Lambda</strong> as Devdependencies. You can do that by running this command:</p>
<pre><code class="lang-bash">npm install -g netlify-cli netlify-lambda
<span class="hljs-comment">#OR </span>
yarn add -D netlify-cli netlify-lambda --save-dev
</code></pre>
<h3 id="heading-add-custom-build-and-development-commands-in-packagejson">Add custom build and development commands in package.json</h3>
<p>These commands build and start the app in the server and also launch the app on your web browser. Here's an example of how you might add these script commands in the <strong>package.json</strong> file:</p>
<pre><code class="lang-bash"><span class="hljs-string">"scripts"</span>: {
   <span class="hljs-string">"build"</span>: <span class="hljs-string">"npm run-script"</span>,
   <span class="hljs-string">"dev"</span>: <span class="hljs-string">"netlify dev"</span>
 }
</code></pre>
<h3 id="heading-install-axios">Install Axios</h3>
<p>You will use the <code>axios.get</code> method, because it is a node function unlike the <code>fetch</code> method that is intended for browser runtime.</p>
<p>To install Axios, open your terminal and enter the command:</p>
<pre><code class="lang-bash">npm install axios
<span class="hljs-comment">#OR</span>
yarn add -D axios
</code></pre>
<p>In this case, you are working with a vanilla JavaScript app, so you should import Axios in the <code>getPhotos.js</code> file as:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">"axios"</span>);
</code></pre>
<p>For JavaScript libraries, like React, import it as follows:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
</code></pre>
<h3 id="heading-create-a-serverless-function">Create a serverless function</h3>
<p>In the root of the project, create a folder named <code>netlify,</code>and inside it create another folder <code>functions</code>. In this <code>functions</code> directory, create a file named <code>getPhotos.js</code>.</p>
<p>You will create a serverless function in the <code>getPhotos</code>. This will completely hide the API keys while fetching images from the <a target="_blank" href="https://pixabay.com/api">Pixabay API</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//netlify/functions/getPhotos.js </span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">"dotenv"</span>).config();

<span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">"axios"</span>);

<span class="hljs-built_in">exports</span>.handler = <span class="hljs-keyword">async</span> (event, context) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { keyword } = event.queryStringParameters;
    <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> axios.get(
      <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${process.env.PIXABAY_API_KEY}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>,
      {
        <span class="hljs-attr">headers</span>: { <span class="hljs-attr">Accept</span>: <span class="hljs-string">"application/json"</span>, <span class="hljs-string">"Accept-Encoding"</span>: <span class="hljs-string">"identity"</span> },
        <span class="hljs-attr">params</span>: { <span class="hljs-attr">trophies</span>: <span class="hljs-literal">true</span> },
      }
    );

    <span class="hljs-keyword">let</span> imageURL = response.data.hits;

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ imageURL }),
    };
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">statusCode</span>: <span class="hljs-number">500</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ error }),
    };
  }
};
</code></pre>
<p>Here, the <code>process.env.PIXABAY_API_KEY</code> references API key environment configuration specified in the <code>.env</code> file for development mode.</p>
<p>The <code>keyword</code> parameter accepts a string accessible in the <code>queryStringParameters</code> property and returns a response data stored in the variable <code>imageURL</code>. This will get passed to the <code>script.js</code> as request response (we'll discuss this later).</p>
<p>If the GET request is successful, it returns a response of <code>statusCode</code> 200 with the corresponding response as a JSON object. For errors, we will get an alert with the error message and status code.</p>
<p>Due to changes in version, Axios might return Buffer as a response in your terminal window, that looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-data.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Buffer response from Axios in Terminal</em></p>
<p>To prevent this, you should attach the following to the GET request:</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> axios.get(
      <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${process.env.PIXABAY_API_KEY}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>,
      {
        <span class="hljs-attr">headers</span>: { <span class="hljs-attr">Accept</span>: <span class="hljs-string">"application/json"</span>, <span class="hljs-string">"Accept-Encoding"</span>: <span class="hljs-string">"identity"</span> },
        <span class="hljs-attr">params</span>: { <span class="hljs-attr">trophies</span>: <span class="hljs-literal">true</span> },
      }
    );
</code></pre>
<h3 id="heading-create-a-netlify-configuration-file">Create a Netlify configuration file</h3>
<p>In the project root directory, create a <code>netlify.toml</code> file. This file specifies how Netlify builds and deploys your app.</p>
<p>Now, add the following build configurations in <code>netlify.toml</code>:</p>
<pre><code class="lang-bash">[build]
  <span class="hljs-built_in">command</span> = <span class="hljs-string">"npm run build"</span>
  <span class="hljs-built_in">functions</span> = <span class="hljs-string">"netlify/functions"</span>
  publish = <span class="hljs-string">"dist"</span>
</code></pre>
<p><strong>Note:</strong></p>
<ul>
<li><p><code>command = "npm run build"</code> triggers the Netlify CLI to build the app from the functions.</p>
</li>
<li><p><code>functions = "netlify/functions"</code> indicates that the <code>getPhotos</code> functions exist in the <code>netlify/functions</code> directory.</p>
</li>
<li><p><code>publish = "dist"</code> identifies <code>dist</code> as the directory where the file will be served from.</p>
</li>
</ul>
<h3 id="heading-update-the-scriptjs-file-with-the-netlify-functions-request-url">Update the script.js file with the Netlify functions request URL</h3>
<p>Next, update the <code>apiURL</code> from this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`https://pixabay.com/api/?key=<span class="hljs-subst">${apiKey}</span>&amp;q=<span class="hljs-subst">${keyword}</span>&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`</span>;
</code></pre>
<p>to the functions HTTP request endpoint:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`/.netlify/functions/getPhotos?keyword=<span class="hljs-subst">${keyword}</span>`</span>;
</code></pre>
<p>This serverless function will get queried to the client-side of your app through the endpoint: <code>/.netlify/functions/getPhotos</code>. Once a fetch request is sent, the <code>getphotos</code> function will be invoked and accessed in the <code>script.js.</code></p>
<p>The <code>getPhotos</code> Netlify functions' response <code>imageURL</code> will be passed and the data accessed as the value of the <code>keyword</code> parameter in the query string of function. It will get loop through to return three images from the Pixabay API to the client-side.</p>
<p>The <strong>script.js</strong> file should look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/* dist/script.js */</span>
<span class="hljs-keyword">const</span> searchbar = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search"</span>);
<span class="hljs-keyword">const</span> submitBtn = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".search-btn"</span>);
<span class="hljs-keyword">const</span> photoWrapper = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">".photo-wrapper"</span>);

submitBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function">() =&gt;</span> {
  getPhoto(searchbar.value);
  searchbar.value = <span class="hljs-string">""</span>;
  photoWrapper.innerHTML = <span class="hljs-string">""</span>;
});

<span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"keydown"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (e.keyCode === <span class="hljs-number">13</span>) {
    getPhoto(searchbar.value);
    searchbar.value = <span class="hljs-string">""</span>;
    photoWrapper.innerHTML = <span class="hljs-string">""</span>;
  }
});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPhoto</span>(<span class="hljs-params">keyword</span>) </span>{
  <span class="hljs-keyword">let</span> apiURL = <span class="hljs-string">`/.netlify/functions/getPhotos?keyword=<span class="hljs-subst">${keyword}</span>`</span>;

  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(apiURL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
      <span class="hljs-attr">headers</span>: { <span class="hljs-attr">accept</span>: <span class="hljs-string">"application/json"</span> },
    });
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; data.imageURL.length; i++) {
      <span class="hljs-keyword">let</span> imageElement = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"img"</span>);
      imageElement.setAttribute(<span class="hljs-string">"src"</span>, <span class="hljs-string">`<span class="hljs-subst">${data.imageURL[i].webformatURL}</span>`</span>);
      photoWrapper.appendChild(imageElement);
    }
  } <span class="hljs-keyword">catch</span> (error) {
    alert(error);
  }
}
</code></pre>
<p><strong>Note:</strong> From the codebase above, your environment variable is secure since it is accessed from the serverless function.</p>
<h3 id="heading-run-the-development-server">Run the development server</h3>
<p>Now, execute your app by running the command:</p>
<pre><code class="lang-bash">netlify dev
</code></pre>
<p>This will load the <strong>getPhotos</strong> function via <code>https://localhost:8888/.netlify/functions/getPhotos.</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-dev-terminal.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>netlify build terminal output</em></p>
<p>Then, start the development server and launch the application in your default web browser on <code>localhost:8888</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-success2.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>netlify function ready on https://localhost:8888</em></p>
<p>At this point, the Netlify function is fully setup, and you can now proceed to make <code>GET</code> HTTP requests.</p>
<h3 id="heading-how-to-send-fetch-requests">How to Send Fetch Requests</h3>
<p>Now that you already have the web app served, go ahead and send a fetch request. Enter some text in the search input field and hit <strong>Enter</strong> or click the button to fetch images from the Pixabay API.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Stock-Photos-Search-Engine--1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>flower images fetched from Pixabay API</em></p>
<p>For more information about the Pixabay API, see the <a target="_blank" href="https://pixabay.com/api/docs">Pixabay documentation</a>.</p>
<p>The preceding command will send a request to the serverless function and then return six responses. Here's what the response looks like in your terminal windows:</p>
<blockquote>
<p>Request from ::1: GET /.netlify/functions/getPhotos?keyword=flower<br>Response with status 200 in 4895 ms.</p>
</blockquote>
<p>You can also use <strong>Developer tools</strong> through the <strong>Network</strong> tab to validate the request.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--118----Copy.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>serverless function API response data</em></p>
<p>The fetch request returns our app URL, the <strong>getPhotos</strong> Netlify function, script.js and the images from Pixabay.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--120-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Netlify function fetch response from Network tab's header section in the browser</em></p>
<h2 id="heading-how-to-host-the-app-on-the-remote-repository">How to Host the App on the Remote Repository</h2>
<p>Now, you should push your project to GitHub version control.</p>
<pre><code class="lang-javascript">git add .
git commit -m<span class="hljs-string">"initial commit"</span>
git push origin -u main
</code></pre>
<h2 id="heading-how-to-deploy-the-app-and-serverless-function-on-netlify">How to Deploy the App and Serverless Function on Netlify</h2>
<p>Once you have published the project on GitHub, log in to <a target="_blank" href="https://app.netlify.com">Netlify</a> and connect your GitHub account by granting authorization.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--130-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Import project for deployment on Netlify</em></p>
<p>As shown in the image above, click the <code>Add A New Project</code> and then search for the app repository from the list. Next, click on <code>Build Your Site</code>. This will take a few minutes to complete.</p>
<p>You just deployed the app from the Netlify UI. Your app is now ready at: <code>https://netlify-func-demo.netlify.app.</code>.</p>
<p>The fetch request URL should look like this: <code>https://netlify-func-demo.netlify.app/.netlify/functions/getPhotos</code>.</p>
<h2 id="heading-optional-how-to-configure-the-netlify-app-from-the-dashboard">Optional - How to Configure the Netlify App from the Dashboard</h2>
<p>Alternatively, you can configure the Netlify command from the Netlify Dashboard. If these settings are already specified in <strong>netlify.toml</strong>, it will override any corresponding configuration.</p>
<p>First, select the project directory's Site settings.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Site-overview-netlify-func-demo1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Site settings for project directory on Netlify</em></p>
<h3 id="heading-set-the-build-command-and-publish-directory">Set the Build command and Publish directory</h3>
<p>Navigate to <code>Site settings</code> &gt; <code>Deploy</code> &gt; <code>Build &amp; deploy</code> and edit the following commands and then save the changes:</p>
<ul>
<li><p>Build command: <strong>npm run build</strong></p>
</li>
<li><p>Publish directory: <strong>dist</strong></p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--132-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Build and deploy section on Netlify</em></p>
<h3 id="heading-set-the-functions-directory">Set the Functions directory</h3>
<p>By default, Netlify uses <code>netlify/functions</code> as the directory to find the functions to deploy them. In our case, our serverless function <code>getPhotos</code> can be found in the <code>netlify/functions</code> directory.</p>
<p>Now we'll set a custom functions directory so Netlify can find your compiled functions. Go to <code>Site settings</code> &gt; <code>Functions</code> and enter the directory path where the functions are stored in your repository.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--131-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Functions directory section on Netlify</em></p>
<h3 id="heading-how-to-set-environment-variables-for-production">How to Set Environment Variables for Production</h3>
<p>In the Netlify Dashboard, click on <code>Site settings</code> &gt; <code>Build &amp; deploy</code> &gt; <code>Environment</code> &gt; <code>Environment variables</code> and then configure KEY/VALUE pairs as follows:</p>
<pre><code class="lang-plaintext">PIXABAY_API_KEY=your-api-key-here
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--133-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Environment variables section on Netlify</em></p>
<p>Click on Save, and then redeploy your app so the changes can be added.</p>
<p>To redeploy the app on Netlify, navigate to <code>Deploys</code> &gt; <code>Trigger deploy</code>. Next, select <code>Clear cache and redeploy site</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--134-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Trigger redeployment tab on Netlify</em></p>
<p><strong>Note:</strong> The environment variable (PIXABAY_API_KEY) name should match the one mentioned in the <code>getPhotos</code> codebase.</p>
<p>For a React application, prepend the API environment variable with the prefix <code>REACT_APP</code> so they can be read from the <code>.env</code> file.</p>
<pre><code class="lang-plaintext"> REACT_APP_PIXABAY_API_KEY=your-api-key-here
</code></pre>
<h2 id="heading-how-to-initialize-the-serverless-function-to-the-remote-app">How to Initialize the Serverless Function to the Remote App</h2>
<p>To connect your project directory to the existing web app deployed on Netlify, login to your Netlify account from the terminal:</p>
<pre><code class="lang-bash">netlify login
</code></pre>
<p>Next, initialize the app on Netlify by running the command in your terminal:</p>
<pre><code class="lang-bash">netlify init
</code></pre>
<p>Your app is now configured for continuous deployment via Netlify.</p>
<h2 id="heading-how-to-build-the-netlify-serverless-function">How to Build the Netlify Serverless Function</h2>
<p>You need to link your app to site ID on Netlify before running the build command on your terminal. To connect your local project folder to its site ID on Netlify, enter the command in your terminal:</p>
<pre><code class="lang-bash">netlify link
</code></pre>
<p>This will prompt you to link the folder to a site through any of the following options:</p>
<ul>
<li><p>Search by full or partial site name</p>
</li>
<li><p>Choose from a list of your recently updated sites</p>
</li>
<li><p>Enter a site ID</p>
</li>
</ul>
<p>Once you select your preferred option, it will connect the project folder to the hosted site on Netlify. This allows you run <strong>Netlify CLI</strong> commands and also automatically deploy the project repository once there are any changes in the codebase.</p>
<p>In the next step, you will build a serverless function while it is running on the server. To activate the build command defined in <code>netlify.toml</code> file, run the following command:</p>
<pre><code class="lang-bash">netlify build
</code></pre>
<p>This runs the <code>npm run-script</code> command under the hood as specified in the <code>package.json</code>. Now, your serverless function in <code>netlify/functions</code> directory is packaged and bundled successfully!</p>
<h2 id="heading-how-to-test-the-application">How to Test the Application</h2>
<p>To test and confirm that the Netlify function is working fine, run this command on your terminal:</p>
<pre><code class="lang-bash">netlify <span class="hljs-built_in">functions</span>:serve
</code></pre>
<p>This injects your project environment variables from the <strong>.env</strong> file and runs the serverless function.</p>
<h3 id="heading-how-to-confirm-api-keys-security">How to Confirm API Keys Security</h3>
<p>To inspect your app and confirm that the API keys are hidden from the public, follow the steps below:</p>
<p>Click on your hosted app URL, and navigate to <strong>Developer tools</strong> by pressing the <strong>F12</strong> key or right-clicking and selecting <strong>Inspect</strong>. Navigate to <strong>Network</strong> tab, where you should see the fetched data from the Pixabay API.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--128-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>API key hidden from the public and unauthorized parties after inspection through the Developer tools</em></p>
<p>Now you've confirmed that you've successfully configured a serverless function and have it deployed on Netlify.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This tutorial introduced serverless functions, asynchronous JavaScript, and Restful API concepts.</p>
<p>Hopefully you now know how to create a serverless/lambda function and secure any sensitive value such as API keys in your frontend JavaScript application.</p>
<p>If you got stuck with anything, you can access the complete source code in this <a target="_blank" href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/main">GitHub repository</a>.</p>
<p>Thank you for reading! If you have any questions, feel free to reach out to me via <a target="_blank" href="https://twitter.com/frankiefab100">Twitter</a>.</p>
<h2 id="heading-relevant-links"><strong>Relevant Links</strong></h2>
<ul>
<li><p><a target="_blank" href="https://docs.netlify.com/functions/overview/">Netlify Functions</a></p>
</li>
<li><p><a target="_blank" href="https://www.netlify.com/blog/intro-to-serverless-functions/">Netlify Intro to Serverless Functions</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/sdras/netlify-functions-example">Netlify Functions Example</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I got Netlify Functions, Firebase, and GraphQL to work together at last ]]>
                </title>
                <description>
                    <![CDATA[ By Jeff M Lowery In a previous post I confessed defeat in attempting to get an AWS Lambda GraphQL server to connect to a Firebase server. I didn’t give up right away, though, and a short time later found a different Node package to achieve what I cou... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/netlify-functions-firebase-and-graphql-working-together-at-last/</link>
                <guid isPermaLink="false">66d45f7e787a2a3b05af43b2</guid>
                
                    <category>
                        <![CDATA[ Firebase ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ netlify-functions ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 11 Nov 2019 13:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/11/levi-guzman-zdSoe8za6Hs-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jeff M Lowery</p>
<p>In a <a target="_blank" href="https://www.freecodecamp.org/news/you-cant-get-there-from-here-how-netlify-lambda-and-firebase-led-me-to-a-serverless-dead-end/">previous post</a> I confessed defeat in attempting to get an <a target="_blank" href="https://www.netlify.com/products/functions/">AWS Lambda</a> GraphQL server to connect to a <a target="_blank" href="https://firebase.google.com/docs">Firebase</a> server. I didn’t give up right away, though, and a short time later found a <a target="_blank" href="https://www.npmjs.com/package/firebase-admin">different Node package</a> to achieve what I couldn’t before.</p>
<p>Why use AWS Lambda to host a GraphQL server? Scalability would be the obvious reason, but I did it to learn.</p>
<h1 id="heading-and-i-learned-a-lot">And I learned a lot</h1>
<p>Especially when it came to deployment. I used Netlify Functions to manage the deployment of both the AWS Lambda functions and the React client that calls them. There was more to this than I originally thought.</p>
<p>There are several ways to deploy a project using Netlify:</p>
<h2 id="heading-zip-it-and-ship-it">zip-it-and-ship-it</h2>
<p>This is <a target="_blank" href="https://github.com/netlify/zip-it-and-ship-it">a utility</a> that works a lot like <a target="_blank" href="https://webpack.js.org/">webpack</a>: for each function, it creates an archive file which bundles the function along with its dependencies. Like webpack, it only pulls in dependencies that are actually required by the function.</p>
<p>Netlify expects /functions folder by convention. When writing new functions, its source is at the same level as any NodeJS modules it needs as dependencies. If you add a new function that has new module dependencies, then they go into the node_modules folder (using <code>yarn add</code> or npm<code>install --save</code>).</p>
<p>The following shows two lambda functions along with a single node_modules folder:</p>
<p><img src="https://www.jeffamabob.com/media/screenshot-2019-11-05-at-3.45.48-pm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To use zip-it-and-ship-it, you write a simple JavaScript program that is called by the package.json build script.</p>
<p><strong>zipIt.js</strong></p>
<pre><code class="lang-text">const { zipFunctions } = require('@netlify/zip-it-and-ship-it')zipFunctions('functions', 'functions-dist')
</code></pre>
<p>And this would be invoked something like so:</p>
<p><strong>package.json</strong></p>
<pre><code class="lang-text">"build": "npm-run-all build:*","build:app": "react-scripts build","build:functions": "node ./zipFuncs.js",
</code></pre>
<p>Once built, several .zip files are generated . Those archives contain the function code as well as a node_modules folder which is not the original, but contains only those dependencies needed by each function:</p>
<p><img src="https://www.jeffamabob.com/media/screenshot-2019-11-05-at-3.52.56-pm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-netlify-lambda">netlify-lambda</h2>
<p>This deployment mechanism is similar to the above, but uses <strong>babel</strong> and <strong>webpack</strong> to perform its duties. If your comfortable and familiar with webpack, this might be the deployment option for you.</p>
<h2 id="heading-continuous-deployment">continuous deployment</h2>
<p>The continuous deployment option is available if you’re using one of the supported repositories (GitHub, GitLab, or Bitbucket). Once you push changes to your repository, Netlify is notified and will run your build processes, which may involve zip-it-and-ship-it or netlify-lambda…but you also have the option of deploying unbundled functions to your repository, and <a target="_blank" href="https://github.com/netlify/netlify-lambda/issues/142#issuecomment-483880089">Netlify will use zip-it-and-ship-it</a> behind the scenes.</p>
<h2 id="heading-netlify-cli">Netlify CLI</h2>
<p>The Netlify CLI offers yet another way to deploy, without much fuss or mystery. There are two main deployment options:</p>
<ul>
<li><code>netlify deploy</code> will push the <a target="_blank" href="https://docs.netlify.com/cli/get-started/#manual-deploys">local project to a Netlify server.</a> You first have to invoke the build step locally, though.</li>
<li><code>netlify dev</code> <a target="_blank" href="https://github.com/netlify/cli/blob/master/docs/netlify-dev.md">creates a local server</a>, along with a proxy to your lambda functions, and kicks off the application. It does <strong>not</strong> require a build step.</li>
</ul>
<p>There is also a script to help you create your lambda functions: <code>netlify function:create</code>. If you use this method, you will get a folder structure different than shown previously:</p>
<p><img src="https://www.jeffamabob.com/media/screenshot-2019-11-05-at-4.11.21-pm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this case, each function has its own folder, along with a node_modules and package.json file (plus others not shown, such as .lock files). Similar to what would be in the .zip archives that zip-it-and-ship-it creates.</p>
<p>Now, if you do generate your lambdas this way, continuous deployment will break, as <strong>zip-it-and-ship-it</strong> doesn’t handle this folder structure by itself. You can put something like this in your build script to fix continuous deployment:</p>
<pre><code class="lang-text">"build": "npm-run-all build:*",
"build:app": "react-scripts build",
"build:functions": "yarn --cwd functions/func1 install",
"build:functions": "yarn --cwd functions/func2 install",
</code></pre>
<p>These build steps will install the dependencies needed by each lambda.</p>
<h1 id="heading-the-example">The Example</h1>
<p>Your homework assignment is to set up a <a target="_blank" href="https://www.netlify.com/">Netlify account</a> and a <a target="_blank" href="https://firebase.google.com/">Firebase account</a>. The app will use <a target="_blank" href="https://docs.netlify.com/visitor-access/identity/#enable-identity-in-the-ui">Netlify Identity</a> to login to the Netlify service. You will also need to grab a <a target="_blank" href="https://firebase.google.com/docs/admin/setup#add_firebase_to_your_app">Firebase credentials JSON file</a> and put it somewhere in your project (the example uses fake-creds.json, which is <strong>FAKE</strong>, so won’t work).</p>
<h2 id="heading-about-the-application">About the application</h2>
<p>Being a geek, I’m building a database of chess openings. I have an openings book in a JSON file, from which I’ll load the database. In this somewhat contrived example, the book is actually stored with the lambda function in the /functions/pgnfen folder (<code>netlify function:create pgnfen</code>), however the loading is triggered by the React client through a GraphQL mutation call.</p>
<h2 id="heading-creating-the-lambda-function">Creating the lambda function</h2>
<p>I used <a target="_blank" href="https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-lambda">apollo-server-lambda</a> add a GraphQL API frontend to the Firestore database. To talk to Firestore, I use <a target="_blank" href="https://www.npmjs.com/package/firebase-admin">firebase-admin</a>. When using <code>netlify function:create</code> to create your function, it will ask what template to use; in this case, the correct choice is in blue:</p>
<p><img src="https://www.jeffamabob.com/media/screenshot-2019-11-05-at-4.39.37-pm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In addition, I have added some utility functions and supporting GraphQL schema and resolvers.</p>
<h3 id="heading-the-lambda-function">The lambda function</h3>
<p>The core of the whole operation is in <code>/functions/pgnfen.js</code>. It creates the server, logs into firebase, makes the necessary GraphQL declarations, and finally invokes the handler function that receives requests from the client and passes them on to the database via GraphQL resolvers:</p>
<pre><code class="lang-js"><span class="hljs-comment">/* eslint-disable no-unused-vars */</span>
<span class="hljs-keyword">const</span> apolloLambda = <span class="hljs-built_in">require</span>(<span class="hljs-string">'apollo-server-lambda'</span>);
<span class="hljs-keyword">const</span> admin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'firebase-admin'</span>);
<span class="hljs-keyword">const</span> typeDefs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./schema.gql'</span>);
<span class="hljs-keyword">const</span> { fetchGames, addOpenings } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./resolvers'</span>);

<span class="hljs-keyword">const</span> {
  ApolloServer,
} = apolloLambda;

<span class="hljs-keyword">const</span> credential = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./fake-creds.json'</span>);


admin.initializeApp({
  <span class="hljs-attr">credential</span>: admin.credential.cert(credential),
});


<span class="hljs-keyword">const</span> resolvers = {
  <span class="hljs-attr">Query</span>: {
    <span class="hljs-attr">allGames</span>: <span class="hljs-function">(<span class="hljs-params">root, args, context</span>) =&gt;</span> [], <span class="hljs-comment">//TBD</span>
  },
  <span class="hljs-attr">Mutation</span>: {
    <span class="hljs-attr">addOpenings</span>: <span class="hljs-keyword">async</span> (root, args, context) =&gt; addOpenings(root, args, { ...context, admin }),
  },
};

<span class="hljs-keyword">const</span> server = <span class="hljs-keyword">new</span> ApolloServer({
  typeDefs,
  resolvers,
});

<span class="hljs-built_in">exports</span>.handler = server.createHandler(
  {
    <span class="hljs-attr">cors</span>: {
      <span class="hljs-attr">origin</span>: <span class="hljs-string">'*'</span>,
      <span class="hljs-attr">credentials</span>: <span class="hljs-literal">true</span>,
    },
  },
);
</code></pre>
<h3 id="heading-the-schema">The Schema</h3>
<p>The schema defines the GraphQL API. ‘Nuf said.</p>
<pre><code class="lang-text">const typeDefs = `

type Mutation {
  addOpenings(start: Int!, end: Int!) : Int! 
}
`

module.exports = typeDefs;
</code></pre>
<h3 id="heading-the-resolver">The resolver</h3>
<p>This resolves the GraphQL addOpenings mutation into Firestore queries (batched, in this case), then sends back a count of documents submitted (if successful):</p>
<pre><code class="lang-text">const openings = require('./scid.js');


const addOpenings = async (_, { start, end }, { admin }) =&gt; {
  const db = admin.firestore();
  const batch = db.batch();
  const fens = db.collection('chess/openings/fen');
  const data = openings.slice(start, end);

  data.forEach((opening) =&gt; {
    const id = opening.fen.replace(/\//g, '$');
    const doc = fens.doc(id);
    batch.set(doc, opening);
  });

  await batch.commit();

  return data.length;
};

module.exports = {  addOpenings };
</code></pre>
<h3 id="heading-the-opening-book">The opening book</h3>
<p>The JSON version of the opening book consists of an SCID (an opening identifier based on <a target="_blank" href="https://www.365chess.com/eco.php">ECO</a>), as well as the opening name and its <a target="_blank" href="https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation">FEN</a>. Each one becomes a document in the database.</p>
<pre><code class="lang-text">/* eslint-disable comma-dangle */
module.exports = [
  {
    SCID: 'A00b',
    desc: '"Barnes Opening"',
    fen: 'rnbqkbnr/pppppppp/8/8/8/5P2/PPPPP1PP/RNBQKBNR b KQkq - 0 1'
  },
  {
    SCID: 'A00b',
    desc: '"Fried fox"',
    fen: 'rnbqkbnr/pppp1ppp/8/4p3/8/5P2/PPPPPKPP/RNBQ1BNR b kq - 1 2'
  },
  {
    SCID: 'A00c',
    desc: '"Kadas Opening"',
    fen: 'rnbqkbnr/pppppppp/8/8/7P/8/PPPPPPP1/RNBQKBNR b KQkq h3 0 1'
  },
  ...
];
</code></pre>
<h2 id="heading-creating-the-client">Creating the client</h2>
<p>I’m using apollo-client in the React application. The easiest way to do so is create-react-app, then toss in [apollo-boost] to get a skeletal client up and running quickly. Then create a React component to trigger a call to the lambda, using the GraphQL API it provides.</p>
<p>The component will provide the start/end indices of the opening book to load, and a submit button.</p>
<p><img src="https://www.jeffamabob.com/media/screenshot-2019-11-06-at-1.54.54-pm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here’s a condensed version of the component code:</p>
<pre><code class="lang-text">/* eslint-disable no-alert */
import React, { useState } from 'react';
import fetch from 'node-fetch';
import ApolloClient, { gql } from 'apollo-boost';

const styles = {
//...
};

const client = new ApolloClient({
  uri: '/.netlify/functions/pgnfen',  //the lambda URL
  fetch,
});

export default () =&gt; {
  const [start, setStart] = useState(0);
  const [end, setEnd] = useState(5);

  const clickHandler = async () =&gt; {
    const mutation = gql`mutation {
      addOpenings(start: ${start}, end: ${end})
    }`;

    // eslint-disable-next-line no-console
    const count = await client.mutate({ mutation })
      .catch((e) =&gt; { window.alert(e); });
    // console.dir(count);
    window.alert(`${count.data.addOpenings} documents written`);
  };

  const startEndHandler = (evt) =&gt; {
    if (evt.target.name === 'start') {
      setStart(evt.target.value);
    } else {
      setEnd(evt.target.value);
    }
  };

  return (
//...
    &lt;input type="button" onClick={clickHandler} value="Load Scids" /&gt;  Row start:&amp;nbsp;&amp;nbsp;
    &lt;input name="start" type="number" step="5" style={styles.numInput} onChange={startEndHandler} value={start} /&gt;
Row end:&amp;nbsp;&amp;nbsp;
     &lt;input name="end" type="number" step="5" style={styles.numInput} onChange={startEndHandler} value={end} /&gt;
//...
  );
};
</code></pre>
<p>And the mutation response will show in a window alert box:</p>
<p><img src="https://www.jeffamabob.com/media/screenshot-2019-11-06-at-11.50.01-am.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-added-bonus">Added bonus!</h2>
<p>Since I used apollo-server-lambda as a basis for the Netlify Function, I can go directly to the service endpoint via URL and it will bring up GraphQL Playground:</p>
<p><img src="https://www.jeffamabob.com/media/screenshot-2019-11-06-at-11.55.16-am.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here I can test queries and mutations prior to embedding them in my React client code.</p>
<p><strong>Also</strong>, invoking <code>netlify dev</code> gives me a hot server courtesy of create-react-app, so I can see result of code changes in “real” time.</p>
<p>That’s it! Here’s a link to <a target="_blank" href="https://github.com/JeffML/firebase-lambda2">source</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to work with FaunaDB + GraphQL ]]>
                </title>
                <description>
                    <![CDATA[ By Jeff M Lowery I have one or two projects I maintain on Netlify, in addition to hosting my blog there. It’s an easy platform to deploy to, and has features for content management (CMS) and lambda functions (by way of AWS). What I needed for my late... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-faunadb/</link>
                <guid isPermaLink="false">66d45f71ffe6b1f641b5fa09</guid>
                
                    <category>
                        <![CDATA[ FaunaDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ netlify-functions ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 30 Sep 2019 17:01:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/09/vincent-van-zalinge-WHrwb43vH9E-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jeff M Lowery</p>
<p>I have one or two projects I maintain on <a target="_blank" href="https://www.netlify.com/">Netlify</a>, in addition to hosting my blog there. It’s an easy platform to deploy to, and has features for content management (CMS) and lambda functions (by way of AWS).</p>
<p>What I needed for my latest project, though, was a database. Netlify has integrated <a target="_blank" href="https://fauna.com/">FaunaDB</a>: a NoSQL, document-oriented database. Fauna has recently <a target="_blank" href="https://fauna.com/blog/the-worlds-best-serverless-database-now-with-native-graphql">boasted support for GraphQL</a>, which is a big plus. At no charge and with a simplified setup, why not try it?</p>
<h2 id="heading-the-database">The database</h2>
<p>Fauna has a unique approach to managing <a target="_blank" href="https://fauna.com/blog/consistency-without-clocks-faunadb-transaction-protocol">transactions across globally distributed data stores</a>, so that that database records don’t get out of sync when they’re updated from points far and wide. This is a problem for global enterprises with high transaction volumes, but irrelevant for my small project.</p>
<h2 id="heading-the-application">The application</h2>
<p>I’m a chess player of middling ability and I want to set up data to do analysis of master-level chess games. SQL or NoSQL didn’t matter—I’ve worked with both and either would support my application’s modest needs.</p>
<p>I love GraphQL, and have been using it since 2016. I don’t want my GraphQL schema exposed on the client side, though. The way around this is to have lambda functions to do the GraphQL requests, then have the client use those functions as a sort of proxy.</p>
<h2 id="heading-the-implementation">The implementation</h2>
<p>I started with <a target="_blank" href="https://github.com/netlify/netlify-faunadb-example">netlify-fauna-example</a><em>. This doesn’t use GraphQL; instead the example’s <a target="_blank" href="https://www.netlify.com/docs/functions/">Netlify Functions</a> use FQL: <a target="_blank" href="https://docs.fauna.com/fauna/current/api/fql/">Fauna Query Language</a>. You can execute queries via <a target="_blank" href="https://github.com/fauna/fauna-shell">fauna shell</a>, or by <a target="_blank" href="https://github.com/fauna/faunadb-js">using a NodeJS client module</a>. The following uses the client to insert a <strong>todoItem</strong> into the <em>*todos</em></em> collection:</p>
<p>todos-create.js</p>
<pre><code class="lang-js">  ...
  <span class="hljs-comment">/* construct the fauna query */</span>
  <span class="hljs-keyword">return</span> client.query(q.Create(q.Ref(<span class="hljs-string">'classes/todos'</span>), todoItem))
    .then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'success'</span>, response)
      <span class="hljs-comment">/* Success! return the response with statusCode 200 */</span>
      <span class="hljs-keyword">return</span> callback(<span class="hljs-literal">null</span>, {
        <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(response)
      })
    }).catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error'</span>, error)
      <span class="hljs-comment">/* Error! return the error with statusCode 400 */</span>
      <span class="hljs-keyword">return</span> callback(<span class="hljs-literal">null</span>, {
        <span class="hljs-attr">statusCode</span>: <span class="hljs-number">400</span>,
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(error)
      })
    })
</code></pre>
<p>To use GraphQL, I need to create a database on Fauna, and then import a GraphQL schema. Once you’ve created an account on Fauna, you can do all this through their dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/Screenshot-2019-09-29-at-11.27.05-AM---Edited.png" alt="Image" width="600" height="400" loading="lazy">
<em>My new Fauna database</em></p>
<p>Once done, a set of collections (akin to tables in SQL) are created based on my imported GraphQL type definitions. Interestingly, new types and fields are also added to handle stuff like identifying instances and managing relations between types. For instance, my type for Opening was:</p>
<pre><code class="lang-gql"><span class="hljs-keyword">type</span> Opening {
  <span class="hljs-symbol">desc:</span> String!
  <span class="hljs-symbol">fen:</span> String!
  <span class="hljs-symbol">SCID:</span> String!
}
</code></pre>
<p>and when I go to the dashboard, open GraphQL Playground, and look at the schema, I see:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/Screenshot-2019-09-28-at-1.05.59-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>OpeningInput and OpeningPage were added by Fauna, in addition to the _id and _ts fields in Opening.</p>
<h3 id="heading-queries-and-mutations">Queries and Mutations</h3>
<p>There are certain queries and mutations that will be automatically implemented for you by Fauna <em>if</em> you define them in the schema you created. When I define the type to hold chess opening information, I <em>may</em> then include the following Query and Mutation definitions in my schema:</p>
<pre><code class="lang-text">type Query {
 allOpenings: [Opening]
}
</code></pre>
<p>And FaunaDB will provide an implementation.</p>
<h3 id="heading-lambda-functions">lambda functions</h3>
<p>The original lambdas in <a target="_blank" href="https://github.com/netlify/netlify-faunadb-example">netlify-fauna-example</a> speak FQL. To convert these to GraphQL requests, use a fetch library such as node-fetch, and make HTTPS requests to the Fauna GraphQL endpoint using an client like the one included with <a target="_blank" href="https://levelup.gitconnected.com/giving-react-a-lift-with-apollo-boost-74c6ff32894d">apollo-boost</a>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> ApolloClient <span class="hljs-keyword">from</span> <span class="hljs-string">'apollo-boost'</span>;
<span class="hljs-keyword">import</span> gql <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-tag'</span>
<span class="hljs-keyword">import</span> fetch <span class="hljs-keyword">from</span> <span class="hljs-string">'node-fetch'</span>
<span class="hljs-keyword">import</span> authorization <span class="hljs-keyword">from</span> <span class="hljs-string">'./authorization'</span>

<span class="hljs-keyword">const</span> URL = <span class="hljs-string">'https://graphql.fauna.com/graphql'</span>

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> ApolloClient({
  <span class="hljs-attr">uri</span>: URL,
  fetch,
  <span class="hljs-attr">request</span>: <span class="hljs-function"><span class="hljs-params">operation</span> =&gt;</span> {
    operation.setContext({
      <span class="hljs-attr">headers</span>: {
        authorization
      },
    });
  },
})


<span class="hljs-built_in">exports</span>.handler = <span class="hljs-function">(<span class="hljs-params">event, context, callback</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> allOpeningFens = gql<span class="hljs-string">`    
  query openings {
      allOpenings {
        data {fen}
      }
    }
  `</span>;


  client.query({ <span class="hljs-attr">query</span>: allOpeningFens })
    .then(<span class="hljs-function"><span class="hljs-params">results</span> =&gt;</span> {
      callback(<span class="hljs-literal">null</span>, {
        <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(results),
      })
    })
    .catch(<span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> callback(e))
}
</code></pre>
<p>The code above requests the FEN strings for all the openings in the Opening collection.</p>
<h2 id="heading-are-we-done-now-no">Are we done now? No.</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-217.png" alt="Image" width="600" height="400" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@sarti46?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"&gt;Massimo Sartirana / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm<em>campaign=api-credit)</em></p>
<p>Fauna’s GraphQL support is in a functional but still formative stage. One of the things I wanted to do was have batch insert ability so I wouldn’t have to insert once opening at a time into the Opening collection. This mutation isn’t created by Fauna automatically (though it is ticketed feature request), so I had to define a resolver for it.</p>
<p>Fauna has a <a target="_blank" href="https://docs.fauna.com/fauna/current/api/graphql/directives/d_resolver">@resolver</a> directive that can be used on mutation definitions. It will direct Fauna to use a user-defined function written in FQL; these can be written directly in the shell. For a collection of simple types like Opening, the resolver FQL is pretty straightforward.</p>
<p>First, I go to the FaunaDB Console Shell, and create the function <code>add_openings</code>:</p>
<pre><code class="lang-text">CreateFunction({
  name: "add_openings",
  body: Query(
    Lambda(
      ["openings"],
      Map(
        Var("openings"),
        Lambda("X", Create(Collection("Opening"), { data: Var("X") }))
      )
    )
  )
</code></pre>
<p>Openings is an array, and the Map method executes Create call on each element. I then add a @resolver directive to my mutation definition in the schema I will import (referred to as a <strong>custom resolver</strong>):</p>
<pre><code class="lang-text">type Mutation {
   addOpenings(openings: [OpeningInput]) : [Opening]! @resolver(name: "add_openings" paginated:false)
}
</code></pre>
<p>Now when the mutation is executed via the GraphQL client, <code>add_openings</code> is called and will insert all games passed in as a parameter to the mutation. From the GraphQL client it looks like this:</p>
<pre><code class="lang-text">import ApolloClient from 'apollo-boost';
import gql from 'graphql-tag'
import fetch from 'node-fetch'
import authorization from './authorization'

const URL = 'https://graphql.fauna.com/graphql'

const client = new ApolloClient({
  uri: URL,
  fetch,
  request: operation =&gt; {
    operation.setContext({
      headers: {
        authorization
      },
    });
  },
})


exports.handler = (event, context, callback) =&gt; {

  const addScidDocs = gql`
  mutation($scid: [OpeningInput]) {
    addOpenings(openings: $scid) {desc}
  }
  `

  const json = JSON.parse(event.body)

  client.mutate({
    mutation: addScidDocs,
    variables: { scid: json },
  })
    .then(results =&gt; {
      console.log({ results })
      callback(null, {
        statusCode: 200,
        body: JSON.stringify(results),
      })
    })
    .catch(e =&gt; callback(e.toString()))

  // callback(null, { statusCode: 200, body: event.body })
}
</code></pre>
<h2 id="heading-the-old-chicken-and-egg-problem">The old chicken and egg problem</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-218.png" alt="Image" width="600" height="400" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@chromatograph?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"&gt;Chromatograph / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm<em>campaign=api-credit)</em></p>
<p>You’ll notice in the mutation above that I refer to the OpeningInput type. In order for me to import my schema into Fauna, that type has to be defined. But… when I imported Opening, Fauna auto-generated that type for me. When I define it in my schema later (for the mutation), I essentially override that type. Since that generated type is used in generated mutations (ie., createOpening, singular), by overriding that type definition in my own schema I could possibly break one of the generated mutations.</p>
<p>The solution suggested is to not override the OpeningInput type, but to rename my input type to something like MyOpeningInput. That ensures that my import schema validates, and doesn’t mess with what the generated mutations expect.</p>
<p>The problem gets messier, though, when you use the <a target="_blank" href="https://fauna.com/blog/getting-started-with-graphql-part-2-relations">@relation</a> directive. That directive generates types used to relate two other type instances.</p>
<p>Here’s the relation in my import schema. Note the directive:</p>
<pre><code class="lang-text">type Game {
  header: Header! @relation
  fens: [String!]!
  opening: Opening @relation
}

type Header {
    Event: String
    Date: String!
    White: String!
    WhiteElo: String
    Black: String!
    BlackElo: String
    ECO: String
    Result: String
}
</code></pre>
<p>To store a Game, I need to have also a required Header (Opening is not required). The relation is maintained by a Fauna-generated <strong>ref</strong> field on the Header. It's defined for the mutation through the use of a GameHeaderRelation type that allows the creation of both Game and Header in a single mutation. Here are the relevant generated types:</p>
<pre><code class="lang-text">input GameHeaderRelation {
  create: HeaderInput
  connect: ID
}

input GameInput {
  header: GameHeaderRelation
  fens: [String!]!
  opening: GameOpeningRelation
}

type Mutation {
  createGame(data: GameInput!): Game!
}
</code></pre>
<p>Now to add a game with the required header info, I can call the mutation like so, from within the Playground:</p>
<pre><code class="lang-text">mutation CreateGameWithHeader {
    createGame(data: {
        fens: [],
        header: { 
           create: {
           date: "2004.10.16", 
           white: "Morozevich, Alexander", 
           ...} ) {
        _id
        fens
        header {
          data {
            date
            white
          }
        }
    }
}
</code></pre>
<p>Let' say I now want to create a mutation to batch upload multiple games. Unfortunately I don’t have access to the generated <strong>GameHeaderRelation</strong> type, or any of the other input types. My import schema won’t validate without those defined if I try to use them in my bulk mutation definition. Again, bulk mutations are a ticketed feature request, so they should be available soon. Yet this type of issue will arise regarding any custom resolver’s use of types.</p>
<p>I though for a minute that the solution would be to download the generate schema (from Playground), then modify it with my bulk mutations. However, I am <em>overriding</em> <strong>__</strong>the otherwise-generated types on import, which is not what I want to happen.</p>
<h3 id="heading-the-workaround-write-a-custom-resolver-in-fql">The workaround: write a custom resolver in FQL</h3>
<p>As stated, I need to ensure that when I create a function for my addGames resolver to call, there has to be a Header created first for each game.</p>
<p>The GraphQL Schema resolver attribute calls the FQL add_games function:</p>
<pre><code class="lang-text">addGames(games: [GameInput]) : [Game]! @resolver(name: "add_games", paginated: false)
</code></pre>
<p>And here’s the function definition for add_games:</p>
<pre><code class="lang-text">CreateFunction({
  name: "add_games",
  body: Query(
    Lambda(
      ["games"],
      Map(
        Var("games"),
        Lambda("X", [
          Create(Collection("Game"), {
            data: Merge(Var("X"), {
              header: Select(
                ["ref"],
                Create(Collection("Header"), {
                  data: Select(["header"], Var("X"))
                })
              )
            })
          })
        ])
      )
    )
  )
}
</code></pre>
<p>I’m not an FQL expert (see acknowledgments), but this code is readable (from innermost outward):</p>
<ol>
<li>creates a header instance</li>
<li>selects its generated reference field “ref”</li>
<li>merges that reference as field “header” into the a data object “X”</li>
<li>“X” represents one element of the input array parameter “games” (GameInput)</li>
</ol>
<p>I should note that one of Fauna’s engineers stated that maintaining references <em>by hand</em> is “tricky”. It requires understanding of what is going on beneath the covers. The <a target="_blank" href="https://docs.fauna.com/fauna/current/api/graphql/directives/d_embedded">@embedded</a> type of relation may be easier to implement in FQL if the relation is one-to-one, as in this case.</p>
<h2 id="heading-where-to-go-from-here">Where to go from here…</h2>
<p>Fauna’s support team and Slack community forum members have been exceedingly helpful with questions and have even offered help with implementing FQL functions. They’re also forthcoming when onsite documentation is incomplete or wrong.</p>
<p>Performance wasn’t great: the bulk insert of a 1000 small documents executed in matters of seconds, which is slow. However I didn’t use pagination in my resolvers, and that may make a significant difference. It is also possible that the GraphQL features are in a slower debuggable configuration as Fauna ramps up the feature set.</p>
<p>To write custom resolvers, it is necessary to master <a target="_blank" href="https://docs.fauna.com/fauna/current/api/fql/">FQL</a>. Its <a target="_blank" href="https://www.tutorialspoint.com/lisp/lisp_basic_syntax.htm">LISPish</a> syntax will appeal to some, but I find it verbose and “nesty”. For simple CRUD operations it is fine. You may not find yourself writing many custom resolvers, either.</p>
<p>I chose to try Fauna not for its strengths, but for convenience. I may come back to it in a few months and see how it has progressed.</p>
<p><strong>Acknowledgements</strong></p>
<p>I’d like to thank Summer Schrader, Chris Biscardi, and Leo Regnier for their patience and insight.</p>
<hr>
<ul>
<li>I guess my life isn’t interesting enough: when I clone a project like netlify-fauna-example, I will usually then run <code>npm update outdated</code> and <code>npm audit fix</code>. I can expect to encounter issues when I do this, but in practice I usually resolve them in an hour or two.</li>
</ul>
<p><strong>Not this time.</strong> I deleted node_modules, package-lock.json, and even did a forced clean of the cache before reinstalling everything. Didn’t work. I eventually switched over to <strong>yarn</strong>, deleted the above (but left the updated version info in package.json alone) and installed. After a few hiccups, success! Here are the dependency versions I would up with:</p>
<pre><code class="lang-text">  "dependencies": {
    "apollo-boost": "^0.4.4",
    "chess.js": "^0.10.2",
    "encoding": "^0.1.12",
    "faunadb": "^2.8.0",
    "graphql": "^14.5.7",
    "graphql-tag": "^2.10.1",
    "node-fetch": "^2.6.0",
    "react": "^16.9.0",
    "react-dom": "^16.9.0",
    "react-scripts": "^3.1.1"
  },
  "devDependencies": {
    "http-proxy-middleware": "^0.20.0",
    "markdown-magic": "^1.0.0",
    "netlify-lambda": "^1.6.3",
    "npm-run-all": "^4.1.5"
  },
</code></pre>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to use Netlify Functions in Elm ]]>
                </title>
                <description>
                    <![CDATA[ This worked example creates a simple Netlify Function and integrates it with an Elm application. Netlify functions provide a very convenient way of working with AWS Lambdas, and they have an impressive array of example use cases, such as sending emai... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-netlify-functions-in-elm/</link>
                <guid isPermaLink="false">66bb926adeef71ff683a6d46</guid>
                
                    <category>
                        <![CDATA[ create-elm-app ]]>
                    </category>
                
                    <category>
                        <![CDATA[ netlify-functions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ELM ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Netlify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cedd Burge ]]>
                </dc:creator>
                <pubDate>Wed, 28 Aug 2019 07:37:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/netlify-functions-elm.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This worked example creates a simple <a target="_blank" href="https://functions.netlify.com/">Netlify Function</a> and integrates it with an Elm application.</p>
<p>Netlify functions provide a very convenient way of working with AWS Lambdas, and they have an <a target="_blank" href="https://functions.netlify.com/examples">impressive array of example use cases</a>, such as sending emails, processing payments and logging.</p>
<p>This example reads secrets from environment variables (to avoid them being exposed in the browser), but it is mostly generic, and can be adapted for other use cases easily.</p>
<h2 id="heading-step-1-prerequisites">Step 1 - Prerequisites</h2>
<ul>
<li>Create a repository for the code, probably on GitHub</li>
<li><code>npm i -g elm</code></li>
<li><code>npm install -g netlify-cli</code></li>
<li><code>npm i -g create-elm-app</code></li>
</ul>
<h2 id="heading-step-2-create-vanilla-elm-app">Step 2 - Create vanilla Elm app</h2>
<ul>
<li><code>create-elm-app elm-app-with-netlify-function</code></li>
<li><code>cd elm-app-with-netlify-function</code></li>
<li><code>elm-app start</code></li>
</ul>
<p>This should start a development server and load the app in your browser.</p>
<p>You can look at <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/d976b2391f98f07113d1e41a64b0359caddf3452">this commit in the companion repository</a> to check that everything is ok.</p>
<h2 id="heading-step-2-deploy-on-netlify">Step 2 - Deploy on Netlify</h2>
<ul>
<li><code>npm init</code> (and fill in sensible values)</li>
<li><code>npm i create-elm-app --save-dev</code> (this adds create-elm-app to package.json, which is used by netlify)</li>
<li>Push the code to GitHub</li>
</ul>
<p>You can see the results of this at <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/aa52ccfabacae69591a920f0675eedf620ae8b03">this commit in the companion repository</a></p>
<ul>
<li>Log in / Sign up / register with <a target="_blank" href="https://www.netlify.com/">Netlify</a></li>
<li>Create a <a target="_blank" href="https://app.netlify.com/start">new site</a> on Netlify</li>
<li>Choose your repository</li>
<li>Set the "Build command" to <code>elm-app build</code></li>
<li>Set the "Public directory" to <code>build</code></li>
<li>Click on "Deploy Site"</li>
</ul>
<p>Netlify will now deploy the site, installing the dependencies specified in package.json, then running <code>elm-app build</code> and then serving the dist directory.</p>
<p>From now on, Netlify will attempt to deploy the latest code every time you push to GitHub.</p>
<h2 id="heading-step-3-link-netlify-dev">Step 3 - Link Netlify Dev</h2>
<ul>
<li><code>netlify login</code></li>
<li><code>netlify link</code> and choose the “Use current git remote url” option</li>
<li>Add “./netlify” to .gitignore</li>
<li>Add a netlify.toml file (from <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/6514012000ea82fb6625fa3686adafa321723d28">this commit in the companion repository</a>)</li>
<li><code>netlify dev</code></li>
</ul>
<p>This should start a local development server and load the app in your browser, in a similar way to step 1.</p>
<h2 id="heading-step-4-add-a-netlify-function">Step 4 - Add a netlify function</h2>
<p>Run <code>netlify functions:create</code> to create a new netlify function. Choose the “js-token-hider” template, and name it "call-api".</p>
<p>This will create a javascript file for the function, and a package.json for its dependencies in “functions/call-api”.</p>
<p>Replace functions/call-api/call-api.js with this one in <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/79381b9c1a7731b01f0c81b58a772d9576f76732">the companion repository</a></p>
<p>Now if you run <code>netlify dev</code>,  the function will be served as well as the app, albeit on different  ports. You can view the function in the browser to check that it is  working (probably at <a target="_blank" href="http://localhost:34567/call-api">http://localhost:34567/call-api</a> or <a target="_blank" href="http://localhost:34567/.netlify/functions/call-api">http://localhost:34567/.netlify/functions/call-api</a>)</p>
<h2 id="heading-step-5-call-the-netlify-function-from-elm">Step 5 - Call the netlify function from Elm</h2>
<p>Install depdencies</p>
<ul>
<li><code>elm install elm/json</code></li>
<li><code>elm install elm/http</code></li>
<li><code>elm install krisajenkins/remotedata</code></li>
</ul>
<p>Update Main.elm to call the function and display the results (from <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/4dc9e8e4b60d061b5d5ef0fb2ce6ab856741236f">the companion repository</a>).</p>
<p>Instruct create-elm-app to proxy api calls to the function, by adding elmapp.config.js, as shown in <a target="_blank" href="https://github.com/ceddlyburge/netlify-functions-with-elm/commit/90a63178e38f2919770e37fcc94e7ee0bec343ab">the companion repository</a>.</p>
<p>At  this point, thee application runs, and successfully calls the api, but  there are no secrets / environment variables yet, so the UI shows an error.</p>
<h2 id="heading-step-6-add-the-secrets">Step 6 - Add the secrets</h2>
<p>Go  to the “Site Settings” - “Build &amp; Deploy” - “Continuous Deployment”  - “Environment Variables” section on the Netlify website for your application.</p>
<p>Add environment variables for API_TOKEN and API_URL</p>
<p>Now when you run ‘netlify dev’ the app should now load in the browser and call the locally hosted netlify function, which will return the API_TOKEN and API_URL environment variables that you set on Netlify.</p>
<p>The  same should be true on the live deployment on Netlify. You may need to  “Trigger Deploy” manually on Netlify, so that it uses the new environment variables.</p>
<p>You can see the deployment of the companion repository at <a target="_blank" href="https://netlify-functions-with-elm.netlify.com">https://netlify-functions-with-elm.netlify.com</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Netlify  / serverless functions are extremely useful for creating / connecting to the backend services that your front end needs. They are also very east to set up, as this artcile (hopefully!) shows.</p>
<p>Create-elm-app is a great tool for developing Elm applications, and its simple proxy feature works well when developing Netlify functions.</p>
<p>Netlify Dev is great for replicating the production Netlify setup when developing locally (in this case by automatically providing the environment variables).</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
