Nest Code Examples

In this document we will cover some useful front end and back end code snippets. We are depart from conventions and instead of showing the shortest working code we are going to show the most reliable and comprehensive code first. Then you can copy and paste the best solution.

The exception is that the last example is probably actually the best. It’s like a sandwich with the overly simple code in the middle for those who want to compromise.

Auto updating frontend with ajax

doctype html
html
 head
  style.
   .webnest {
    justify-content:center;
    align-items:baseline;
   }
   .flow {
    display:flex;
   }   
   .flowh {
    flex-flow:row;
    flex-wrap:wrap;
   }   
   .flowv {
    flex-flow:column;
    flex-wrap:wrap;
   }   
 body
  //Other content
  #content
  //Links element
  .webnest.flow.flow 
  script.
   //Populates the .webnest element and updates them continually
   function renewlinks() {
    $.post('/getnestlinks',links=>{
     var additions=links.map(l=>{
      var a = $('<a>').attr('href',l.link);
      var img = $('<img>').attr('src',l.logo);
      var br = $('<br>');
      var span = $('<span>').text(l.domain);
      return a.append(img,br,span);
     });
     var webnest=$('.webnest');
     webnest.children().remove();
     webnest.append(additions);
     setTimeout(renewlinks,links[0].expires-Date.now()-1000*60*Math.random());
    });
   }
   renewlinks();

So what that did was populate your links with ajax. When the links expire the page will automatically update with fresh links. To avoid sudden load on the server a bit of randomness was used to spread the requests over a minute.

Caching backend for use with ajax frontend

Then on the backend I will share the longest and most correct solution first.

var UrlResolver = require('urlresolver');
var p = new UrlResolver();

p.add('/getnestlinks',(req,res)=>{
 nestlinks((err,links)=>{
  if(err) {
   res.writeHead(400,{'Content-Type':'application/json'});
   return res.end(JSON.stringify(err));
  }
  res.writeHead(200,{'Content-Type':'application/json'});
  res.end(JSON.stringify(links));
 });
});

const NESTSECRET = ''//Your secret key
var nestcache=null;
var nestlinkcbs=[];
function nestlinks(cb) {
 if(nestcache && nestcache.length && nestcache[0].expires<Date.now()) {
  return cb(null,nestcache);  //It is in cache
 }
 //We need to request new links
 //Group our incoming requests in case more than one comes through
 // before getting a response.
 nestlinkcbs.push(cb);
 if(nestlinkcbs.length!=1) {
  return; //Some other request initiated the outbound request 
 }
 require('request')('https://nest.jsos.pw/sec/getlinks/'+NESTSECRET,
  (err,res,links)=>{
   if(err) {
    nestlinkcbs.forEach(cb=>cb(err));
    nestlinkcbs=[];
    return;
   }
   nestcache = JSON.parse(links);
   nestlinkcbs.forEach(cb=>cb(null,nestcache));
   nestlinkcbs=[];
  });
}

That is the harder way to do it but it results in fewer requests and it’s copy and paste.

More generic cache

An easier approach would be

var nestlinks = require('../asynccache')(cb=>require('request')('http://nest.jsos.pw/sec/getlinks/71KkVqWdtqGZ',err,res,links)=>cb(err,JSON.parse(links||'[]'))),1000*59);

With asynccache folder containing in index.js

module.exports = (asyncfunc,ttl)=>{
 var cache=null;
 var cbs=[];
 return cb=>{
  if(cache) { return cb(null,cache); }
  cbs.push(cb);
  if(cbs.length!=1) { return; }
  asyncfunc((err,result)=>{
   if(err) {
    cbs.forEach(cb=>cb(err));
    cbs=[];
    return;
   }
   cache=result;
   cbs.forEach(cb=>cb(null,cache));
   cbs=[];
   setTimeout(()=>{
    cache=null;
   },ttl);
  });
 };
};

The TTL in that example has a value of 59 seconds. This guarantees that the cache will be cleared within the 60 second grace period. (At 14 minutes you have new links available to you. At 15 minutes the old ones don’t count towards your stats.) It’s disadvantage is that it doesn’t observe the marked expiration date so it has to clear the cache more frequently and has more requests.

Simply piping

The easiest backend solution yet:

p.add('/getnestlinks',(req,res)=>{
 require('request')('https://nest.jsos.pw/sec/getlinks'+secretkey).pipe(res);
});

That would not cache results though so you will have a lot of outbound requests and you likely will not render the links as quickly.

Without ajax

How about if you don’t want to render them with ajax.

var frontPage = pug.compileFile('pug/front.pug');
var errPage = pug.compileFile('pug/err.pug');
p.add('/',(req,res)=>{
 async.parallel(
  cb=>//get some data,
  cb=>//get some more data,
  nestlinks
 ],(err,[usefuldata1,usefuldata2,links])=>{
  if(err) {
   res.writeHead(400,{'Content-Type':'text/html'});
   return res.end(errPage({err}));
  }
  res.writeHead(200,{'Content-Type':'text/html'});
  res.end(frontPage({usefuldata1,usefuldata2,links}));
 });
});

This is assuming you have a nestlinks function. You could also inline a request(‘url’)

 cb=>request(linksurl,(err,resp,links)=>cb(err,JSON.parse(links||'[]'))),

Here is some pug code for rendering it without ajax

.webnest.flow.flowv
 each l in links
  a(href=l.link,target='_blank')
   img(src=l.logo)
   br
   span= l.domain

Here is some CSS to go with that. It’s similar to the above CSS but it’s for a vertical arrangement.

div.webnest {
 text-align:center;
}   
.webnest img {
 max-width:10vw;
}
.webnest > a { 
 margin-bottom:20px;
}   
.flow {
  display:flex;
} 
.flowh {
  flex-flow:row;
}   
.flowv {
  flex-flow:column;
}

In initial document but also updates with ajax <- Best

Lets cover the front end first because the backend is all things you’ve seen before. In fact that will be enough.

doctype html
html
 head
 body
  #content
  .webnest
  .webnest.flow.flowv
    each l in links
     a(href=l.link,target='_blank')
      img(src=l.logo)
      br
      span= l.domain
  script!= 'var expires='+links[0].expires
  script.
   function renewlinks() {
    $.post('/getnestlinks',links=>{
     var additions=links.map(l=>{
      var a = $('<a>').attr('href',l.link);
      var img = $('<img>').attr('src',l.logo);
      var br = $('<br>');
      var span = $('<span>').text(l.domain);
      return a.append(img,br,span);
     });
     var webnest=$('.webnest');
     webnest.children().remove();
     webnest.append(additions);
     setTimeout(renewlinks,links[0].expires-Date.now()-1000*60*Math.random());
    });
   }
   setTimeout(renewlinks,expires-Date.now()-1000*60*Math.random());

Notice that we could only have set timeout that doesn’t look at the expiration stamp inside the function. One could make the mistake of thinking subsequent calls can be made between 14 and 15 minutes later. Then we likely randomize it somewhat to reduce request spikes on the server.

The problem is that if there are a large number of users inevitably someone will request right at 14 minutes and the server will get new links. The server will then be on a 14 minute cycle. But the most unfortunate of our users will be on 15 minute cycles. After a while that will add up and there will be times when the links are out of date. That’s why looking at the expiration date or requesting within <60 seconds is recommended.

Conclusions

Why did I include the “best” solution last. Because it’s debatable if it is the best solution. It is the best solution but with costs and it’s up to you to consider if those costs impact you. If so the first solution is likely best.

It is “best” because the links will render right away and stay up to date. It’s not best because it mucks up every page handler with one more async call which depending on the organization of your code may be a real cost. And second because if you don’t have much traffic a large number of requests will be waiting on an outbound call before receiving any content.

Questions to ask yourself are:

I personally have chosen different solutions for different sites because there is a right solution for every site but they often aren’t the same.

On JSsocial I generate the links in pug because several other asyncronous calls are occurring anyway and getting the nest links can be done in parallel. Also their format is similar to other links being generated by the pug.

On this page I’m doing it with ajax because the links are on the bottom of the page and a documentation page is expected to load quickly every time. Also because this document is saved as raw html and so there is no pug or other templating engine to be considered.

I would suggest signing up before putting too much time into generating the links. You won’t be linked to pretty quickly if you don’t have your links up so you really can take your time. But unnecessary optimization is a common root of evil. So after you sign up you can always generate the links smarter later. I say do it the simple way first. The tools for doing it another way are here when you need it.

Feedback

You can always reach me as x0x7 @ poal.co