Coder Social home page Coder Social logo

fewpjs-sending-data-with-fetch's Introduction

Sending Data with Fetch

Learning Goals

  • Use fetch() to send data to a remote host
  • Handle the response from a successful request
  • Handle errors from an unsuccessful request

Introduction

If you think about it, fetch() is a little browser in your browser. You tell fetch() to go to a URL by passing it an argument, e.g. fetch("https://flatironschool.com"), and it makes a network request. You chain calls to fetch() with then(). Each then() call takes a callback function as its argument. Based on actions in the callback function, we can display or update content in the DOM.

This is a lot like browsing the web: you change the URL in the URL bar, or you follow a link, and those actions tell the browser to go somewhere else and get the data. A technical way to describe that is: "The browser implements an HTTP GET to retrieve the content at a URL." It's also 100% technically correct to say "fetch() uses an HTTP GET to retrieve the content specified by a URL."

The browser also provides a helpful model for understanding what sending data from the browser looks like. We know this as an HTML form. Technically speaking, HTML forms "use an HTTP POST to send content gathered in <input> elements to a specified URL." It's also 100% technically correct to say "fetch() uses an HTTP POST to send content gathered in a JavaScript Object."

HTML forms are still widely used, but with fetch(), we have more detailed control of the request. Using fetch(), we can actually override the normal behavior of an HTML form, capture any user input, package it up with the appropriate request information and send it out.

Our focus in this lesson will be learning how to send data using fetch().

Introduce the JSON Server

To help us practice sending fetch() requests, this lab comes with a dependency called json-server. The JSON Server allows us to start a fake RESTful API within our lab folder, giving us the ability to send both GET and POST requests and to persist and receive data.

Install it by executing npm install -g json-server. (If this command errors out, you may need to run sudo npm install -g json-server instead; you will likely also need to provide your system password.)

To start up JSON Server, run json-server --watch db.json in your terminal. Note: Running this command will instruct json-server to use a db.json file in your terminal's current directory, so make sure to run this command from the same directory as this lab.

Once the server is running, you'll see a list of available resource paths in the terminal:

Resources
  http://localhost:3000/dogs
  http://localhost:3000/cats
  http://localhost:3000/users
  http://localhost:3000/robots

These endpoints each provide different sets of data. Since it is mimicking a RESTful API, sending a request to 'http://localhost:3000/dogs' will return all records in the database for dogs, while 'http://localhost:3000/dogs/1' will return the dog with the id of 1.

Some example data is already present, stored in db.json. If the JSON server is running, you can also visit any of the above resources in a browser to see the data.

The tests in this lab do not need JSON Server to be running, but if you would like to run tests while also running the server, open a second tab in your terminal.

Note: For users of the Live Server VSCode extension, you'll need to do a bit of extra configuration so that the json-server plays nicely with Live Server. Follow the steps in this gist (you'll only need to do this once), then come back to this lesson.

Analyze Data Sent in an HTML Form

Let's take a look at an HTML <form> (see sample_form.html in this repo):

<form action="http://localhost:3000/dogs" method="POST">
  <label> Dog Name: <input type="text" name="dogName" id="dogName" /></label><br />
  <label> Dog Breed: <input type="text" name="dogBreed" id="dogBreed" /></label><br />
  <input type="submit" id="submit" value="Submit" />
</form>

When we use the <form> element's default POST behavior in combination with a backend server, the key components for sending the submitted data to the server are:

  • The destination URL as defined in the action attribute of the <form> tag
  • The HTTP verb to use as defined in the method attribute of the <form> tag
  • The key / value data obtained from the inputs in the fields dogName and dogBreed

We should expect that our "mini-browser," fetch(), will need those same bits of information in order to send a Post request to the server.

Note: with JSON Server and our HTML form, we already have what we need to submit our form the conventional way, without using JavaScript. To try this out, make sure the JSON server is running and open sample_form.html in the browser. If you enter a dog name and breed in the input fields and click "Submit," your information should successfully POST to the JSON server database, db.json.

Construct a POST Request Using fetch()

Sending a POST request with fetch() is more complicated than what we've seen up to this point. It still takes a String representing the destination URL as the first argument, as always. But as we will see below, fetch() can also take a JavaScript Object as the second argument. This Object can be given certain properties that can be used to change fetch()'s default behavior.

fetch(destinationURL, configurationObject);

The configurationObject contains three core components that are needed for standard POST requests: the HTTP verb, the headers, and the body.

Add the HTTP Verb

So far, comparing to an HTML form, we've only got the destination URL ('http://localhost:3000/dogs' in this case). The next thing we need to include is the HTTP verb. By default, the verb is GET, which is why we can send simple GET requests with only a destination URL. To tell fetch() that this is a POST request, we need to add a method property to our configurationObject:

const configurationObject = {
  method: "POST"
};

Add Headers

The second piece we need to include is some metadata about the actual data we want to send. This metadata is in the form of headers. Headers are sent just ahead of the actual data payload of our POST request. They contain information about the data being sent.

One very common header is "Content-Type". "Content-Type" is used to indicate what format the data being sent is in. With JavaScript's fetch(), JSON is the most common format we will be using. We want to make sure that the destination of our POST request knows this. To do this, we'll include the "Content-Type" header:

const configurationObject = {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  }
};

Each individual header is stored as a key/value pair inside an object. This object is assigned as the value of the headers property as seen above.

When sending data, the server at the destination URL will send back a response, often including data that the sender of the fetch() request might find useful. Just like "Content-Type" tells the destination server what type of data we're sending, it is also good practice to tell the server what data format we accept in return.

To do this, we add a second header, "Accept", and assign it to "application/json" as well:

const configurationObject = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  }
};

There are many other headers available for particular uses. Some are used to send credentials or user authentication keys. Others are used to send cookies containing user info. "Content-Type" and "Accept" are two that we'll see the most throughout the remainder of this course.

Servers may reject requests without the specific headers the server is configured to expect.

Add Data

We now have the destination URL, our HTTP verb, and headers that include information about the data we're sending. The last thing to add is the data itself.

Data being sent in fetch() must be stored in the body of the configurationObject:

const configurationObject = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: /* Your data goes here */
};

There is a catch here to be aware of — when data is exchanged between a client (your browser, for instance), and a server, the data is sent as text. Whatever data we're assigning to the body of our request needs to be a string.

Use JSON.stringify() to Convert Objects to Strings

When sending data using fetch(), we often send multiple pieces of information in one request. In our code, we often organize this information using objects. Consider the following object, for instance:

{
  dogName: "Byron",
  dogBreed: "Poodle"
}

This object contains two related pieces of information, a dog's name and breed. Let's say we want to send the data in this object to a server. We can't simply assign it to body, as it isn't a string. Instead, we convert it to JSON. The object above, converted to JSON would look like this:

"{"dogName":"Byron","dogBreed":"Poodle"}"

Here, using JSON has enabled us to preserve the key/value pairs of our object within the string. When sent to a server, the server will be able to take this string and convert it back into key/value pairs in whatever language the server is written in.

Fortunately, JavaScript comes with a built in method for converting objects to strings, JSON.stringify(). By passing an object in, JSON.stringify() will return a string, formatted and ready to send in our request:

const configurationObject = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({
    dogName: "Byron",
    dogBreed: "Poodle"
  })
};

Send the POST Request

We've got all the pieces we need. Putting it all together, we get:

fetch("http://localhost:3000/dogs", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({
    dogName: "Byron",
    dogBreed: "Poodle"
  })
});

With the JSON server running, if you open up sample_form.html or index.html, you can test out the code above in the console. Try it and take a look in db.json: you should see that Byron the Poodle has been successfully persisted to our database.

Note that we aren't using the configurationObject variable we defined earlier in the code above. Obviously, we don't have to define all our configuration information inside of one anonymous Object. If we instead used our configurationObject variable, our call to fetch() would look like this:

fetch("http://localhost:3000/dogs", configurationObject);

Or we could also split out the body of our request into a variable:

const formData = {
  dogName: "Byron",
  dogBreed: "Poodle"
};

const configObj = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify(formData)
};

fetch("http://localhost:3000/dogs", configObj);

All three approaches yield the same results!

Note: As a security precaution, most modern websites block the ability to use fetch() in console while on their website, so if you are testing out code in the browser, make sure to be on a page like index.html or sample_form.html.

Handling What Happens After

Just like when we use fetch() to send GET requests, we have to handle responses to fetch(). As mentioned before, servers will send a Response that might include useful information. To access this information, we use a series of calls to then() which are given function callbacks.

Building on the previous implementation we might write the following:

const formData = {
  dogName: "Byron",
  dogBreed: "Poodle"
};

const configObj = {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify(formData)
};

fetch("http://localhost:3000/dogs", configObj)
  .then(function(response) {
    return response.json();
  })
  .then(function(object) {
    console.log(object);
  });

Notice that the first then() is passed a callback function that takes in response as an argument. This is a Response object, representing what the destination server sent back to us. This object has a built in method, json(), that converts the body of the response from JSON to a plain old JavaScript object. The result of json() is returned and made available in the second then(). In this example, whatever response.json() returns will be logged in console.log(object).

Let's go ahead and send the example above to our JSON server in the console; once the request is successfully resolved, you should see the following log:

{dogName: "Byron", dogBreed: "Poodle", id: 6} // Your ID value may be different

The JSON server is sending back the data we sent, along with a new piece of data, an id, created by the server.

When Things Go Wrong: Using catch()

When something goes wrong in a fetch() request, JavaScript will look down the chain of .then() calls for something very similar to a then() called a catch().

When something goes wrong in a fetch(), catch() will be called; this allows us to write code to "handle" the error. Say for instance, we forgot to add the HTTP verb to our POST request, and the fetch() defaults to GET. By including a catch() statement, JavaScript doesn't fail silently:

let formData = {
  dogName: "Byron",
  dogBreed: "Poodle"
};

// method: "POST" is missing from the object below
let configObj = {
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify(formData)
};

fetch("http://localhost:3000/dogs", configObj)
  .then(function(response) {
    return response.json();
  })
  .then(function(object) {
    console.log(object);
  })
  .catch(function(error) {
    alert("Bad things! Ragnarők!");
    console.log(error.message);
  });

If you try the code above in the console from index.html or sample_form.html, you should receive an alert window pop-up and a logged message:

Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.

While catch() may not stop all silent errors, it is useful to have as a way to gracefully handle unexpected results. We can use it, for instance, to display a message in the DOM for a user, rather than leave them with nothing.

Challenge

It's time to practice writing your own POST request using fetch(). In index.js, write a function, submitData, that takes two strings as arguments, one representing a user's name and the other representing a user's email.

The first two tests mirror the behavior of the JSON server. As you write your solution, keep the server running to test your code. Open index.html in a browser to gain access to your submitData function in console.

Note: The tests in this lab need access to the fetch() request inside submitData. In order to give them access, write your solution so that submitData returns the fetch(). You can do this by simply adding the return keyword in front of the fetch() request. This will not change the behavior of your fetch().

Test 1 - Send Data

In submitData, write a valid POST request to http://localhost:3000/users using fetch(). This request should include:

  • The destination URL
  • Headers for 'Content-Type' and 'Accept', both set to 'application/json'
  • A body with the name and email passed in as arguments to submitData. These should be assigned to name and email keys within an object. This object should then be stringified.

Test 2 - Handle the Response

On a successful POST request, expect the server to respond with a Response object. Just like we saw earlier in the dog example, the body property of this response will contain the data from the POST request along with a newly assigned id.

Use a then() call to access the Response object and use its built-in json() method to parse the contents of the body property. Use a second then() to access this newly converted object. From this object, find the new id and append this value to the DOM.

If JSON Server is running and index.html is open in the browser, you can test your code in the console: calling submitData() in the console should cause an id number to appear on the page.

Test 3 - Handle Errors

For this final test, after the two then() calls on your fetch() request, add a catch().

When writing the callback function for your catch(), expect to receive an object on error with a property, message, containing info about what went wrong. Write code to append this message to the DOM when catch() is called.

Conclusion

Congratulations! You can now use fetch() — the browser inside your browser's JavaScript environment — to both:

  • READ data using HTTP GET (and use the response to update the DOM)
  • SEND data using HTTP POST (and use the response to update the DOM)

With this we're ready to stitch together server updates (reads and updates) with DOM updating and event handling. We're almost ready to build the "Simple Liker" from scratch!

fewpjs-sending-data-with-fetch's People

Contributors

brewchetta avatar dependabot[bot] avatar elliott-king avatar ihollander avatar jenmyers avatar lizbur10 avatar maxwellbenton avatar paulnicholsen27 avatar

Stargazers

 avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fewpjs-sending-data-with-fetch's Issues

Test Malfunction

Hi, I sat with AAQ trying to get these tests to pass. The right result is showing up in the DOM. The coaches said my code looked like the solutions and they hunted for any issues in the test but couldn't figure it out after an hour. Here are the errors that are continuing to show up.
submitData()
(node:5606) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
✓ makes a POST request to /user with a name and email
{ id: 544, name: 'Steve', email: '[email protected]' }
1) handles the POST request response, retrieves the new id value and appends it to the DOM
{ id: 546, name: 'Sam', email: '[email protected]' }
2) handles a failed POST request using catch, appends the error message to the DOM

1 passing (1s)
2 failing

  1. submitData()
    handles the POST request response, retrieves the new id value and appends it to the DOM:
    AssertionError: expected '544' to include 546
    at Context. (test/indexTest.js:72:11)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

  2. submitData()
    handles a failed POST request using catch, appends the error message to the DOM:
    AssertionError: expected '546' to include 'Unauthorized Access'
    at Context. (test/indexTest.js:89:11)

README is unclear about where to write its example code, if at all

To begin, the Analyze Data Sent in an HTML Form section near the top says:

We should expect that our "mini-browser," fetch() will need those same bits of information in order to send data to the server. Let's place this data inside our form() skeleton.

I'm not clear on what or where the form() skeleton is, unless it's referring to the sample_form.html file.

The lesson then has us build a configurationObject and use it to make a POST request with fetch() like this:

fetch("http://localhost:3000/dogs", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json"
  },
  body: JSON.stringify({
    dogName: "Byron",
    dogBreed: "Poodle"
  })
});

This is in the Send the POST Request section, halfway through the lesson. I had assumed that we were supposed to write this in the index.js file, but the README doesn't say. But here's what it does say (in the same section as the code above):

With the JSON server running, if you open up sample_form.html or index.html, you can use the above to successfully send a POST request and persist data to db.json.

If I write that fetch() code in the index.js file, then opening index.html makes a POST request with that code. However, opening sample_form.html does not; instead, it makes a POST request with its HTML form (which I think it's supposed to do).

Further down in that section, the README also says this:

Note: As a security precaution, most modern websites block the ability to use fetch() in console while on their website, so if you are testing out code in browser, make sure to be on a page like index.html or sample_form.html.

Does that mean that we were supposed to write out that fetch() code in the DevTools console, and not in the index.js file? If that's the case, do NOT try this on the sample_form.html page! As I discovered, that seemed to cause http://localhost:3000 to freeze (i.e. take forever to load) for whatever reason, even after stopping json-server, and I had to re-clone the lab. However, it worked OK with the console in the index.html file.

All of this is before the actual "lab" part itself. I'm just not clear on where or if we were supposed to write that fetch() code above.

Thanks a lot for looking into this!

--- Sdcrouse

errors running npm install -g json-server

when running npm install -g json-server

the following errors were received

`npm WARN checkPermissions Missing write access to /usr/local/lib
npm ERR! code EACCES
npm ERR! syscall access
npm ERR! path /usr/local/lib
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, access '/usr/local/lib'
npm ERR! { [Error: EACCES: permission denied, access '/usr/local/lib']
npm ERR! stack:
npm ERR! 'Error: EACCES: permission denied, access '/usr/local/lib'',
npm ERR! errno: -13,
npm ERR! code: 'EACCES',
npm ERR! syscall: 'access',
npm ERR! path: '/usr/local/lib' }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR! /home/trader/.npm/_logs/2020-10-08T12_46_45_969Z-debug.log`

After much agony, found that running sudo npm install -g json-server gets the job done. Please add this help to the page so other users don't waste hours like I just did. A Unix newb :)

Update to body text - word replacement

Last paragraph in this README - should be "Simple Liker" not "Simple Looker"
Last paragraph:
"With this we're ready to to stitch server updates (reads and updates) with DOM updating and event handling. We're almost ready to build the "Simple Looker" from scratch!"

Instructions typo, write a method

Line 400 of Readme says
``index.js, write a method, submitData`, that takes two strings arguments, one`

Should be "write a function" instead

issue when json-server --watch db.json

Hi,

I got this issue solved but I though I would share since I'm not the only one in my cohort who ran into this problem.
When I run the command "json-server --watch db.json" in the terminal, it throws me an error.
Apparently it is because the json server didn't install globally
The fix is to run the command "sudo npm install -g json-server"

Regards
Aude

running `npm install` or `learn` didn't actually install `json-server`

Description
The instructions in this lab say to run npm install or learn once to get json-server to install. I did this and then when trying to run the next command in the instructions (json-server --watch db.json), bash gave me an error:
[16:22:49] (master) fewpjs-sending-data-with-fetch-online-web-ft-061019 // $ json-server --watch db.json bash: json-server: command not found

I ended up looking at the Readme of the json-server github repo and found the following command to manually installed it npm install -g json-server.

I was then able to run the json-server --watch db.json successfully.

Expectation
The lab would have worked by following the specified instructions

Actual Result
Had to do some troubleshooting and install json-server manually then was able to continue with the lab.

Suggestions to help align curriculum and lecture

These are some suggested changes to help align these lab's teachings with what students are learning in lecture.

Issue

  • Test's do not require a user to add anything to the POST request other than the URL and method: 'POST' in order get back the correct response.
  • Not a "real" endpoint makes this very hard to debug and work through in the console (we try and stress these tools in class)
  • The lab does not discuss or require the use of headers in a POST request.
  • The fetch methods asks to post keys of :firstName and :registryMessage but returns a key :message

Suggested Improvement

  • Add a test to verifying that a body is included in the post request
  • Add a discussion of headers to the README
  • Set up a really quick, RESTful, testable endpoint using JSON Server
  • Use JSON.stringify() for the body

#staff

Test 4 example

Thanks for raising this issue! Future learners thank you for your diligence. In
order to help the curriculum team address the problem, please use this template
to submit your feedback. We'll work on addressing the issue as soon as we can.

Please fill out as much of the information below as you can (it's ok if you
don't fill out every section). The more context we have, the easier it will be
to fix your issue!

Note: you should only raise issues related to the contents of this lesson.
If you have questions about your code or need help troubleshooting, reach out to
an instructor/your peers.


Link to Canvas

https://learning.flatironschool.com/courses/2620/assignments/76884?module_item_id=148426

What should be changed?

For test 4, can you show a syntax-correct example of what that would look like? Even though we will use it later, may be good to see the path ahead.

Additional context

Add any other context about the problem here.

Submit key value pair

Because there is a name="submit" in the sample form, it is creating a key value pair of submit: "Submit" when an input is submitted.

`


Dog Name:

Dog Breed:

`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.