Skip to content
Ameen Abozaid
PublishedBEG-052-L06

URLs and query parameters

  • Lesson 6 of 7
  • 10 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

The order slip needs an address: which restaurant, which counter, and which dish. In HTTP that address is the URL. You met the parts of a URL in BEG-003; this lesson works with them the way a backend developer does, asking of each part: who reads it, and what is it for?

Two pieces matter most when you design and read APIs. The path names the resource (/books/7). The query parameters after the ? filter, sort, or tune the answer (?author=herbert&page=2). Learning to keep those two jobs apart makes URLs predictable, and lets you read any address you see in the browser's Network tab.

This lesson builds on lesson 1 (the request target), lesson 2 (nouns in paths), and BEG-003 (scheme, host, port, fragment).

Learning Objectives

By the end of this lesson you will be able to:

  • Split a URL into scheme, host, port, path, query, and fragment.
  • Read a query string as name=value pairs joined by &.
  • Explain which part identifies the resource and which part refines the request.
  • Explain that the fragment is handled by the browser and is not sent to the server.
  • Explain why special characters in a URL are written in an escaped form such as %20.
  • Extract the parts of a URL in Java with java.net.URI (a preview of a standard JDK class).

Written Explanation

The parts

text
https://api.example.com:8443/books/7?author=herbert&page=2#top
|       |               |    |       |                     |
scheme  host            port path    query                 fragment
PartValueJob
SchemehttpsWhich protocol to use
Hostapi.example.comWhich server (a domain name or an IP address)
Port8443Which door on that server; usually omitted
Path/books/7Which resource on the server
Queryauthor=herbert&page=2Extra key/value details for the request
FragmenttopA position inside the resource, used by the browser

MDN's description: the port may be left out when the standard ports are used (80 for HTTP, 443 for HTTPS), the path is these days usually handled by the server rather than being a literal file path, and query parameters are key/value pairs separated by &, which the server decides how to interpret.

The path identifies the resource

A good path is a noun, in the style of lesson 2: /books is the collection, /books/7 is one item, and /books/7/reviews could be the reviews of that item. Two different paths are two different resources. Changing the path changes what you are talking about.

The query refines the request

After a ?, the query holds name=value pairs, separated by &:

text
/books?author=herbert&page=2

This still means "the books collection" but asks for those by one author, second page. Query parameters usually express optional choices: filtering (author=herbert), sorting (sort=title), paging (page=2), and similar. Leave them off and you get the default behaviour.

A rule of thumb: if the thing is which resource, it belongs in the path; if it is how you want to see it, it belongs in the query. /books/7 is book 7. /books?year=1965 is the books from 1965. The split is a convention that servers follow, not something the protocol enforces.

The fragment stays in the browser

The part after # points to a place inside the resource, such as a heading on a page. MDN states that the fragment is never sent to the server with the request: the browser handles it locally. So a server cannot read the fragment, and you must not rely on it for data your backend needs. In the request line you saw in lesson 1, only the path and query appear:

text
GET /books?author=herbert&page=2 HTTP/1.1
Host: api.example.com

Special characters and escaping

URLs can only contain a limited set of characters. A space, an & inside a value, or non-Latin text must be written in an escaped form, called percent-encoding, where a character becomes % plus two hexadecimal digits. A space is %20: ?q=science%20fiction carries the value "science fiction". Browsers and libraries usually do this for you, and the server decodes it. Java's URI shows both forms: the raw one keeps %20, the decoded one shows a space (see Example 3). (The JDK documentation and a test run confirm this; the full list of reserved characters is beyond this lesson.)

Reading URLs in the wild

Real-world URLs can look intimidating: a long address from a shopping site may have a dozen query parameters with cryptic names such as ref= or utm_source=. Do not panic. Read left to right: scheme, host, then the path up to the first ?, then each name=value pair between & signs, and finally anything after #. Most of the length is usually tracking or display options. What matters to the backend is the path plus the parameters the server actually reads, and a server may simply ignore parameters it does not know. A port number in the middle (:8080, :3000) is common while a service is being developed on your own machine, which is why you will often type addresses like that during the Java web modules.

URL versus URI

You will see both words. A URI is a general identifier for a resource; a URL is a URI that also says where and how to get it. For daily work they are used almost interchangeably, and Java's class is called URI.

Examples

Example 1: Split a URL (simple)

https://shop.example.com/books/7?lang=en#reviews

  • Scheme: https
  • Host: shop.example.com
  • Port: none written (HTTPS default, 443)
  • Path: /books/7
  • Query: lang=en
  • Fragment: reviews (not sent to the server)

Example 2: Path or query? (practical)

You are designing endpoints for a library API.

NeedURLWhy
Show member 12/members/12Identifies one resource: path
Show members in the city of Jeddah/members?city=JeddahA filter on the collection: query
Second page, 20 per page/members?page=2&size=20How to slice the result: query
Loans of member 12/members/12/loansA sub-resource belonging to the member: path
Overdue loans of member 12/members/12/loans?status=overdueResource in the path, filter in the query

Notice how every row reads naturally as "which thing, then how to look at it". The names (page, size, status) are choices of the API designer.

Example 3: Pick a URL apart in Java (realistic)

java.net.URI is a ready-made JDK class that parses a URL. It is a preview here: you only need to know how to construct it and call getters.

java
import java.net.URI;

public class L06 {
    public static void main(String[] args) {
        URI u = URI.create("https://api.example.com:8443/books/7?author=herbert&page=2#top");
        System.out.println("scheme   = " + u.getScheme());
        System.out.println("host     = " + u.getHost());
        System.out.println("port     = " + u.getPort());
        System.out.println("path     = " + u.getPath());
        System.out.println("query    = " + u.getRawQuery());
        System.out.println("fragment = " + u.getFragment());
        for (String pair : u.getRawQuery().split("&")) {
            String[] kv = pair.split("=", 2);
            System.out.println("  " + kv[0] + " = " + kv[1]);
        }
        URI plain = URI.create("https://api.example.com/books");
        System.out.println("no port -> " + plain.getPort());
    }
}

Output (compiled and run with JDK 21):

text
scheme   = https
host     = api.example.com
port     = 8443
path     = /books/7
query    = author=herbert&page=2
fragment = top
  author = herbert
  page = 2
no port -> -1

Per the JDK documentation, getPort() returns -1 when the URI has no port, and getRawQuery() returns null when there is no query (so split on it would throw; check first). A second run shows how a request target is built from a URL, and how escaping looks:

java
URI u = URI.create("https://api.example.com/books?author=herbert&page=2#top");
String target = u.getRawPath() + "?" + u.getRawQuery();
System.out.println("GET " + target + " HTTP/1.1");
System.out.println("Host: " + u.getHost());
URI s = URI.create("https://shop.example.com/search?q=science%20fiction");
System.out.println(s.getRawQuery());   // raw
System.out.println(s.getQuery());      // decoded
text
GET /books?author=herbert&page=2 HTTP/1.1
Host: api.example.com
q=science%20fiction
q=science fiction

The fragment top does not appear in the request target, matching the rule above. URI.create is meant for text known to be valid: on an illegal string it throws IllegalArgumentException, so for user input the JDK recommends new URI(...), which throws a checked URISyntaxException that you handle.

Backend Insight

The URL structure is the public face of your backend. Frameworks you will use later let you attach Java methods to paths and read query parameters as method arguments. Getting the split right pays off: clients can guess /books/7 after seeing /books, and your logs and monitoring group traffic by path.

Remember that everything in a URL is visible: it appears in browser history, server logs, and the address bar. Never put passwords, tokens, or other secrets in a path or query string. Send credentials in headers over HTTPS instead (lesson 4).

Query parameters also arrive as text, even when they look like numbers. page=2 is the string "2" until your code converts it, and the conversion can fail: page=abc should lead to a 400, not a crash with a 500 (lesson 3). Treat missing parameters as normal and give them defaults.

Common Mistakes

Mistake 1: Putting verbs or filters in the wrong place

  • Incorrect: /getBooksByAuthor/herbert.
  • Correct: /books?author=herbert.

Mistake 2: Expecting the server to see the fragment

  • Incorrect: building a feature that reads #section on the backend.
  • Correct: the fragment never reaches the server; put data you need in the path or query.

Mistake 3: Putting secrets in the URL

  • Incorrect: /login?user=ana&password=secret.
  • Correct: send credentials in the request body or an Authorization header over HTTPS. URLs are logged and stored in history.

Mistake 4: Forgetting that query values are text, and need escaping

  • Incorrect: ?q=science fiction&x with a raw space, or treating page=2 as already an int.
  • Correct: write ?q=science%20fiction and convert page yourself, handling bad input.

Exercises

Level A: Foundation

A1. Split http://library.example.org:8080/loans/5?status=open#details into scheme, host, port, path, query, and fragment.

A2. In /movies?genre=drama&year=2016, how many query parameters are there, and what are they?

A3. Is the fragment sent to the server?

Level B: Application

B1. Decide path or query for each: movie 42; movies of genre drama; the cast of movie 42; the third page of results.

B2. Write the request line (method, target, version) a browser sends for https://api.example.com/movies?genre=drama#cast.

B3. A colleague designs GET /search?q=rock&password=abc123. Name two problems.

Level C: Challenge

C1. Write a Java snippet that, given the string https://api.example.com/books?author=herbert&page=2, prints the value of the page parameter as an int plus 1. Say what should happen if the query is missing.

C2. A user searches for "fish & chips". Explain why the raw text ?q=fish & chips breaks the query and how it should be written.

Show solutions

A1. Scheme http; host library.example.org; port 8080; path /loans/5; query status=open; fragment details.

A2. Two: genre=drama and year=2016.

A3. No. The browser keeps it.

B1. /movies/42 (path); /movies?genre=drama (query); /movies/42/cast (path, a sub-resource); /movies?page=3 (query).

B2.

text
GET /movies?genre=drama HTTP/1.1

The fragment #cast is not sent; the Host: api.example.com header goes below.

C1.

java
URI u = URI.create("https://api.example.com/books?author=herbert&page=2");
String q = u.getRawQuery();
if (q == null) {
    System.out.println("no query, use defaults");
} else {
    for (String pair : q.split("&")) {
        String[] kv = pair.split("=", 2);
        if (kv[0].equals("page")) {
            System.out.println(Integer.parseInt(kv[1]) + 1);
        }
    }
}

This prints 3 (compiled and run). If the query is missing, getRawQuery() returns null, so the null check chooses defaults. If page is not a number, Integer.parseInt throws NumberFormatException, which a backend turns into a 400.

C2. The & is the separator between parameters, so the server would read q=fish and then a parameter named chips; the space is not allowed raw either. Write ?q=fish%20%26%20chips, where %26 is the escaped & and %20 is the escaped space. (%26 for & is standard percent-encoding; I did not run this one.)

Mini Challenge

Take a URL from a real site you use (a search result or a product page) and copy it as text, removing anything that looks personal, like tokens or ids you do not want to share. Split it into the six parts in a table, mark which parts reach the server, and list each query parameter with a guess of what it does. Then redesign the same request as a clean API URL, with a noun path and query for the refinements.

Check your work: the table has scheme, host, port (or "default"), path, query, and fragment; the fragment is marked as not sent; your redesigned URL has no verbs and no secrets.

Summary & Next Lesson

Key takeaways

  • A URL has a scheme, host, optional port, path, optional query, and optional fragment.
  • The path identifies the resource; query parameters (name=value joined by &) refine it.
  • The fragment after # is for the browser and is never sent to the server.
  • Special characters are percent-encoded, such as %20 for a space.
  • URLs are visible in logs and history, so never put secrets in them.

Next lesson

The next lesson, "Demo: reading real HTTP traffic in the browser", puts every part from this module to work on a real site using the Network tab, in preparation for the lab.

Self-check

  1. In /books?page=2, what is page=2?
  2. Which part of a URL identifies the resource?
  3. Is the fragment sent to the server?
  4. How is a space written inside a URL?
  5. Why should a token never be put in a query string?
Show answers
  1. A query parameter: name page, value 2.
  2. The path.
  3. No.
  4. As %20 (percent-encoding).
  5. URLs show up in browser history and logs, so the token could leak.

References

  • MDN: What is a URL?. Supports the parts of a URL, default ports 80 and 443, the path, the &-separated query, and that the fragment is never sent to the server.
  • Java SE 21 API: java.net.URI. Supports getPort() returning -1, getRawQuery() and getFragment() returning null when absent, raw versus decoded forms, and URI.create throwing IllegalArgumentException.

Not verified from a source: the URL-versus-URI wording is simplified, and the advice about secrets in URLs is general security practice.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.