Module 3: Writing to DynamoDB + HTTP

Module Overview

Learn how to write data to DynamoDB and understand the fundamentals of HTTP for building RESTful services.

Learning Objectives

  • Implement and annotate a Java POJO that represents any item from a provided DynamoDB table
  • Implement and annotate a Java POJO that represents any item from a provided DynamoDB table, where the names of some of its fields differ from the corresponding names of attributes of the DynamoDB table
  • Implement functionality that retrieves a unique item by partition key from a provided DynamoDB table
  • Implement functionality that retrieves a unique item by partition and sort key from a provided DynamoDB table
  • Implement functionality that creates an item in a provided DynamoDB table
  • Implement functionality that modifies an existing item in a provided DynamoDB table
  • Master the techniques for writing and updating data in DynamoDB
  • Understand HTTP fundamentals including methods, status codes, and headers
  • Learn how to implement create and update operations using DynamoDB
  • Explore transaction support in DynamoDB for atomic operations
  • Understand the relationship between HTTP operations and database operations

DynamoDB Saving

In the previous readings, you learned how to use annotations and a DynamoDBMapper to map an item from DynamoDB to a Java Class and how to load an item from a DynamoDB table into your Java program. Now it's time to learn how to save an item created or modified in Java back to your DynamoDB table.

Saving Items to a Table

Let's review the Java class Song, which represents items in the Songs DynamoDB table.

@DynamoDBTable(tableName = "Songs")
public class Song {
    private String artist;
    private String songTitle;
    private String genre;
    private Integer year;
    private Boolean isFavorited;

    @DynamoDBHashKey(attributeName = "artist")
    public String getArtist() { 
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist; 
    }

    @DynamoDBRangeKey(attributeName = "song_title")
    public String getSongTitle() { 
        return songTitle;
    }

    public void setSongTitle(String songTitle) {
        this.songTitle = songTitle; 
    }
    
    @DynamoDBAttribute(attributeName = "genre")
    public String getGenre() { 
        return genre;
    }

    public void setGenre(String genre) { 
        this.genre = genre;
    }

    @DynamoDBAttribute(attributeName = "year")
    public Integer getYear() { 
        return year;
    }

    public void setYear(Integer year) { 
        this.year = year;
    }

    @DynamoDBAttribute(attributeName = "isFavorited")
    public Boolean isFavorited() { 
        return isFavorited;
    }

    public void setFavorited(Boolean isFavorited) { 
        this.isFavorited = isFavorited;
    }
}

As a reminder, the annotations in the Song class map the partition key and sort key to the appropriate fields (indicated by the annotations @DynamoDBHashKey and @DynamoDBRangeKey, respectively) and @DynamoDBAttribute annotations are used to map the rest of our fields to the attributes in DynamoDB. We will always use the attributeName parameter to explicitly define what attribute we are referencing in DynamoDB.

The Song class represents an item from the corresponding Songs table:

artist song_title genre year isFavorited
Black Eyed Peas I Gotta Feeling Pop 2009 false
Linkin Park Numb Rock 2003 true
Black Eyed Peas Pump It Pop 2005 true
Eminem Not Afraid rap 2010 false
Daddy Yankee Gasolina latin pop 2004 true

If we want to load the song "Numb" by Linkin Park we would use the following code:

DynamoDBMapper mapper = new DynamoDBMapper(DynamoDbClientProvider.getDynamoDBClient());
Song numb = mapper.load(Song.class, "Linkin Park", "Numb");

The second line uses the load() method in DynamoDBMapper to search for an item in DynamoDB and, if found, populate a new instance of the class given as the first argument, which in this case is Song. The load() method finds an item from the Songs table whose partition and sort keys match the given keys, "Linkin Park" and "Numb".

What happens, however, if we attempt to load an item that does not exist? If we want to load the song "In the End" by Linkin Park, we would instead write:

Song inTheEnd = mapper.load(Song.class, "Linkin Park", "In the End");

That song, however, does not currently exist in our table. We briefly touched on this in the previous reading on loading, but never saw it in action. When you attempt to load a value that doesn't exist, DynamoDBMapper will return null. If we want that item in our table, we will need to create and save that item to the table ourselves.

To save the song "In the End" to our table we can use the DynamoDBMapper save() method. First, we will create an instance of the Song class and set the values, then we'll save it to DynamoDB:

Song inTheEnd = new Song();
inTheEnd.setArtist("Linkin Park");
inTheEnd.setSongTitle("In the End");
inTheEnd.setGenre("rock");
inTheEnd.setYear(2000);
inTheEnd.setFavorited(false);

DynamoDBMapper mapper = new DynamoDBMapper(DynamoDbClientProvider.getDynamoDBClient());
mapper.save(inTheEnd);

First, we create inTheEnd, a new empty instance of the Song class. The next few lines use the setter methods to set the values we want on our song, artist, songTitle, genre, year, and isFavorited. Finally, we create our DynamoDBMapper as we have in previous examples, then call save() with our new song. Unlike the load() method where you explicitly pass in the key values, the save() method uses the key values that are set in our Song instance. Since the annotations in our Song class say that artist and songTitle are the key values, the save() method knows that "Linkin Park" and "In the End" are the key values for this specific instance by looking in the object itself using the getter methods. The updated table would now look like this, with our new item in bold:

artist song_title genre year isFavorited
Black Eyed Peas I Gotta Feeling pop 2009 false
Linkin Park Numb rock 2003 true
Black Eyed Peas Pump It pop 2005 true
Eminem Not Afraid rap 2010 false
Daddy Yankee Gasolina latin pop 2004 true
Linkin Park In the End rock 2000 false

In a previous lesson, we discussed how the DynamoDB annotations acted as a translator between Java and a DynamoDB table. For the DynamoDBMapper load() method, the annotations helped translate mapping from a table item to our class. These annotations are also very important for the DynamoDBMapper save() method and provide the same purpose, only now DynamoDBMapper is using them to map from our class to save a new item in the table.

Notice that in load(), we provide a reference to the class we will load into, e.g. Song.class, while in save() we provide a specific instance of a class. This is because in load() we are specifying what class DynamoDBMapper should create a new instance of. In save(), we already have a specific instance, and we're asking DynamoDBMapper to take that instance's values and write them to DynamoDB.

The first annotation in your class, @DynamoDBTable, uses the parameter tableName to indicate to DynamoDBMapper which table to save the item to. In our Song example, tableName = "Songs", indicating to DynamoDBMapper that the new song item we've created should be saved to the Songs table. The other annotations function the same as they did for the load() method---they provide information for the key definitions and attribute names, based on the values set in the class instance that's being saved. Properly annotating your classes is important to allow the DynamoDBMapper to obtain the information needed to both save and load to the DynamoDB table.

Updating Items in a Table

Let's look at our Songs table again, including the new item we saved in the last section:

artist song_title genre year isFavorited
Black Eyed Peas I Gotta Feeling pop 2009 false
Linkin Park Numb rock 2003 true
Black Eyed Peas Pump It pop 2005 true
Eminem Not Afraid rap 2010 false
Daddy Yankee Gasolina latin pop 2004 true
Linkin Park In the End rock 2000 false

You've been listening to Linkin Park's song "In the End" nonstop for the past week and it's become your new favorite song and you want to reflect that by favoriting it. It's possible to update an existing item using the same DynamoDBMapper save() method we used to create it. The behavior of the save() method depends on whether the primary key values for the item already exist in the table. For a table that uses only a partition key as the primary key, the save() method checks if an item already exists in the table that has the same partition key value as the item being saved. If it does, it will update that item instead of creating a new one. For our Songs table, which uses a composite primary key of partition + sort key, the save() method checks if an item already exists in the table that has the same partition and sort key as the item being saved. If it does, it will update that item instead of creating a new one. If you save an item that has the exact same values as an item that already exists in the table, nothing will happen because there is nothing to update.

To update the Linkin Park song "In the End" in our table, we could create a new song instance that's the same as the item that already exists except the isFavorited value is true. The code for updating the item in this way looks like the following:

Song inTheEnd = new Song();
inTheEnd.setArtist("Linkin Park");
inTheEnd.setSongTitle("In the End");
inTheEnd.setGenre("rock");
inTheEnd.setYear(2000);
inTheEnd.setFavorited(true);

DynamoDBMapper mapper = new DynamoDBMapper(DynamoDbClientProvider.getDynamoDBClient());
mapper.save(inTheEnd);

The Songs table now looks like this:

artist song_title genre year isFavorited
Black Eyed Peas I Gotta Feeling pop 2009 false
Linkin Park Numb rock 2003 true
Black Eyed Peas Pump It pop 2005 true
Eminem Not Afraid rap 2010 false
Daddy Yankee Gasolina latin pop 2004 true
Linkin Park In the End rock 2000 true

The Songs table uses a composite primary key so the save() method is looking at both the partition key ("Linkin Park") and the sort key ("In the End") when saving the item. Since this composite key matches an item that already exists in the table, the save() method updates that item in the table instead of creating a new one.

When updating an item, it may work to instantiate a new object, then populate all of the fields from scratch before calling save() to update it. This works great for creating a new item, but when we are updating an existing item, we don't want to have to know in advance all the same values again, even if they aren't actually changing. Instead of using the save() method alone, we generally want to use the load() and save() methods together to update an item. We'll first load() the item to get all the existing attributes, then update only the ones we need to in code before calling save().

We can rewrite the previous code to get an item using the load() method, modify only necessary fields, and then use the save() method to safely update the item, knowing the other fields are unchanged. We could also add error handling to throw an exception if the load() method returns a null value, meaning that the song we attempted to load does not exist in the table.

DynamoDBMapper mapper = new DynamoDBMapper(DynamoDbClientProvider.getDynamoDBClient());
Song inTheEnd = mapper.load(Song.class, "Linkin Park", "In the End");
if (Objects.isNull(inTheEnd)) {
    throw new Exception("Couldn't find a song with those key values);
} else {
    inTheEnd.setFavorite(true);
    mapper.save(inTheEnd);
}

To sum it up: the DynamoDBMapper save() method looks at the partition key or composite key of the item being saved and determines if it already exists in the table or not. If the key does not exist, it creates a new item in the table with the attribute values provided in the Java object that's passed in. If it does exist, DynamoDBMapper updates the item with the matching primary key. Use the DynamoDBMapper load() method to first load the item from the table, then modify only the necessary fields, then call save() to update the item in the table.

Adding and Removing Attributes

Attributes in DynamoDB are fluid. This means that, besides defining the primary key (be that a partition key or a composite key), DynamoDB tables do not enforce a strict set of attributes that an item must contain. For any non-key attributes, they can exist or not.

Let's repeat our first update from above, but this time only set the key values and the new value for isFavorited that we care about.

Song inTheEnd = new Song();
inTheEnd.setArtist("Linkin Park");
inTheEnd.setSongTitle("In the End");
inTheEnd.setFavorited(true);

DynamoDBMapper mapper = new DynamoDBMapper(DynamoDbClientProvider.getDynamoDBClient());
mapper.save(inTheEnd);

The Songs table now looks like this:

artist song_title genre year isFavorited
Black Eyed Peas I Gotta Feeling pop 2009 false
Linkin Park Numb rock 2003 true
Black Eyed Peas Pump It pop 2005 true
Eminem Not Afraid rap 2010 false
Daddy Yankee Gasolina latin pop 2004 true
Linkin Park In the End true

"In the End" by Linkin Park no longer has a genre or year defined. Note, it's not that these attributes have been set to null. As far as DynamoDB is concerned, those attributes don't exist for the "In the End" item.

By the same token, we can create new attributes in the table by saving an item with an attribute that didn't exist before.

Say we updated out Song class to include a new field, album, with the appropriate DynamoDB annotations:

@DynamoDBTable(tableName = "Songs")
public class Song {
    // Existing fields and methods omitted for brevity

    ...
    
    String album;

    @DynamoDBAttribute(attributeName = "album")
    public String getAlbum() {
        return album;
    }

    public void setAlbum(String album) {
        this.album = album;
    }
}

With our new version of Song, we could update "In the End" to include its album:

Song inTheEnd = new Song();
inTheEnd.setArtist("Linkin Park");
inTheEnd.setSongTitle("In the End");
inTheEnd.setFavorited(true);
inTheEnd.setAlbum("Reanimation")

DynamoDBMapper mapper = new DynamoDBMapper(DynamoDbClientProvider.getDynamoDBClient());
mapper.save(inTheEnd);

The Songs table now looks like this:

artist song_title genre year isFavorited album
Black Eyed Peas I Gotta Feeling pop 2009 false
Linkin Park Numb rock 2003 true
Black Eyed Peas Pump It pop 2005 true
Eminem Not Afraid rap 2010 false
Daddy Yankee Gasolina latin pop 2004 true
Linkin Park In the End true Reanimation

DynamoDB gracefully creates a new attribute in our table, and our existing items just don't have that attribute. This is part of why we will always specify attributeName in our annotations. If we don't include attributeName but change the name of our variables, we can end up with multiple attributes in DynamoDB for what should be the same field, making it impossible to load an item that was saved as a previous version of our class.

The ability to have only some attributes defined for an item allows for more complex behaviors that we'll cover in later units. For the time being, all our tables should have every attribute defined for each item.

DynamoDB Capacity

When we configure DynamoDB tables, we set how much we can access the table. This is set with Read Capacity Units (RCUs) and Write Capacity Units (WCUs). RCUs track the work taken to read an item from your table. One RCU represents reading one item up to 4 KB in size per second. WCUs track the work taken to write an item to your table. One WCU represents writing one item up to 1 KB in size per second. If our items are larger than these sizes, and as we make more requests pre second, we consume more Capacity Units. We can see how many RCUs and WCUs we are consuming in the "Metrics" tab of the DynamoDB console for our table.

Determining the best settings for read and write capacity on a DynamoDB table is hard. For one, we usually don't really know what our usage will be until we start using our service. We also often see peaks and valleys in usage over the course of a day or week. Setting our capacity to handle our highest peak is wasteful the rest of the time.

Thankfully, DynamoDB provides a solution where we don't need to determine our capacity in advance. DynamoDB on-demand capacity will automatically ramp our table up and down depending on how many read and write requests are being received. We will always set our DynamoDB capacity to on-demand.

Writing Data to DynamoDB

The DynamoDB Enhanced Client provides several operations for creating and updating items in DynamoDB tables:

Creating New Items

To add a new item to a DynamoDB table:


// Create a new MusicItem instance
MusicItem newSong = new MusicItem();
newSong.setArtist("Imagine Dragons");
newSong.setSongTitle("Radioactive");
newSong.setAlbumTitle("Night Visions");
newSong.setYearReleased(2012);
newSong.setGenres(new HashSet<>(Arrays.asList("Alternative Rock", "Indie Rock")));

// Save the item to DynamoDB
musicTable.putItem(newSong);
System.out.println("Added new song: " + newSong.getSongTitle() + " by " + newSong.getArtist());
                

The putItem method will overwrite any existing item with the same key, so be careful when using it on items that might already exist.

Updating Existing Items

There are two main approaches to updating items:

  1. Complete Replacement - Using putItem to replace the entire item
  2. Partial Update - Using updateItem to modify specific attributes

For partial updates, you can use the UpdateItemEnhancedRequest:


// Create a key for the item to update
Key key = Key.builder()
    .partitionValue("Imagine Dragons")
    .sortValue("Radioactive")
    .build();

// Define the update expression
UpdateItemEnhancedRequest<MusicItem> updateRequest = UpdateItemEnhancedRequest
    .builder(MusicItem.class)
    .key(key)
    .updateExpression(Expression.builder()
        .expression("SET yearReleased = :year, genres = :genres")
        .expressionValues(singletonMap(":year", AttributeValue.builder().n("2013").build()))
        .expressionValues(singletonMap(":genres", AttributeValue.builder()
            .ss(Arrays.asList("Alternative Rock", "Electronic Rock"))
            .build()))
        .build())
    .build();

// Execute the update
musicTable.updateItem(updateRequest);
                

Introduction to RESTful APIs and Supporting Technologies

What is a RESTful API?

Figure 1: Diagram representation of HTTP Request Model, showing a client requesting a webpage and receiving an HTML response.

Figure 1: Diagram representation of HTTP Request Model, showing a client requesting a webpage and receiving an HTML response

You may have heard of an API before, and you may have even heard of a REST API, but what does that mean? An API, or Application Programming Interface, is an interface that simplifies the implementation and maintenance of software. Think of it like a Java Interface on a larger scale. A Java Interface can help you understand how to interact with a particular class correctly, whereas an API helps you understand how to interact with a server correctly.

A RESTful service, or RESTful API, is a document interface that allows two systems to request and send data over HTTP. Many web-based systems have RESTful APIs available for us, such as Twitter, Git, and AWS. Even connecting to a website using a web browser is a RESTful transaction! Before we dig into the mechanics of how to use a RESTful API, we want you to understand more about what is going on behind the scenes. All RESTful APIs are built on top of HTTP, so we're going to start at the bottom and learn how computers communicate in order to bring us to the RESTful API!

What is HTTP?

You may have noticed that website addresses begin with 'http' or 'https' such as https://www.amazon.com/, but what does that 'http' actually mean? HTTP stands for HyperText Transfer Protocol and is the underlying protocol used by the World Wide Web. HTTPS is a Secure version of HTTP but for our purposes works the same as HTTP. In order for computers to talk to each other, they need to share a common method of communication. When your computer wants to view a website being served from another computer, they need to agree on how to talk. A protocol is a shared and agreed upon common format for transmitting data between devices over the internet. The HyperText Transfer Protocol happens to be the most common protocol on the web and works as a request-response format between a client and server.

Let's say you're at home, going through this prework and you have questions about how a developer can interact with AWS. One way you might start is by going to the AWS Documentation site and looking up the resources Amazon provides. The documentation website is a huge storehouse of information, so much so that it's too much information for your personal computer. You have room for some of that documentation, but there are so many other topics you might need to dig into that you can't store them all. Thankfully, it's available as a website that anyone can visit. Visiting the developer site is an example of using HTTP.

The client is a computer hardware device or software that requests data from a service made available by a server. A server, or host, is a separate computer dedicated to running services to provide information or functionality to clients. In this case, the client is your development laptop and the server is the computer that is hosting the Amazon developer site. There are many kinds of servers depending on the service running on it, such as an API server, email server, file server, database server, print server, or web server. The developer site would be running on a web server, since it's a website.

This scenario is a common client-server system you might have interacted with. It is not the only scenario though. In another one, the client could be a Java program that you write that is communicating to another program using a RESTful API. Server to server communication is very common, particularly in the business world. They may take turns being the client and being the server, depending on which is asking for data and which is providing it. This is often the foundation of how large systems, like AWS, are built.

Communicating Over HTTP

Let's take a closer look at our initial example. In order for our computer to talk to Amazon's server there are a few steps we need to walk through. This would be the case with any HTTP request, be it viewing a website or making a RESTful API call. The most important part of any HTTP request is making sure it gets to the right server. If it never receives your request then you'll never receive a response. We're going to take a close look at how we can figure out the right server to contact when we make an HTTP request.

IP Address

Every computer on the Internet has an address like every building has an address. Just like a building address is used to help people find your house and send you mail; an IP address is used so that a computer can be found in order to send a server a request or to send a response back to a client. The IP address functions as the computer's home address.

An Internet Protocol (IP) address is a numerical designation that's assigned to each device connected to a computer network that uses the Internet Protocol for communication. The World Wide Web uses the Internet Protocol and HTTP works on top of it. Without IP addresses, HTTP doesn't know where to send a request.

One way to find your public IP Address is to use Google and search for the term "IP address". If you go ahead and do this, you'll see four numbers each divided by a dot (.) This format is referred to as IPv4 (Internet Protocol version 4). Each of the four numbers can range in value from 0 to 255. An example IP address is: 192.168.1.1.

The most recent version of the Internet Protocol is IPv6, which was created to address the problem of reaching the maximum number of IPv4 addresses. The internet is slowly moving to IPv6 as the standard but for now we won't go deeply into it. It's enough to know that IPv6 will support a LOT more addresses.

Domain Name Server (DNS)

So, to get back to our example, we now know that we need to find the IP address of the developer site before we can make a request to the server. You may have used this site before and probably don't remember ever having to type anything that looked like the IP addresses we described above. Let's look at why you haven't seen IP addresses very often, if at all.

In real life, there are probably some building addresses you have memorized, such as your own home and where you work. You might know the address of your friend's house, but you probably don't know the address of your grocery store off the top of your head. With the use of phone contact lists, however, you can store the address as "Carlos' house" or "AnyCompany's Office." This way you don't need to remember them all.

If it's difficult to remember a list of building addresses, imagine how difficult it would be to remember the IP addresses of websites you interact with daily! Luckily, a contact list of human readable names exists for IP addresses, as well. This contact list is known as a Domain Name Server (DNS) and the human readable name for an IP address is called a fully qualified domain name (FQDN). So, when you type 'developer.amazon.com' into your browser, you are using the FQDN for the developer site. This is much easier to remember than 52.46.133.116 -- the site's IP address. Glad we don't have to remember it ourselves.

At the core of both the DNS and FQDN is the word domain. A domain is the website name where Internet users can access the website and is used for finding and identifying computers on the Internet. The Domain Name Server (DNS) is a group of computers that contain mappings of domain names to their IP addresses. So, when you type the domain name developer.amazon.com into your web browser, the DNS sees that it corresponds to the IP address of the server that the website lives on. Your computer talks to the DNS, telling it the domain name that you typed in. The DNS responds with the IP address of the server you want to contact. This process is known as resolving the domain name. You may notice the "request-response" pattern here too. Talking to the DNS also uses HTTP and follows the same model.

You can perform a DNS Lookup through the Command Prompt using the nslookup command:

ATA-Mac:\~ ata-user$ nslookup developer.amazon.com
Server: 172.18.193.221
Address: 172.18.193.221#53

Name: developer.amazon.com
Address: 52.46.133.116

In this example we're using nslookup to find the IP address for 'developer.amazon.com'. The first section tells us the server the command was run from and the IP address of the computer used to run the command. The second section tells us the domain name of the website entered and then the IP address associated with that domain name. These are the actions that are going on in the background each time you go to a website in your browser.

Notice that our domain name has three parts to it: "developer"."amazon"."com". Everyone knows "amazon.com" but "developer.amazon.com" goes to a completely different site (and server) than "amazon.com" does. Yet their names are so close! Since each FQDN is just a shortcut in our contact list to an IP address there's no need for these two sites to share the same server. This is just a way of organizing domains. The "com" part is called the top-level domain, this is the major grouping and is supposed to specify what type of domain it is. For example, "com" specifies a "company" or business versus "edu" which specifies a school or educational site. "amazon" is the main domain and "developer" is the sub-domain. Again, this is just a way of organizing domains so people know that the developer site is part of Amazon.

When we are developing on our own computers and want to test services we have running locally, there is a special domain name reserved that we can use to access ourself, localhost. localhost is mapped to the IP address 127.0.0.1, which always points to the requesting machine. Sending a request to localhost will route the request back to the machine the request was made from. Additionally, localhost's special definition means that it is automatically resolved to IP address 127.0.0.1 without requiring a DNS request.

Ports

The parts of the FQDN are designed to get us to the address (server) we're looking for, but we often need to find not just a building, but a specific suite or apartment within that building. Similarly, we often need to connect not just to a computer, but a specific service on a computer. This is done using a port. A port provides more detail to an IP address in order to identify a specific process or network service running on the computer at that IP address. There can be many services on one server that each have their own port assigned; such as, different RESTful APIs, web servers, and email servers.

The port number is not always shown, but it is always being used. Some port numbers are so standard that many applications will assume a port number unless you specifically override it to connect to a different port. If you're interested, the default ports used by webservers are port 80 (HTTP) and port 443 (HTTPS).

Universal Resource Locators (URLs)

We're so close now, we almost have all the information we need to make an HTTP request. We have our protocol (HTTP), we have our FQDN and we even have a port to use. All of that will get us to the correct server, but we'll still need to make a request for specific information. Let's assume we've gone to "developer.amazon.com" and clicked on a few links until we've reached the documentation for an AWS service like CodeCommit. The location bar in our browser now shows:

https://docs.aws.amazon.com/codecommit/?id=docs_gateway

This entire string is known as the Universal Resource Locator (URL). You may have heard the term URL thrown around before, but you may not have known what it means. Every piece of data being passed back and forth on the internet can be thought of as a "resource". The website text, the images, they're all resources that need to be specified (or "located") before they can be sent along to the requesting computer. The URL is designed to be a way to identify these resources for use in HTTP. We mentioned images and text, but the URL can also specify a request for an API to return a data set.

Let's break down the URL above for the CodeCommit documentation.

Figure 2: Diagram of a URL, labeling its component parts: protocol, fqdn, subdomain, domain, top level domain, port, endpoint, and parameters.

Figure 2: Diagram of a URL, labeling its component parts: protocol, fqdn, subdomain, domain, top level domain, port, endpoint, and parameters.

The first piece of the URL is 'https://' which is what defines the HTTP protocol that is used for the request. The next piece is 'docs.aws.amazon.com' which is the FQDN that will be used to lookup the IP address. In this example we've included the port number, which is '443'. This is the port for the web server and would not be shown in your browser. The next part '/codecommit/' is the endpoint which is the particular resource on the web server, and the last part, '?id=docs_gateway', is the parameters that together direct you to a specific page or API method on the site. We'll look at those last pieces more closely in the next reading. For now, you can think of them as pointing to which drawer in which room of the apartment the resource is located.

This is the same way that we will specify the server hosting the RESTful API and which part of the API we want to access. We've thrown a lot of information at you in this reading. The Try Activities should help reinforce these concepts before we start to look at how the HTTP requests are actually formed and how we use them to talk to a RESTful API.

Guided Project

Additional Resources