Get all SharePoint Document Library Files and Folders at a ServerRelativeUrl in One REST Call

Recently, I was building a directory tree view of a Document Library for a client. Yes, you can say this shouldn’t be necessary. We can just tag the documents with metadata and we won’t need folders at all. Unfortunately, that’s not always the way people want to work.

To build this, I started by calling the _api/Web/Lists/getbytitle('Document Library Name')/items endpoint. I figured I’d just get all the documents in the library and sort out the display from the array. I got this working pretty well and in my test environment with a few hundred documents, it worked great.

Then – boom. There was a little requirement I didn’t know about: quite a few of the Document Libraries where we wanted to use this had more than 5000 documents. I stupidly hadn’t thought of that possibility. In my experience, it’s pretty unusual to see Document Libraries with that many documents, though it definitely happens.

Why does 5000 documents matter? Well, as of SharePoint 2010, we went from being able to request as many items as we wanted to an item limit of 5000. I’ll admit it’s not usually a great idea to request that many items from the client, but sometimes we need to.

I had two choices:

  • Add some paging logic to the call. This would mean that if there were more than 5000 items, I’d simply make Math.ceil(documentCount / 5000) calls to get them. In smaller Document Libraries, it would still be one call; as the number of documents went up, it would take more calls.
  • Be smart about it. Just request the objects (files and folders) in the root of the Document Library, and then on-demand, only request what I needed as the user expanded each folder.

The former would have been a little easier, but with larger libraries those calls could get pretty slow. The latter idea was really the “enterprise” way to do it. The problem was that the _api/Web/Lists/getbytitle('Document Library Name')/items endpoint didn’t really give me the right info to do it well.

So I turned to a different endpoint: _api/Web/GetFolderByServerRelativeUrl('folderRelativeUrl'). This is a newer endpoint and is designed for doing stuff like this. We can pass in the relative URL – maybe something like “/sites/SiteA/SubSiteA/LibraryName/TopFolder/SubFolderA/SubFolderB” – and get back just the files and folders for that relative path.

It takes two calls for this, though:

  • _api/Web/GetFolderByServerRelativeUrl('folderRelativeUrl')/Folders
  • _api/Web/GetFolderByServerRelativeUrl('folderRelativeUrl')/Files

That would work, but it seemed a bit inefficient. Wouldn’t it be better to get the files and folders at the same time?

Off I went to Bingle. Luckily, I found a post on SharePoint StackExchange pretty quickly from jkr asking the same thing: Get all Files and Folders in one call. Vadim Gremyachev replied with the trick.


With this one call, we can get the info about the file and the folders together in one complex object.

Figure 1: Complex object returned from _api/Web/GetFileByServerRelativeUrl()/$expand=Files,Folders

Figure 1: Complex object returned from _api/Web/GetFileByServerRelativeUrl()/$expand=Files,Folders

As I said, this endpoint is perfect for building something like a directory tree.

There’s not a lot of good documentation for this endpoint (surprise!). You can find some examples of calls on the MSDN page Files and folders REST API reference, but no examples of the results. If you download the SharePoint 2013 REST Syntax (wall posters) you get some more clues.

The Files result provides results like those shown in Figure 2. As far as I can tell, there’s no way to control what fields you get back, as using $select has no effect.

Figure 2: Complex object returned from _api/Web/GetFileByServerRelativeUrl()/Files

Figure 2: Complex object returned from _api/Web/GetFileByServerRelativeUrl()/Files

The Folders result provides results like those shown in Figure 3. As far as I can tell, there’s no way to control what fields you get back here either, as using $select has no effect.

Figure 3: Complex object returned from _api/Web/GetFileByServerRelativeUrl()/Folders

Figure 3: Complex object returned from _api/Web/GetFileByServerRelativeUrl()/Folders

Note that in the Folders results, there are also Files and Folders objects, so the idea of recursion is there, though the objects are deferred. Because each folder has a ServerRelativeUrl value, you can dig as deep as you need to.

If you know you only need to go a few layers deep, you can also do things like:






Both of these calls will get you three folders deep, which may be enough for some things you might want to do. I could also see using a call like these latter ones to get a bit ahead of your user to reduce the “chatter” on the line. That would make your array processing on the client side a little more complex, but could be worth it.

With some spiffy recursion in your framework of choice, you can build some very nice user interfaces with data like this. But that’s for another post…


Displaying a Multi-Select Column "Nicely" in a DVWP with Web Services

<UPDATE dateTime=”2011-06-29T18:11Z”>I fixed a bug in the template where it didn’t always show the final value in the multi-select.</UPDATE>

In a previous post entitled Displaying a Multi-Select Column “Nicely”, I gave a recursive XSL function which split apart the values in a multi-select column and gave you the option to separate them with any separating text you chose.

In the comments on that post, I recently was asked how to adapt that function to work for multi-select values that are returned by the Lists Web Service (GetListItems).  The values that GetListItems passes back are separated differently, like this:

247;#Miami;#1;#Abington;#2;#Acton;#3;#Acushnet;#4;#Adams;#5;#Agawam;#6;#Amesbury;#7;#Amherst;#8;#Amherst Center

and here’s the XSL to format it “nicely”:

<xsl:template name="MultiSelectDisplay">
  <xsl:param name="MultiSelectValue"/>
  <xsl:param name="MultiSelectSeparator"/>
    <xsl:when test="contains($MultiSelectValue, ';#')">
        <xsl:when test="string-length(substring-before(substring-after($MultiSelectValue, ';#'), ';#')) &gt; 0">
          <xsl:value-of select="concat(substring-before(substring-after($MultiSelectValue, ';#'), ';#'), $MultiSelectSeparator)" disable-output-escaping="yes"/>
          <xsl:call-template name="MultiSelectDisplay">
            <xsl:with-param name="MultiSelectValue" select="substring-after(substring-after($MultiSelectValue, ';#'), ';#')"/>
            <xsl:with-param name="MultiSelectSeparator" select="$MultiSelectSeparator"/>
          <xsl:value-of select="substring-after($MultiSelectValue, ';#')" disable-output-escaping="yes"/>
      <xsl:value-of select="substring-after($MultiSelectValue, ';#')" disable-output-escaping="yes"/>

It just required some simple tweaks to get it to deal with the ‘;#’ separators rather than the ‘;’ separator.