Programs can treat the data of many other programs as if it were their own using the programming method known as metaprogramming. In addition to changing itself, a program may be built to read, generate, analyze, or modify other programs while it is functioning.
Basically, a proxy is a meta-programming technique that improves the behavior of a function and essentially lets programmers deceive the JavaScript language.
We will study how proxies function in JavaScript and how to utilize them as proxy objects in this tutorial. We will also develop a weather forecasting application utilizing proxies.
What Is a Proxy?
Between an object, handler, and a target object, a proxy functions as a layer that intercepts and redefines basic operations or traps, such as getting, setting, and defining properties. Common uses for proxies objects include validating, formatting, filtering inputs, and logging property accesses.
A proxy can also be seen as an object wrapper for targets that can be used to access and change the target object’s properties.
Two key functions, which direct how to use proxy objects, include:
- In order to control data before modifying a target object’s property, a proxy is employed.
- Proxy is used to access the target object’s property and conduct additional functionality.
Why Are Proxies Important?
You can use a proxy for the following:
- Proxies are used to have complete control over data
- REST API requests, data validation, and asynchronous function monitoring
- Working with revocable references and type checking
- Used for JavaScript’s DOM Document Object Model implementation
How Does Proxy Work?
Below is an example of a proxy:
let proxy = new Proxy(target, handler);
target and handler are the parameters passed to the proxy() object:
- target: this is the original object you’d like to proxy
- Handler: The target object’s behavior can be controlled by methods in the handler object.
The wrapped object is the target object, the handler is the function used on the target object, and operations on the target object are executed inside the handler using traps methods.
Traps Method
When certain operations are carried out on the target object via the proxy object, traps are handler methods that are triggered. The target object can be updated, formatted, and stored with the use of the traps function after data has been retrieved from a REST API.
Let’s quickly review some of the typical trap methods and how we may use traps to verify responses from REST APIs:
The set() Trap
Every time a property on the target object is updated while accessing the proxy object, the set() trap is activated. It can be used to modify the actions of the target object.
Let’s have a look at an example of how the set() trap may be used to retrieve data from an API, validate the response, and then save the data in the target object.
Let’s use the gender-api.com/en API as an example to show how to use API in JavaScript proxy objects. The free API can determine a user’s gender based on their name:
let personObject = {
name: "",
gender: "",
};
const handler = {
set(target, prop, value) {
fetch("https://gender-api.com/v2/gender?name=" + value)
.then((res) => res.json())
.then((data) => {
if ("string" === typeof value) {
if (data.name !== "undefined") {
target["name"] = data.name;
} else {
target["name"] = "";
}
} else {
throw new TypeError("Name must be a string");
}
});
},
};
let proxyied = new Proxy(personObject, handler);
proxyied.name = "John";
Here, the set() trap proxy is used to validate the name, fetch, and gender of the API and then update the person object’s properties with the data we retrieve from the API.
The get() Trap
When a property on the target object is accessed through the proxy object, the get() trap is called. This trap can be used in a variety of ways, such as to limit access to certain attributes or return only a portion of the values.
We will show how to return a custom value from the target object using the get() trap:
let personObject = {
name: "",
gender: ""
}
const handler = {
get(target, prop) {
if (prop === 'userDetails') {
return `${target.name} is a ${target.gender}`
} else if (prop === 'userInfo') {
if (target.name === 'Male') {
return 'the user is a man'
} else {
return 'the user is a woman'
}
} else {
return target[prop]
}
}
}
const proxyied = new Proxy(person, handler);
Here we used the get() trap to return a custom value that is based on our properties for the person object. We do this because the personObject does not have properties for userDetails and userInfo.
More Traps
The list below includes the other traps available from the proxy object:
- Enumerate: Traps the usage of for….in
- getOwnPropertyDescriptor: Traps a call to Object.getOwnPropertyDescriptor
- getPrototypeOf: Traps an internal call to [[GetPrototypeOf]]
- ownKeys: Traps a call to Reflect.ownKeys()
- setPrototypeOf: Traps a call to Object.setPrototypeOf
- isExtensible: Traps a call to Object.isExtensible
- construct: Traps usage of the new operator
- preventExtensions: Traps a call to Object.preventExtensions
Prerequisites
For this application, you can sign up on https://openweathermap.org/api and get an API key. If you click on the API key, you should see something like the image below:
Building a Forecast App for Weather With Proxies
In this section, we will create a JavaScript proxy to create an application using the REST client. We will use our open weather API to create an application that will show the weather details of any address we input.
Creating a Frontend for Our Application
First, create a project directory and create an index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Weather App</title>
</head>
<style>
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td,
th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
</style>
<body>
<h1 style="text-align: center">Weather forecast</h1>
<div
style="
float: left;
width: 300px;
height: 200px;
background-color: red;
margin-left: 50px;
"
>
<label>enter city name</label>
<input type="text" value="" id="city" onchange="check('state')" /><br />
<label>enter state name</label>
<input
type="text"
value=""
id="state"
onchange="check('country')"
/><br />
<label>enter country name</label>
<input
type="text"
value=""
id="country"
onchange="{proxyPerson.name ='load'}"
/>
<br />
<input
type="submit"
id="load"
onclick="{proxyPerson}"
value="weather forecast"
/>
</div>
<div
style="
float: right;
width: 300px;
height: 200px;
background-color: red;
margin-right: 50px;
"
>
<table>
<tr>
<td>location</td>
<td><span id="location"></span></td>
</tr>
<tr>
<td>Status</td>
<td><span id="status"></span></td>
</tr>
<tr>
<td>Description</td>
<td><span id="description"></span></td>
</tr>
<tr>
<td>Pressure</td>
<td><span id="pressure"></span></td>
</tr>
<tr>
<td>Humidity</td>
<td><span id="humidity"></span></td>
</tr>
<tr>
<td>temperature</td>
<td><span id="temperature"></span></td>
</tr>
</table>
</div>
</body>
<script> type="text/javascript" src="proxy.js"></script>
</html>
The code above handles the interface for the application; We have three input fields, city, state, and country, which, when filled, will retrieve weather forecasts for that city.
Creating Proxy.js
The next step is creating a proxy.js file that will handle the functionalities of the application.
Add the following code to the proxy.js
In the code block above, we created a proxyPerson object, then passed in the location object as the original object we want to proxy. We pass in the handler object as the second argument, which defines the object’s location behavior.
The user’s address is obtained from the city, state, and country input forms using the set() trap, and the latitude and longitude of the address are obtained by sending a request to the openweathermap.org endpoint. Finally, we add the city’s name, latitude, and longitude to the location object’s properties.
function check(id) {
document.getElementById(id).value = "";
}
let location = {
name: "",
lat: "",
lon: "",
};
const handler = {
set(target, property, value) {
let apikey = "0abe276c48b3362cd8a7950b9382ee37";
let city = document.getElementById("city").value;
let state = document.getElementById("state").value;
let country = document.getElementById("country").value;
if (city != "" && state != "" && country != "") {
fetch(
"https://api.openweathermap.org/geo/1.0/direct?q=" +
city +
"," +
state +
"," +
country +
"&limit=1&appid=" +
apikey
)
.then((response) => response.json())
.then((data) => {
target["name"] = data[0]["name"];
target["lat"] = data[0]["lat"];
target["lon"] = data[0]["lon"];
console.log(target["lat"]);
});
} else {
alert("please enter a valid location ");
}
},
get(target, property) {
let apikey = "0abe276c48b3362cd8a7950b9382ee37";
let lat = target["lat"];
let lon = target["lon"];
if (lat != "" && lon != "") {
fetch(
"https://api.openweathermap.org/data/2.5/weather?lat=" +
lat +
"&lon=" +
lon +
"&appid=" +
apikey
)
.then((response) => response.json())
.then((data) => {
document.getElementById("location").innerHTML = data["name"];
document.getElementById("description").innerHTML =
data["weather"][0]["description"];
document.getElementById("status").innerHTML =
data["weather"][0]["main"];
let icon = data["weather"][0]["icon"];
document.getElementById("temperature").innerHTML =
data["main"]["temp"];
document.getElementById("pressure").innerHTML =
data["main"]["pressure"];
document.getElementById("humidity").innerHTML =
data["main"]["humidity"];
});
} else {
alert("please enter a valid location");
}
},
};
const proxyPerson = new Proxy(location, handle);
We use the get() trap to extract the latitude and longitude from the location object, retrieving the weather information from the openweathermap.org endpoint and showing it on the page.
A complete code for this application can be found in this Github repo.
Conclusion
The concept of proxies, some major proxy traps, and how to create a weather forecast using JavaScript proxy objects were all covered in this post.