Airtable database issues(javascript) | Project: hoggystyle

Hello guys.

I’m trying to create a database with Airtable, and I need to make a button that creates some data.
The issue is that I can only launch the function that creates data from a file i have called server.js

The issue with that is i can’t refer to that file from my index.html file, as it’ll result in error: The server responded with a status of 404 ()

And i can’t move the code to another file because then it won’t recognize my “require” tag.

Code:

function submitData() {
var Airtable = require(‘airtable’);
var base = new Airtable({apiKey: process.env.AIRTABLE_API_KEY}).base(process.env.AIRTABLE_BASE_ID);

var username = document.getElementById(“newUsername”);
var attacksMissed = document.getElementById(“newAttacksMissed”);
var date = document.getElementById(“newDate”);
var totalStars = document.getElementById(“newTotalStars”);
var whichClan = document.getElementById(“newWhichClan”);

base(‘MissedAttacks’).create({
“Username”: username,
“AttacksMissed”: attacksMissed,
“Date”: date,
“Total Stars”: totalStars,
“Is Valid”: true,
“Which Clan”: whichClan
}, function(err, record) {
if (err) {
console.error(err);
return;
}
console.log(record.getId());
}); }

I’m not familiar with Airtable, but I think what you’ll want to do here is set up a route in your server.js file use something like a fetch request, or an XMLHttpRequest on the frontend, and send the data via that route. When you hit the route, you can launch the function that creates data from within server.js.

I see from the code in hoggystyle that you’ve already set up a /data route, so you’re halfway there! This one will just be a POST request (or something similar.) Let me know if any of that doesn’t make sense or if I’ve misunderstood the question!

1 Like

Thanks for the help.

I think that makes sense, but i don’t know how to do any of that. I’m new to coding.
Anything you can help with? :grimacing:

Absolutely! I’m on the train but I’ll get back to this in a bit :slight_smile:

Hey @darknessgamingdk!
So, the first thing I’d like to debug with you is why require isn’t working for you. I think I had the exact same problem earlier this week, and I think I solved it! I tested it by remixing your project, commenting out all the Airtable code, and then creating a new file called test.js right next to server.js. In server.js I put const test = require('./test.js'); at the top. In test.js I wrote module.exports = "hello world!";.

Then in server.js, under app.get("/".... I wrote console.log(test); (so it looked like this, when finished:)

// http://expressjs.com/en/starter/basic-routing.html
app.get("/", function(request, response) {
  console.log(test);
  response.sendFile(__dirname + '/views/index.html');
});

And when I viewed the site, in the logs below, I could see “hello world!” getting logged out, like this:


If require still doesn’t work after matching this code, let me know, and we can try to identify which part might be the sticking point.

So, the reason why you can’t find the submitData() function in your html file is because it’s located on the server. The “public” files are the ones that get sent to someone’s browser when they try to view your site – the server files are the one that remain running wherever your website is hosted. So when the requesting browser runs your index.html file and tries to look up the submitData() function, it’s trying to look up a function that only exists back on your server. (Also, I know some of this may be overly simple or obvious to you, I just want to make sure I cover all bases :slight_smile: ). So either you need to move that function into the “public” (or “client”) code, or you need to send a message to the server to run that code.

In my original message, I suggested that you create a new route on the server side, which would wait for you to send a request from the clientside. To clarify what I mean by this, you’ll see that there is already an app.get("/"... route set up in the code. This means, “when a user goes to the url for this website, please send them this response.” The response is the website itself.

Looking at the submitData() function which you wrote, however, it looks more like client-side code! This function is clearly trying to reach out and grab data directly off the form that you built in the index.html file. It might be simpler (than the way I suggested) to just drop this into your client.js file, in the public directory, and attach it to the button from inside the JS file.
You’ll notice that you import client.js at the bottom of the file – this can be a good practice, since that file wants to do things which require the page to be constructed already. The snag is that, even if submitData() were defined in client.js, you would be trying to reference it on the button element before it had been read by the browser. One way to handle that (and perform the above part of attaching the function to the button) is as follows –

First, give the button an ID so that it’s easy grab via javascript: <button id="submitDataButton">Submit</button>.

Then, grab a reference to the button in your client.js file as follows:

const myButton = document.getElementById("submitDataButton");

Now you have a reference to the button, so you can work with it!

Next, you want to define that function in your client.js folder. This should be as easy as copying and pasting that code into client.js – if you have an original reference you were working off of, that might have some more clues (and feel free to share it with me, I can take a look)

Having defined the function, you can attach it to your button. It might look something like this (obviously with the functionality of your submitData call vastly simplified.)

function submitData(){
  console.log("Hello World!");
}

const myButton = document.getElementById("submitDataButton").addEventListener("click", function(){
  submitData();
}

My only other suggestions are that you probably want to move the

// var base = new Airtable({apiKey: process.env.AIRTABLE_API_KEY}).base(process.env.AIRTABLE_BASE_ID);

calls to the top of the client.js file so that they aren’t called every time the function is called.

I think this should get you closer to your goal! Let me know if any of this could be clearer, or if it doesn’t answer the question.

2 Likes

One extra caveat – I noticed that submitData seems to be logging out the data (presumably for testing purposes.) The submit event for a button will automatically reload the page, and will therefore wipe out any console logs. If you want to see those things logged out, you can add in an event.preventDefault() call by passing event into the parens (()) in the definition of the submitData call, and then adding event.preventDefault() at the top of the function.

1 Like

I think that if the airtable API key should be kept secret, the submit should stay on server side.

Checking on airtable … How do I get my API key?

Warning: do not share your API key with anyone (including anyone at Airtable) since it’s effectively a password that allows access to all your bases. If you accidentally reveal your API key, you should regenerate your API key as soon as possible

Nice analysis, @househaunt :slight_smile:

1 Like

That’s a great catch, @mishavee you’re totally right re: API key. @darknessgamingdk I’ll post a revised approach which takes that into account tomorrow

1 Like

Thank you for the responces. I’ll try them out now.
The API key isn’t an issue. It’s in my .env file :wink:

First of all, I don’t see what this has to do with require. Require works fine in the server.js file. It’s outside that file it doesn’t work.
If i move my code to client.js then i get require is not defined.

Yes. The code is suppost to take the code, and then send it to my Airtable database.

Yes of course, but then i get require is not defined.

It doesn’t matter to much where the code is located, as long as it works.


Edit:
Alright so i tried this stuff now, and moved my code to client.js.

The const myButton = document.getElementById..... part seems to break my code, as it doesn’t stop loading the database, and never actually loads it.
Commenting that out, and it works, but require is still undefined.

Important thing for you:

require()/import is only valid in Node.JS

If you want to use these in Vanilla JS, you need some bundlers, for instance, Webpack. Otherwise, require() will be undefined, because Vanilla JS doesn’t have this feature.

1 Like

Alright. I’ll try to install that.

I think I installed that now, and don’t know how exactly it makes a difference??

Ah I see what you mean now. So we can go one of two paths here – either we can try to get the require working on the frontend using webpack, or we can split the submitData function into 2 parts: 1 part that grabs the data from the elements in the DOM and then sends them to the server, and a second part that waits on the serverside to catch that data and pass it to Airtable.

The way that webpack works is that it bundles all your required files together, so that those require calls never go to the browser. It probably just needs some config, but I’ll take a look at your stuff and be able to give you better feedback.

This is an intro I found that seems pretty good → Webpack: When To Use And Why - Andy Ray's Blog

I think what @mishavee is referencing is that those keys aren’t an issue right now, because they’re on the serverside – but if we were to move the code to the client side, either we would leave the keys in the .env file (and the code wouldn’t be able to read the keys) or you would have to move the keys clientside, and they would be exposed.

So, having looked through the code and discussed, the problems I see are as follows:

  1. require() doesn’t work on the frontend, but you’re trying to require Airtable on the frontend.
    ^ Technically, we could get require() working on the frontend using webpack, as mentioned by @jarvis394. I linked to an intro and explanation as to when to use webpack here. But I think in this instance, we don’t actually need it (for this specific issue anyway.) We would need it if we wanted to load Airtable on the frontend, and I don’t think we should because of @mishavee’s comment. We don’t want the keys exposed on the frontend – we want to keep them on the server.

  2. Airtable is being loaded over and over again with the current code (using the submit button) but isn’t working.
    This is became of the var Airtable = require 'Airtable'...var base = new Airtable calls. Every time you run the function, they’ll require Airtable again, and then create a new instance. Both should be moved out of the function call, but don’t sweat it right now because I think you’ll need to restructure the functions anyway.

  3. So how do we get the data from the form on the frontend, and handle it using logic that has to live on the backend?
    One classic construction for this (which I’m advising here) is to gather all the data from the form on the frontend and send a network request to the server with that data. Then you can do whatever you need with it on the backend. Here’s how we do that:

    a. First, put together the network request.
    You’re going to want to send a POST request with the data in the body – possibly something like this: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Supplying_request_options. In this example, the space where they’ve put { answer: 42 } is where you would put the data from your form that you’re trying to forward, and instead of putting http://example.com/answer you would put just the route you want to hit (which can be whatever you want – /submitData, for example.) This will be in client.js. It might be helpful to add this as the onClick for your submit button, as described previously.

    b. Catch the request on the server side
    Now that you’re sending the data, you can listen for it on the server, the same way that the server listens for requests to view your site in general. You’ll want to add a route ('/submitData', for example, if what I suggested above) and first, just log out what you get from the request to make sure it’s coming through. Here’s an explanation of how to write your own route! https://expressjs.com/en/guide/routing.html

    c. Handle the request on the server side
    This is the part you’ve been trying to do this whole time, and now you should have the context to do it. Now that you have your route and have confirmed that you’re getting the data you want, you can run that base('MissedAttacks') call etc to interact with Airbase.

Hope this is a help!

1 Like