In March I published a tutorial on how to handle form submissions in Gatsby and using Airtable as your backend. I was pretty convinced that by hiding your API keys with process.env
they would be hidden away from view.
The tutorials and the blog posts that I read, all said that your keys would be hidden if you just used process.env
. In a way that is true and if you inspect the code on your browser the keys will be hidden.
Then Fran Caballero commented on my blog, saying that the keys would be shown in the network tab after you made the request. This happens because Airtable expects you to pass your API Key as a parameter in the header of your request.
As a reminder, the code from the tutorial looks like this:
handleSubmit = e => {
const fields = {"fields": {"Name": this.state.name, "Notes": this.state.notes}}
fetch("<https://api.airtable.com/v0/><account id>/<table name>", {
method: "POST",
headers: {"Authorization": `Bearer ${process.env.AIRTABLE_API}`,
"Content-Type": "application/json"},
body: JSON.stringify(fields)
})
.then(() => alert("Form Sent!"))
.catch(error => alert(error))
e.preventDefault();
}
As you can see, the API key is being passed in the headers and when a request is made the headers are exposed through the network tab. That means, everyone can see the API Key.
I needed a solution to keep the API keys hidden.
Netlify functions to the rescue!
Most of my sites are being served by Netlify and Netlify functions looked like the solution for this problem.
Netlify functions are nothing more, nothing less than functions run on the server. So the API keys should remain hidden from prying eyes.
A great thing about Netlify functions is that you can use 125k calls and 100minutes for free in a month per site.
Setting up functions for your Netlify site is pretty easy. All you need to do is create a folder, in the root of your project, inside this folder, you will put your functions. Then tell Netlify where to find this folder.
I like to put all the things related to Netlify inside a folder called .netlify
. Inside that .netlify
folder I just created another folder called functions
.
Now all I needed to do was to tell Netlify where my functions were. To do this, all you need to do is login into your Netlify account, choose the site that will be using the functions and then press the functions tab. In this tab, you just need to write the path of your folder.
In my case, I just wrote .netlify/functions
. Then you can push new changes or redeploy your site and Netlify will find the folder automatically - you will get an email saying that you are now using functions on your site.
Netlify functions rules
Netlify functions let you deploy AWS functions without the need to have an AWS account. At the moment you can write your functions using Javascript or Go.
Your javascript function will need to export a handler and should look like this:
exports.handler = function(event, context, callback) {
// your server-side functionality
}
When you call a function the handler will get an event object that will look similar to this:
{
"path": "Path parameter",
"httpMethod": "Incoming request's method name"
"headers": {Incoming request headers}
"queryStringParameters": {query string parameters }
"body": "A JSON string of the request payload."
"isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
}
So when you call your netlify function, the headers, method and path that you add will be available to you by writing event.path
, event.headers
and event.method
.
Finally, the callback will be what your Netlify function will return when you run it. This is where you handle success/failure and any response that you might need.
Creating a server-side function for airtable
I'm going to use the example above on how to handle form submissions and send them to airtable.
First, create a file inside your netlify functions folder called airtable.js
.
Now you need mix the handler format and the fetch request shown above. The function will look like this:
exports.handler = async (event, context, callback) => {
const pass = (body) => {callback(null, {statusCode: 200, body: JSON.stringify(body)})}
try {
let response = await fetch("https://api.airtable.com/v0/<account id>/<table name>",
{
method: event.httpMethod,
headers: {
"Authorization": `Bearer ${process.env.AIRTABLE_API}`,
"Content-Type": "application/json"
},
body: event.body
})
let data = await response.json()
await pass(data)
} catch(err) {
let error = {
statusCode: err.statusCode || 500,
body: JSON.stringify({error: err.message})
}
await pass(error)
}
}
Notice that I am using process.env
to get the Airtable API Key. Netlify will get the key automatically from the environmental variables since they are living server-side.
Connecting your form to the Netlify function
Now that we have our function, all that is left to do is to connect our form to the netlify function. Netlify makes it pretty easy for us to do this, all you need to do is use the path of your function on the fetch request.
Let's say that our form is located on the component called Notes
and its full path is your-awesome-site/src/components/Notes.js
we need to change our form to this:
handleSubmit = e => {
const fields = {
"fields": {
"Name": this.state.name,
"Notes": this.state.notes
}
}
fetch("../../.netlify/functions/airtable", {
method: "POST",
body: JSON.stringify(fields)
})
.then(() => alert("Form Sent!"))
.catch(error => alert(error))
e.preventDefault();
}
Notice that we are fetching the Netlify function airtable from the root folder. Also, we took the headers param from the request since we are using it on the server-side function.
So this fetch request will just call the netlify function and pass the form fields into the server-side function and that server-side function is the one calling the Airtable API.
This is all you need to do to hide your API keys and use Netlify functions. If you want to log things from your Netlify function you can use console.log
and this log will be available within your function screen inside your Netlify account - from the functions tab.
Top comments (22)
Pretty cool. A general note, though, storing an API key in your code is generally bad practice because it's likely to end up in your repo, which even if private doesn't guarantee someone won't eventually be able to see it. A common way to get around that is to read the API key from a file stored in a separate area on the server or in a database. Environment variables would be used to change where you pull that from as the code moves through your environments.
Your solution is obfuscating, not securing. The endpoint / method where you store the API key is still in your repo and your code, and would still be accessible, just more steps to do so.
I've seen the whole reply between you and Lawrence and decided to reply here.
There is a good chance that I might be doing something wrong, after all, I am self-taught and the gap between what I know and what I don't know is pretty colossal. Also, this second part came out because someone informed me that my first part wasn't hiding the API key and could be seen in the requests.
I agree that if you are using a public repository everyone can see your code - remember that you can create private repositories for free on github.
The server-side functions are run on netlify are aws functions - you can read more about it on the official server-side functions documentation. So the code run there will be server-side.
Obviously, you could just use environmental variables to hide the airtable url like what I am doing with the api keys. These environmental variables are being set on netlify domain account and this is the only place where you put your api keys.
I agree that anyone could easily send a request following the server-side endpoint but I did try to do that with postman and the api key didn't get leaked by netlify. Am I wrong to think that the api key are indeed hidden?
Netlify also allows you to choose a context which could prevent people to use the endpoint and submit forms but I didn't touch that here.
With the increase use of JAMStack and serverless am I wrong to think that this might be an effective way to use airtable (or any other API) to power up a static site like Gatsby?
I apologise if I made any mistake, mostly I write about my journey and discoveries and thought it could be helpful to others to tackle issues that I have faced myself.
I think Freddy was refusing to accept the part about you using server-side functions. If your keys are transmitted from the server to the API, then the client-side will not see them. Using an environment variable is a fine solution for keeping your keys out of your repo. As an FYI, it wasn't that long ago that a bunch of private repos on github got accessed by malicious users, so I wouldn't ever rely on any repo always having true privacy.
Who are you responding to? I can't speak to how Netlify environment variables work, but if it's in response to what I wrote, no, you're wrong. You don't put it anywhere in your application. You store it in a separate area on the server that only root and your web app have access to.
The comments hierarchy clearly shows who I'm talking to :) So does my answer.
Alrighty, so what's your response? In no way have I proposed obfuscation nor would the key end up in a repo.
Sorry, I was under the impression it was the OP that didn't know how the comments work, I should have known it was you, considering you didn't understand the article to begin with.
If you store the key in a seperate location, that be a database or a file, then the key is still accessible. This entire post is specific towards static websites, SPAs and similiar, where the entire site is running directly in the clients browser.
For your solution to work, you still need a publicly accessible endpoint where the API key can be retrieved from. That be example.com/myapi.key or example.com/fetchKeyFromDBReturnJSON. If your static, client-run website can fetch the key, then so can anyone else.
You're confusing the entire approach with a regular dynamic website that's run on a traditional server that handles backend processing. That's not the case with Netlify, it's entire approach is based on static content, with the exception of the extra feature, AWS Lambda functions, which the OP is turning to.
I'm curious as to why you are responding to an article that's talking about a service you don't know anything about? Your comment is referring to "standard practices" when running in "traditional environments". Netlify is neither.
Easy big-timer. I know you got this new account to flex on people, but you picked the wrong guy. Using any sort of method to transmit a key client-side is absurd. Speaking of not reading articles, you clearly missed the whole section titled "Creating a server-side function for airtable" explaining how OP is using server-side code. This is where you avoid storing your API keys directly within your code and what I was responding to. OP was clearly able to deduce that in his response.
I have no intention of flexing; wanna-be-devs that provide faulthy information to the public on the other hand, is a problem.
I guess you missed the entire article title. "Hide your API Keys with Netlify Functions"
Meaning, your method won't work. As Netlify is based on static content, where everything is run, executed and loaded in the clients browser. IF you where to get your solution to work; then you would need a public endpoint where the key can be retrieved, and as I've said multiple times, and as you are now repeating, is absurd.
"Creating a server-side function for airtable", the op is using server side code, yes, on Netlify. Not just any random server.
The entire article is based on Netlify, running backend code with netlify, to achieve the goal of hiding your API key. The OP is writing everything that needs to be written, and you're comment is not only adding confusion to those who don't know better (Including yourself), it's also taken entirely out of context and has absolutely no relevance to the written article.
I get that you're the kind of person that refuse to admit that you're wrong, and simply invent context and meaning to whatever bullshit you spit out.
But for future reference, it's a huge benefit to everyone reading articles such as these if people who actually knew what they where talking about commented on the "wrong-doings".
You have yet to explain how hiding a key server-side is wrong or incorrect. You say that its impossible to have server-side code with Netlify (which may be because I have not used it), but that is not what OP said.
Get over yourself; you're just looking for a problem and you picked the least appropriate comment to do so. There's no doubt that someone who was thinking about storing their API keys in a code repo as OP proposed that will benefit from the explanation of how that is a poor choice. That is 100% platform independent.
Cool that you know so much about Netlify though. I bet that's super useful.
Dude, get over yourself; you're just looking to add confusion. There is no doubt that the OP wrote this article specifically to avoid storing the API key in the code, considering that would reveal it to the world, being static and all :)
The facts are that there is only one person flexing; and that's you, from your very first comment where you add irrelevant context to every reply you have made since.
The server-side element doesn't even have anything to do with Netlify. It's airtable. I mean, I don't see how you aren't getting this (maybe just bad troll?)
"So this fetch request will just call the netlify function and pass the form fields into the server-side function and that server-side function is the one calling the Airtable API."
Stop the try-hard act. You'll never learn anything with that attitude.
LOL! Now you're truly showing how incompetent you are.
Airtable is only relevant as that's the API he is trying to communicate with from Netlify. And he is trying to make calls to Airtable from Netlify without revealing the API token, considering Netlify sites are run on a CDN and requires the site to be static.
Airtable is his chosen third-party API, it has no relevance to his problem or the article in question; it could be any API. I guess your next advice is fetch the API client key directly from the API right?
Idiot. You are the one that needs to listen, the one with a lot to learn. After all, I'm the one with experience here, you're just the troll throwing out random comments with no context :)
Thank you for the comment yeah I agree with you. With Netlify you don’t need to store the environment variables in your repo, all you need to do is add them to your deploy tab and Netlify will get them for you so no need to add them on your repo 😄
"I like to put all the things related to Netlify inside a folder called .netlify. Inside that .netlify folder I just created another folder called functions."
I am unable to get git to recognize the ".netlify" folder, so it will never push to to netlify. Any thoughts here?
Check gitignore documentation in regards to the folder being ignored by git, but as a general rule anything with
.
in front of it is considered hidden in most OS environments. In other words, you wouldn't want to put anything in a folder like that unless you truly wanted it hidden from everything except the local environment its stored in (generally speaking).Thanks for replying. Maybe I am just misunderstanding how creating this locally, then NOT pushing it to the repo will result in this working somehow.
Hello I think you might have misunderstand - or I haven't explained myself well on the post. You will need to push .netlify folder to your repo. All your api keys should be environmental variables which will be set on netlify website.
That way netlify will get your keys server side when running your server-side function. If you can't make netlify discover that folder make sure that this folder is located in the root of your project and in netlify functions tab you wrote the full path './netlify/functions'
Hope this helps
'./netlify/functions' instead of '.netlify/functions'
This seems to have been the issue. thanks!
Good article!
Is there a way to take this to the next level, where the cloud function is used to authenticate the client app with the API, and then they can request/response DIRECTLY?
With this method, and all the examples i can find, every API request has to go through the Netlfiy Function, and the response has to go back the same way... Surely there is a way around this...???
Yes, every API call must contain the API KEY. But still.
Anyone?
i can't get to use fetch in the functions
Some comments may only be visible to logged-in visitors. Sign in to view all comments.