SharePoint PnP JavaScript Core Components – deserve more attention

I've been eschewed PnP-JS-Core for a long time and preferred to use JSOM or plain REST calls with the use of jQuery.ajax or SP.RequestExecutor. "Why do I need any wrappers or helpers for the API stuff which can be an additional point of failure?", I asked myself a couple of times putting PnP-JS-Core aside from learning.

But there was an opportunity this week to dive into the library and estimate expediency of its usage on a next project. The first fresh glance at it created a new and rather a positive attitude. First of all, it is made with the toolchain I love, and there are so many familiar faces in its contributors group, I didn’t even know to be related to the project. That was a good sign so I started.

What is it

PnP-JS-Core is a part of Office 365 Developer Patterns and Components for JavaScript’ing in SharePoint.

It is a wrapper over RESTful API and a bit more. PnP JS Core provides a fluent JS API simplifying REST calls to SharePoint API.

Stack & Project

Yes, it’s made with the use of all of that shiny technologies we use on the most modern web projects: JavaScript (TypeScript), Gulp, NPM, Typings, Git, Linting, Serve and cool automation. It’s easy to understand the project structure, logic and contribute.

It is not necessary to investigate the source, but a lot of questions can be solved by checking the code and samples. In the end, PnP-JS-Core is not something complex to learn at all.

Usage

PnP-JS-Core can be installed with the use of NPM or Bower. The library can be used in TypeScript project or referenced as any other JS library and consumed on a page or browser console.

PnP-JS-Core uses fetch protocol and ES6 promises, that’s why to make it work in IE, polyfills got to be loaded first. It is important to note, otherwise, if this part in documentation was skipped, one would be wondering “What the heck, it doesn’t work in IE? What is that error message in the console, saying "'Headers' is undefined?"”. But keep calm, PnP works perfectly in IE and Edge with es-promise and fetch polyfills.

Syntax

PnP-JS-Core has a base object pnp in case of default TypeScript import and $pnp if there is a pnp.js reference on a page and you are writing plain JS in a console or Chrome snippets.

REST calls, with the use of the PnP, can be constructed via fluent API chains with a promise initiator at the end of the statement.

For example, let’s take some basic REST endpoint: /_api/web/lists/getByTitle('CustomList')/items call and reproduce it with the use of pnp:

$pnp.sp.web 
  .lists.getByTitle('ListTitle').items 
  .get() 
  .then(function(items) { 
    // all items are in the `items` array 
  });

$pnp.sp.web.lists.getByTitle('Custom01').items – is a constructor which builds endpoints URI.

IntelliSense helps during the process, so you can be even not aware of REST reference or at least have a bad memory and not necessarily remember the endpoints.

Yet, I highly recommend to have a strong understanding of REST API reference and plan to receive all the fruits that PnP provide only after or use both during the learning curve of course.

When a fluent call is described, get method should be placed to initiate a query to a server. Get method and some others return a promise, that should be chained with then and catch to treat the results.

Fluent API is really a piece of gold, all that $select, $filter, $extend, $top, etc. parameters are there as helper methods as well. The same is relevant for REST API properties and methods. But not everything is implemented in the current version. So it’s good to compare REST reference and PnP features if you feel like something is missing.

For example, I’ve found myself unable to get a list by its URL, which I accept as the only reasonable way to communicate with lists and libraries in the code (as display title can be changed anytime in the UI and GUIDs-based code is complex to maintain and is not reasonable too, as IDs will be different from deploy to deploy for the same business objects). But less than in a half an hour I managed to fork and implement pnp.sp.web.getList method representation for /_api/web/getlist(‘/sites/site/web/list/name’) and create a pull request for this.

That’s the power of open source.

Batches

The well-known fact that REST API is very chatty (over the network) one. Sometimes JSOM’s pros argument over REST says that by queueing some OM queries definitions into one physical executeQueryAsync call you can make an application faster. That is definitely true. But did you know that REST also has such tricks in O365 and SharePoint 2016. That is /_api/$batch endpoint.

PnP provides a great simplification for batch REST queries. Any query can be placed in batch call and retrieved inside one single batch execute.

var batchResults = [];  
var batch = new $pnp.sp.createBatch();  
$pnp.sp.web.getList('/sites/dev01/lists/custom01').items.inBatch(batch).get().then(function(d) {
  batchResults.push({ 
    custom01: d 
  });
});
$pnp.sp.web.getList('/sites/dev01/lists/custom02').items.inBatch(batch).get().then(function(d) {
  batchResults.push({ 
    custom02: d 
  });
});
for (var i = 0, len = 10; i < len; i += 1) {  
  $pnp.sp.web.getList('/sites/dev01/lists/custom03').inBatch(batch).items.add({ 
    Title: 'Item ' + i 
  });
}
batch.execute().then(function() {  
  console.log("All is done!", batchResults);
});

I was really surprised when discovered such a powerful and simple capability.

Unfortunately, REST batches aren’t supported in On-Prem versions before 2016 version.

Conclusion

There was so much hype around PnP during last few years, but most people (developers or course), with whom I discussed PnP in general, never dived inside it at all, at the same time, were constantly mentioning it as a buzzword on different SP events.

At last, I’ve touched some part of the PnP projects from the JS world side personally and can say it louder that it is a very interesting library, it’s worth learning and I will definitely include it in the next UI project.

UPD:

I've also tried PnP-JS-Core with sp-rest-proxy and it works! :)

* This gonna work for GET calls only for now, due to the way sp-rest-proxy and pnp deal with Request Digest on a page.