Last exercise in learnyounode! W00t!
There’s an awful lot of acronyms in the title for this exercise, and that’s appropriate, because this one is pretty complex. Strap in, because this is going to be a long post!
This one was very frustrating though. I spent an hour or more stumped because I couldn’t access the port – I was getting an error that said, “error: listen EACCES http://localhost:37243” (Mind you, the number changed each time). I was able to finally get around this by creating a new worksheet in Cloud9, installing learnyounode on it, and then doing this exercise. I also needed to use npm install http and npm install url again, since this is a new workspace.
As always, directions and hints first!
HTTP JSON API SERVER (Exercise 13 of 13)
Write an HTTP server that serves JSON data when it receives a GET request to the path ‘/api/parsetime’. Expect the request to contain a query string with a key ‘iso’ and an ISO-format time as the value.
For example:
/api/parsetime?iso=2013-08-10T12:10:15.474Z
The JSON response should contain only ‘hour’, ‘minute’ and ‘second’ properties. For example:
{
“hour”: 14,
“minute”: 23,
“second”: 15
}
Add second endpoint for the path ‘/api/unixtime’ which accepts the same query string but returns UNIX epoch time in milliseconds (the number of milliseconds since 1 Jan 1970 00:00:00 UTC) under the property ‘unixtime’. For example:
{ “unixtime”: 1376136615474 }
Your server should listen on the port provided by the first argument to your program.
─────────────────────────────────────────────────────────────────────────────
## HINTS
The request object from an HTTP server has a url property that you will need to use to “route” your requests for the two endpoints.
You can parse the URL and query string using the Node core ‘url’ module. url.parse(request.url, true) will parse content of request.url and provide you with an object with helpful properties.
For example, on the command prompt, type:
$ node -pe “require(‘url’).parse(‘/test?q=1’, true)”
Documentation on the url module can be found by pointing your browser here:
file:///home/ubuntu/.nvm/versions/node/v4.4.3/lib/node_modules/learnyounode/node_apidoc/url.html
Your response should be in a JSON string format. Look at JSON.stringify() for more information.
You should also be a good web citizen and set the Content-Type properly:
res.writeHead(200, { ‘Content-Type’: ‘application/json’ })
The JavaScript Date object can print dates in ISO format, e.g. new Date().toISOString(). It can also parse this format if you pass the string into the Date constructor. Date#getTime() will also come in handy.
The actual URL for help with URL is:
https://nodejs.org/dist/latest-v6.x/docs/api/url.html
Typing node -pe “require(‘url’).parse(‘/test?q=1’, true)” at the $ prompt gives us:
Url {
protocol: null,
slashes: null,
auth: null,
host: null,
port: null,
hostname: null,
hash: null,
search: ‘?q=1’,
query: { q: ‘1’ },
pathname: ‘/test’,
path: ‘/test?q=1’,
href: ‘/test?q=1’ }
}
Okay, to start, we are going to need the http and url packages, so we write our require statements. Also, although they don’t tell us, we need a port. If you make an empty program that just says
console.log(precess.argv[2];
It will return a port number. Note that this port number changes all the time, so don’t bother using a number. So far, then, we have:
</div>
<div>var http = require('http');</div>
<div>var url = require('url');</div>
<div>var port = process.argv[2];</div>
<div>
A few exercises back, we did an HTTP server, and we’re going to use the skeleton from that one:
</div>
<div>var server = http.createServer(function (req, res) { </div>
<div> // request handling logic... </div>
<div>}) </div>
<div>server.listen(8000)</div>
<div>
We’ll change 8000 to port, of course.
Now’s where we start really digging in. We need to parse the URL that is sent to us. We’re going to assign the result to a variable. The URL is specified in the request, and is accessed by req.url. So:
</div>
<div>var reqURL = url.parse(req.url);</div>
<div>
When you parse it (see the documentation I linked to above), you can, for example, pull out the path. We’re going to need that, so we know if it is ‘/api/parsetime ‘ or ‘/api/unixtime’. That is done by adding a dot followed by pathname and that gives us:
</div>
<div>var reqPath = url.parse(req.url).pathname;</div>
<div>
Last, I declare a variable called timeStmp that will hold the time sent to me by the request. We’re going to use it in different places so I need to declare it here for scope reasons.
Also, from the lesson I referenced above, we also need to add:
</div>
<div>res.end();</div>
<div>
So the program stops listening on the port or it will never end!
All right, then, here’s what we have so far:
</div>
<div>var http = require('http');</div>
<div>var url = require('url');</div>
<div>var port = process.argv[2];</div>
<div></div>
<div>var server = http.createServer(function(req,res) {</div>
<div></div>
<div>var reqURL = url.parse(req.url);</div>
<div>var reqPath = url.parse(req.url).pathname;</div>
<div>var timeStmp;</div>
<div></div>
<div>res.end();</div>
<div></div>
<div>});</div>
<div>server.listen(port);</div>
<div>
Now we need to start processing the requests. This had me stumped for a bit. The key is to note that you are going to get more than one request, and you need to execute different actions depending on which request you get. That sounds like a job for an if statement!
Two if statements, in fact, one to deal with the hours/minutes/seconds request and one to deal with the unixtime request. If the request we get is neither of these, the res.end(); statement we have in there near the end will just print a blank line.
So, we create the two if statements, which will compare the paths given to us by the requests to the ones given in the directions:
</div>
<div>if(reqPath == '/api/parsetime') {</div>
<div></div>
<div>/* do stuff */</div>
<div></div>
<div>}</div>
<div></div>
<div></div>
<div>if (reqPath == '/api/unixtime') {</div>
<div></div>
<div>/* do different stuff */</div>
<div></div>
<div>}</div>
<div>
Now, we need the time given to us by the requests to return the information they are asking for. They are giving us something like this:
/api/parsetime?iso=2013-08-10T12:10:15.474Z
The part we need is from 2013 on. According to the documentation, the stuff after the question mark is the query. That includes “iso=”, which we don’t need, so we’re going to get a substring of that query string that leaves out those 4 letters, like so:
timeStmp = reqURL.query.substring(4);
This date is in ISO-format, but we can convert this to a usable date like so:
</div>
<div>var urlTime = new Date(timeStmp);</div>
<div>
For the hours/minutes/seconds request, we can easily get them now:
</div>
<div>
<div>var hours = urlTime.getHours();</div>
</div>
<div>
<div>var minutes = urlTime.getMinutes();</div>
</div>
<div>
<div>var seconds = urlTime.getSeconds();</div>
<div>
Finally, we have everything we need. We must send it back via the response now. We specify JSON format as given to us in the hints:
Next, we use res.end to send it to the requester, and we convert it to JSON format. For the hours/minutes/seconds request:
And that’s it! We’re done! This is the last exercise in the learnyounode tutorial, and now we’re going to move on to the Express.js tutorial next! See you then. In the meantime, here’s the final program: