Categories
Javascript

Proxy in JavaScript

As we can already guess from the name, a Proxy object works as a “proxy” to another object and allows us to customize the behavior of the said object in certain ways. Let’s say you have an obj named awesomeAPI which has some properties and methods. You want to “trap” any calls to the object. May be you want to debug something and log every time a property is read/set on the object? Or when a method is called? Since the object has “API” in it’s name, let’s assume it makes a HTTP call to an external API We want to cache the response instead of hitting the resource every time a method is called – we can use a proxy to do that. In fact there can be so many useful use cases for Proxy in JavaScript as we will see.

A Basic Proxy

To create a new proxy, we need two things – one target object and a handler. The target object is the object we want to proxy. The handler is an object which will define certain methods to control what happens when an operation is requested on the target object through the proxy. The proxy object traps the requests, instead of performing the requested operations directly on target object, the proxy would first see if there’s a handler method defined for that operation in our handler object. If a method is available, it’s called. Otherwise the operation is forwarded to the target object directly.

The description alone might not be clear enough. Let’s go ahead and see a basic example.

const obj = {
  hello() {
    console.log("Hello");
  }
}

const handler = {
  get(target, propKey, receiever) {
    console.log("Trapping GET for " + propKey);
    return target[propKey]
  }
}

const proxiedObject = new Proxy(obj, handler);
proxiedObject.hello();

If you run the code example, you will notice a message on your console – “Trapping GET for hello” followed by the actual “Hello” printed by the target object. What’s happening here? We are creating a new Proxy object for the obj object. We have a handler which sets a trap for get. Now any time we access a property on proxiedObject, the get method on the handler is called with the target object, name of the property and the receiver. We will focus on the property name and the target arguments for now. In our handler code, we have just logged the name of the property on the console and then returned the actual value from the target object. We could of course return any value we wish – may be a transformed value? a cached value? Well, anything we wanted.

You may be wondering – we made a function call, hello() but why would that get trapped by get which is for property access? Methods in JavaScript are actually properties. A method call happens in two stages – get the method (read the property) and then an apply call. So when we called proxiedObject.hello(), it looked up the hello property first. Then called it.

Traps in Our Proxies

The methods we define on the handler objects correspond to certain operations. For example, get is called during property lookup, has is called when the in operator is used, set is for setting property values.  These methods are the “traps” for the corresponding operations. Traps are optional, you can just define the ones you need. If any trap is not set for a particular operation, it’s forwarded to the target object directly.

Here’s an example:

const numberStorage  = {
  number: 0
}

const handler = {
  set(target, propKey, value, receiever) {
    target[propKey] = value * value;
    return true;
  }
}

const squaredNumberStorage = new Proxy(numberStorage, handler);

squaredNumberStorage.number = 2;
console.log(squaredNumberStorage.number);

In this example, we have trapped the property set operation and instead of storing the number as it is, we are squaring it and saving the squared value. However, we have defined no traps for the get operation, so when we access the number,  the operation is forwarded to the target object and we get the actual value without any changes.

Now that you know how to trap the operations on an object using a Proxy, you can check out the available traps from the awesome MDN docs here.

Going Ahead

There is an excellent chapter on meta programming with proxies in Exploring ES6 book. The MDN docs on the Proxy object is also pretty nice with adequate examples and complete API references.

 

 

Categories
Javascript

Promises in JavaScript

We encounter promises in real life every now and then. You have promised to deliver that project within the next week. Your friend has promised to play Overwatch with you tonight. If you think about it, promises are everywhere around us. Promises in JavaScript also play similar roles. A Promise object in JS is an object that promises to come up with a value or in the case of any error, a reason for the failure. But we don’t know when the promise will complete, so we attach callbacks to the promise object which get called on value or error.

Why Promises are useful?

Of course before we can dive into the technicalities of promises, you will have this question. Why does Promise matter in the first place? What is the use case? If you have written any JS code that fetches some data over the internet, you may have already got used to the fact that JavaScript is single threaded and uses asynchronous operations in places. To deal with asynchronous JS parts, we are long used to using callbacks and event listeners.

function downloadImage(imageURL, callback) {
    // Some network requests here which takes time,  let's use setTimeout as an example

    const error = null;
    const result = "image data";

    setTimeout(() => callback(error, result), 3000);


}

// How we pass callbacks
downloadImage("some url", (error, result) => {
    if (error) {
        console.log("Error")
    }
    else {
        console.log(result)
    }

});

This looks good but if you have written a few level of nested callbacks, you will soon find out the callback hell is real. You may also architect a few Pyramids of Doom.

Pyramid of Doom

Promises are one of clean ways to solve this problem. Without getting into technical parts, we can rewrite the download image example using Promises like this:

function downloadImageWithPromise(imageURL) {
    // Some network requests here which takes time,  let's use setTimeout as an example

    const error = null;
    const result = "image data";

    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (error) {
                reject(error);
            }
            else {
                resolve(result)
            }
        }, 3000)


    })


}

downloadImageWithPromise("some url").then(console.log).catch(console.error);

In most cases, we will be consumers of promises, so don’t worry if the downloadImageWithPromise doesn’t immediately make sense. We will dive into promise creation soon. For now, take a look at how easy it is to consume a promise. No more callbacks or headaches. The code is clean, easy to reason about and should be easy to maintain in the long run.

With the latest JS changes, some of the important APIs are also based on promises. So it’s very essential that we understand the basics of Promises before hand.

Making a Promise

If you were a little confused about the downloadImageWithPromise function, worry no more, we will break it down now. And hopefully, it will no longer remain confusing. The basic idea of making promises in JavaScript that when we don’t immediately have a value to return from our function (for example an async operation), we should return a promise instead. So the user / consumer can rely on that promise and retrieve the value from it in the future. In our code, when the async operation is complete, we should “settle” the promise object we returned. Settling means either resolving / fullfilling or rejecting the promise with an error.

Creating a new promise object is simple. We use the new keyword with the Promise constructor. We pass it an “executor” function. The executor function takes two arguments – a resolve callback and a reject callback. We run our async operations within this executor function and when the operation completes, we either call the resolve or reject callback with appropriate values.

function iPromiseValue() {
    return new Promise(executor);
}

function executor(resolve, reject) {
    setTimeout(() => resolve("Here's your value!"), 3000)
}

To make things clearer, we separated our executor function. This is basically how promise works. The promise constructor takes a executor function. The executor function takes two callback, resolve and reject. As soon as one of these callbacks is called, the promise is settled and the value (or the error) is made available to the consumer.

Consuming a Promise

Promise objects have two convenient methods – then and catch – we can pass callbacks to these methods which will be later called in order. When these callbacks are called, we will get the values passed to our callbacks. Let’s take a quick example:

function getValueAfterDelay(delay, value) {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(value), delay);
    })
}

getValueAfterDelay(3000, "the value")
    .then((value) => {
        console.log("Got value: " + value)
    });

And here’s an example with rejection:

function rejectAfterDelay(delay) {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject("No!"), delay)
    })
}

rejectAfterDelay(3000)
    .then((value) => console.log("Did we get a value? :o"))
    .catch(console.error);

Chaining Callbacks

The then method returns a promise. So we can keep chaining multiple promises one after one.

function getMePromise(value) {
    return Promise.resolve(value);
}

getMePromise(2)
    .then((value) => 2 * value)
    .then((value) => value + 1)
    .then(console.log);

First things first – the Promise.resolve and Promise.reject methods immediately return a resolved or rejected promise with the value we pass. This is convenient where we need to return a promise but we don’t need any delay, so need for an executor function and the separate callbacks. We can just do Promise.resolve or Promise.reject and be done with it.

We can see how we chained multiple then methods and passed multiple callbacks to gradually transform the values. If we return a promise from one of this callbacks to then methods, it will be settled before passing on to the next callback.

Please note: If you look at the Promise related docs on MDN or some other places, you will find that the then method can take two callbacks, one for success and one for failure. The catch method is simply then(undefined, errorHandler) in disguise. But there’s a problem with using two callbacks to the then method. Take a look at this example:

function getMePromise(value) {
    return Promise.resolve(value);
}

function successCallback(value) {
    if (value < 10) {
        throw new Error("Less than 10");
    }
}

function failureCallback(err) {
    console.log("Error:" + err);
}

getMePromise(2).then(successCallback, failureCallback)

Running the code will get us an error:  UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Less than 10. 

So what’s happening here? The error callback in a then method gets called only if there’s any error in the previous step (in this case the getMePromise function). But it can not handle the error caused in the same level (from within the successCallback function). This is why a then().catch() chain works better than passing two callbacks to the then method itself.

Promise in Real Life

We have got some basic ideas about Promise. Now we will see a real life example of using promise. We would use the axios npm package to fetch a web page content. This package has a nice promise based API. Let’s install it first:

npm i -S axios

Now we can use the package.

const axios = require("axios");

axios.get("http://google.com")
    .then((resp) => console.log(resp.data.length))
    .catch((error) => console.error(error));

The axios.get function makes a HTTP GET request to the URL provided and returns a promise. We then attach success and error callbacks.

Multiple Promises

Sometimes we may need to deal with multiple promises. We can do that using Promise.all. It takes a list (iterable) of promises and returns a single promise that we can track. This single promise is resolved when all the promises in the list has resolved or one has failed (whichever happens first). It will return the results of the resolved promises in the same order. Let’s see an example:

const axios = require("axios");

const googlePromise = axios.get("http://google.com");
const facebookPromise = axios.get("http://facebook.com");

const allPromises = Promise.all([googlePromise, facebookPromise]);

allPromises
    .then(([googleRes, fbRes]) => console.log(googleRes.data.length, fbRes.data.length))
    .catch((error) => console.error(error));

Here we create two promises separately, put them in a list and pass it to Promise.all(). Then we attach our callbacks to the new promise we got. Note how we got two results in the same order inside the callback to the then method.

There is another convenient method – Promise.race() which is similar but settles as soon as one of the passed promises is resolved or rejected.

Migrating from Callback

In most cases, you can use various utilities available to convert callback based APIs to promise based one. In case, it’s not possible, just wrap their APIs in your own promises. For example, the popular request package on npm has a little different callback syntax. So we can wrap it in our own promise like this:

const request = require("request");


function makeRequest(url) {

    return new Promise((resolve, reject) => {
        request(url, function (error, response, body) {
            if (error) {
                reject(error);
            } else {
                resolve(body);
            }
        });
    });


}

makeRequest('http://www.google.com').then(console.log);

In  this case, we make the request call from inside an executor function and return the promise. We pass a callback to the request function just like it expects. Inside the callback we make use of our resolve / reject callbacks to settle the promise.

Further Reading

 

Categories
Javascript

REST API with KoaJS and MongoDB (Part – 3)

In Part -1 of this series, we saw how we can get started with KoaJS and in Part – 2 we built CRUD endpoints with MongoDB. In this part, we’re going to work with authentication. We will be using JSON Web Tokens aka JWT for the auth part. We have written detailed pieces on JWT before. You can read Understanding JWT to check out the basics and read our tutorial on JWT with Flask or JWT with Django to see how other frameworks like Flask uses JWT.

JWT with KoaJS

To implement JSON Web Tokens with KoaJS, we would be using two packages – koa-jwt and jsonwebtoken. The second package (jsonwebtoken) provides useful helper functions to generate and verify JWTs. Where as koa-jwt provides an easy to use middleware that we can use with KoaJS.

Let’s go ahead and install these packages:

npm i -S koa-jwt jsonwebtoken

That should install the dependencies and save them in our package.json.

Securing Routes with JWT

We have the required packages installed. So we can now start securing our routes with JWT. We can just require the koa-jwt package directly and use it. But we want to customize some aspects. For that we would create our own module named jwt.js and put the custom stuff in there.

const jwt = require("koa-jwt");
const SECRET = "S3cRET~!";
const jwtInstance = jwt({secret: SECRET});

module.exports = jwtInstance;

Now in our index.js file, we would add the middleware to the app.

app.use(require("./jwt"));

If we try to visit http://localhost:3000/, we will get a plain text error message saying “Authentication Error”. While the message is clear and concise, we want to output JSON, not a plain text error message. For that, we will write a custom middleware.

function JWTErrorHandler(ctx, next) {
    return next().catch((err) => {
        if (401 == err.status) {
            ctx.status = 401;
            ctx.body = {
                "error": "Not authorized"
            };
        } else {
            throw err;
        }
    });
};

The code for this middleware is pretty simple. It invokes the next middleware and if it catches an error, it checks if it’s 401, if so, it sets a nice detailed JSON as the output. If you’re familiar with how middlewares work in express / koa, this should make sense. If it doesn’t make sense, don’t worry, you will get it over time.

Now we need to export this function from our jwt.js module. Let’s change the exports a little bit.

module.exports.jwt = () => jwtInstance;
module.exports.errorHandler = () => JWTErrorHandler;

Now we’re exporting two functions, which, when called will return the specific middlewares. We also need to change our imports in index.js –

const jwt = require("./jwt");
app.use(jwt.errorHandler()).use(jwt.jwt());

Please note the order of the middleware we used. The error handler must come before the JWT middleware itself, so it can call next() and check for the 401 error.

If we try to browse the API now, we should get a nice JSON like this:

{"error":"Not authorized"}

Secured Routes and Router

We used the middleware directly on the koa app. That means all our routes are now secure. All the routes would now check for the Authorization header value and try to verify it’s value as a JSON Web Token. That’s good but there’s a slight problem. If we can’t access any of the routes without a token, which route do we access to get the token in the first place? And what token do we use for that? Yeah, we need to have at least one route which is not secured with JWT which will accept login details and issue the JWTs to the users. Besides, there could be other API end points which we can keep open to everyone, we don’t need authentication on those routes. How do we achieve that?

Luckily, Koa allows us to use multiple routers and each router can have their own set of middlewares. We will keep our current router open and add the routes to obtain the JWT. We will create a separate route which will use the middleware and be secured. We will call this one the “secured router” and the routes would be “secured routes”.

// Create a new securedRouter
const router = new Router();
const securedRouter = new Router();

// Add the securedRouter to our app as well
app.use(router.routes()).use(router.allowedMethods());
app.use(securedRouter.routes()).use(securedRouter.allowedMethods());

We modified our existing codes. We now have two routers and we added them both to the app. Let’s now move our old CRUD routes to the secured router and apply the JWT middleware to just the secured router.

// Apply JWT middleware to secured router only
securedRouter.use(jwt.errorHandler()).use(jwt.jwt());

// List all people
securedRouter.get("/people", async (ctx) => {
    ctx.body = await ctx.app.people.find().toArray();
});

// Create new person
securedRouter.post("/people", async (ctx) => {
    ctx.body = await ctx.app.people.insert(ctx.request.body);
});

// Get one
securedRouter.get("/people/:id", async (ctx) => {
    ctx.body = await ctx.app.people.findOne({"_id": ObjectID(ctx.params.id)});
});

// Update one
securedRouter.put("/people/:id", async (ctx) => {
    let documentQuery = {"_id": ObjectID(ctx.params.id)}; // Used to find the document
    let valuesToUpdate = ctx.request.body;
    ctx.body = await ctx.app.people.updateOne(documentQuery, valuesToUpdate);
});

// Delete one
securedRouter.delete("/people/:id", async (ctx) => {
    let documentQuery = {"_id": ObjectID(ctx.params.id)}; // Used to find the document
    ctx.body = await ctx.app.people.deleteOne(documentQuery);
});


We removed the previously setup JWT middleware from the app and used it on securedRouter instead. Remember, the JWT middleware must be setup before we setup the routes themselves. Ordering of middleware matters.

If we try to visit “http://localhost:3000/”, we will no longer get the auth error, rather will see “not found” (we didn’t define any routes for the root url). However, if we try to visit “http://localhost:3000/people”, we will get the authentication error again. Exactly what we wanted.

Issuing JWTs

We now need to create the route to issue JWTs to our users. We will be accepting their login (username and password) and if they’re valid, we will issue them JWTs which they can use to further access our APIs.

The koa-jwt package no longer supports issuing tokens. We have to use the jsonwebtoken package for that instead. Personally, I like to create a helper function in my custom jwt.js module like this:

// Import jsonwebtoken
const jsonwebtoken = require("jsonwebtoken");

// helper function
module.exports.issue =  (payload) => {
    return jsonwebtoken.sign(payload, SECRET);
};

Then we can write a new route on our public router like this:

router.post("/auth", async (ctx) => {
    let username = ctx.request.body.username;
    let password = ctx.request.body.password;

    if (username === "user" && password === "pwd") {
        ctx.body = {
            token: jwt.issue({
                user: "user",
                role: "admin"
            })
        }
    } else {
        ctx.status = 401;
        ctx.body = {error: "Invalid login"}
    }
});

We have hardcoded the username and password here. In production environments, we would store the details in a database and we would hash the password. No one in their right mind should store password in plain text.

In this view, we are accepting a JSON payload and checking the username and password. And then if the details match, we are issuing the token. To test if it’s working, we can make a curl request and checkout the response:

curl -X POST \
  http://localhost:3000/auth \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{"username": "user", "password": "pwd"}'

If it worked, we will get a JSON back with a `token` value containing the JWT.

Using the JWT

We made a request and got the following response:

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUwMjI3MTM0Nn0.GWtjeECIHFQr7vI_MphfUle06Pav_zx4sLmSrd3HE8g"}

That is our token. Now we can start using it in the Authorization header. The format should be like:Authorization: Bearer <Token> . We can make a request to our secured “/people” resource using curl with this header:

curl -X GET \
  http://localhost:3000/people \
  -H 'authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTUwMjI2OTg4MX0.Ugbh4UwN9tRwhIQEQUHoo-affUf5CAsCztzAXncBYt4' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \

We will now get back the list of people we have stored in our mongodb.