Skip to Content
2015.3.09

Building a Client Library for ArcGIS

Writing a wrap­per client li­brary to smooth out de­sign weird­ness at the API level leads to plenty of de­sign think­ing on the way things should be.

This year I built a JavaScript wrap­per for Node and the Browser around the ArcGIS REST API to sim­plify work­ing with that plat­form as a de­vel­oper. This was as an ex­er­cise in API de­sign, as well as mak­ing a tool that I wanted to use but did­n’t ex­ist yet. The pro­ject is a bare-bones li­brary to ease in­ter­ac­tions with the ArcGIS REST API in JavaScript and Node apps.

Sometimes — and for sure in this case — an API can be rough, built over time, and not pro­vide the sort of log­i­cal mod­els that work well with spe­cific lan­guage en­vi­ron­ments. This was the case with the ArcGIS REST API that I was run­ning in to. A lot of the de­ci­sions had been made over the course of years, and did­n’t trans­late very smoothly a lan­guage as young as Node.js.

The first step was to fig­ure out what prob­lems I wanted to solve. A lot of my work with Esri PDX has been about con­tent han­dling, and so this is where I started. Reading all the doc to get a big pic­ture of what’s go­ing on with the API, and talk to every­one who’s done work like this be­fore to fig­ure out what prob­lems they needed to solve was the first step. From there I felt I had enough con­text and in­for­ma­tion to make the thing use­ful for more peo­ple than just me, and make sure that it was co­her­ent with the un­der­ly­ing goals of the orig­i­nal API.

This pro­ject works to sim­plify and unify the gap be­tween the ArcGIS REST API and a con­tem­po­rary Node ap­pli­ca­tion. This li­brary is a UI in the most ba­sic sense of the term — it pro­vides an in­ter­face be­tween the de­vel­oper and the servers. That in­ter­face needs to be well de­signed and thought­ful in or­der to make the process as smooth, in­tu­itive, and plea­sur­able as pos­si­ble.

One of the most im­por­tant parts of the pro­ject is to pro­vide de­vel­op­ers with a way to ac­cess the ArcGIS plat­form with­out need­ing to ar­chi­tect their en­tire ap­pli­ca­tion around opin­ion­ated frame­works (like Dojo, for ex­am­ple). Though the li­brary it­self is writ­ten in ES6, it’s dis­trib­uted as plain, nor­mal ES5 — both as a node pack­age and a pack­aged bun­dle. This means it works both in Node and the browser, and has very few opin­ions on how it in­te­grates with the rest of your app.

Right now, the li­brary wraps most of the ba­sic plat­form con­tent man­age­ment and in­ter­ac­tions - get­ting and edit­ing users, or­ga­ni­za­tions, and items. The Node ArcGIS Client Library is an open source pro­ject — so it’s scope will in­crease as the com­mu­nity works to ac­com­plish more goals and work­flows.

Setting up the client

The first step in us­ing the li­brary is ini­tial­iz­ing the client with your tar­get por­tal.

var ArcGIS = require('arcgis')
var arcgis = ArcGIS()

This sets up a de­fault ob­ject for in­ter­act­ing with the API. This de­fault is go­ing to talk to ArcGIS Online as an anony­mous, unau­then­ti­cated user. One can au­then­ti­cate this client ses­sion as a named user by pass­ing in a user to­ken ob­tained from a suc­cess­ful OAuth lo­gin process.

var arcgis = Arcgis({
  token: namedUserToken
})

This is­n’t ex­clu­sive to ArcGIS Online. The API for in­ter­act­ing with your or­ga­ni­za­tion’s in­stal­la­tion of Portal or Server is the same. Setting up the client ses­sion with your in­stance is done by spec­i­fy­ing your API do­main.

var arcgis = Arcgis({
  domain: 'ago.my-server.com',
  token: namedUserToken
})

Beyond the ini­tial­iza­tion of the client, the li­brary is ex­clu­sively async. All the func­tions re­turn promises by de­fault.

function log (m) {
  console.log(m)
}
function ohNo (err) {
  return new Error(err)
}
arcgis.request()
.then(log)
.catch(ohNo)

You can also pass in a node-style call­back, if you’d pre­fer.

function log (err, results) {
  if (err) {
    return new Error(err)
  } else {
    console.log(results)
  }
}
arcgis.request({}, log)

Both meth­ods work just as well, and use all the same busi­ness logic. I like promises, but maybe you don’t. This is one of the main rea­sons the li­brary does its best to avoid in­flict­ing my opin­ions on your code­base.

Once we have an au­then­ti­cated ses­sion, we can do all sorts of stuff — like fig­ure out who we are:

function hello (user) {
  console.log('Hello, ' + user.firstName)
}
arcgis.user('NikolasWise').then(hello)

We can get all of the items that user has in the plat­form:

function getContent (user) {
  return user.content()
}
function logContent (content) {
  console.log(content)
}
arcgis.user('NikolasWise')
.then(getContent)
.then(logContent)

Or a list of all the groups that a user is a mem­ber of.

function logGroups (item) {
  item.groups.forEach(function (group) {
    console.log(group.title)
  })
}
arcgis.user('NikolasWise').then(logGroups)

The li­brary also can in­ter­act with the user’s or­ga­ni­za­tion, re­turn­ing in­for­ma­tion, mem­bers, or all the con­tent as­so­ci­ated with the or­ga­ni­za­tion.

function logOrg (org) {
  console.log(org)
}
arcgis.organization('esripdx').then(logOrg)

The or­ga­ni­za­tion call de­faults to self’ — what­ever or­ga­ni­za­tion the cur­rent user is a mem­ber of.

function getMembers (org) {
  return org.members()
}
function log (members) {
  console.log(members)
}
arcgis.organization().then(getMembers).then(log)

Many of the con­tent calls are ab­strac­tions or helper meth­ods for longer, more com­pli­cated calls to the search end­point.

function getContent (org) {
  return org.content()
}
function log (items) {
  console.log(items)
}
arcgis.organization().then(getContent).then(log)

In this way we are able to cre­ate a tran­si­tional layer be­tween the API it­self (a su­per com­pli­cated call to the search end­point) and what ap­pli­ca­tion de­vel­op­ers need (all my or­ga­ni­za­tion’s con­tent).

Working with con­tent

Platform con­tent is the weak­est link in the li­brary as of to­day. ArcGIS sup­ports a huge range of item types, and quite a num­ber of so­phis­ti­cated things you can do with those item types. For now the ba­sics are more or less in place — like get­ting an item’s de­tails:

var layerID = 'a5e5e5ac3cfc44dfa8e90b92cd7289fb'
function logItem (item) {
  console.log(item)
}
arcgis.item(layerID).then(logItem)

Or up­dat­ing the those de­tails and edit­ing the per­mis­sions:

var layerId = 'a5e5e5ac3cfc44dfa8e90b92cd7289fb'
function updateItem (item) {
  return item.update({
    snippet: 'Building footprints in my neighborhood in Portland, Oregon'
  })
}
function shareItem (item) {
  console.log(item)
  return item.permissions({
    access: 'public'
  })
}
arcgis.item(layerId)
.then(updateItem)
.then(shareItem)

So far, there’s some sup­port for item-type-spe­cific meth­ods that are start­ing to open up the pos­si­bil­i­ties of ma­nip­u­lat­ing your con­tent from Node — like get­ting all the data in a layer.

var layerID = 'a5e5e5ac3cfc44dfa8e90b92cd7289fb'
function getData (item) {
  return item.data()
}
function logData (data) {
  console.log(data)
}
arcgis.item(layerId)
.then(getData)
.then(logData)

There is a lot more of the plat­form that we could cover than this - ser­vices, analy­sis, layer cre­ation and tile pub­lish­ing all are ac­tions that this li­brary or ones like it could cover.