In our ColdBox RESTFul RoadShow this month, we've been talking about why ColdBox is the fastest and easiest ways to build a robust REST API in CFML.  One of the very powerful features of the ColdBox MVC Platform is that you can use modules to break your apps and APIs up into management chunks  instead of building a monolith.  Modules also let you decouple pieces of your application so multiple teams can work on different parts.  And with the power of CommandBox's CLI and package manager to assemble your app's dependencies, you can even store modules in a completely separate code repository!   This is exactly what we're going to demo today.

Versioning Strategy

One of the most important aspects of any public API is to incorporate a consistent versioning structure from day one.  This is often represented in the URL as

  • /v1/foo
  • /v2/foo
  •  etc.

This can be accomplished a few different ways, but my recommended approach is to encapsulate each version of your API in a module and use the module's entrypoint setting to prefix the URLs.  You can have multiple modules in your app at once, all registering their own URL routes and they can still share a core collection of model CFCs that's same across APIs if you wish.

Versioned API Sample App

This is exactly what I've created in this new sample app.  You can have this app up and running in just a few seconds with your handy CommandBox CLI.  Just run the following two commands and a browser window will open with the running site for you to play with:

install bdw429s/coldbox-versioned-api
server start

Once the app is running, read through the text on the home page.  You can see there are four different APIs, each with their own custom endpoints that you can hit.

  • /api/v1/
  • /api/v2/
  • /api/v3/
  • /api/vNext/

Each set of URLs represents a different version of the same API.  This particular APi returns information about Raspberry Pi devices and you can see that the routes as well as the format of the data changes in each version.  Run the "list" command from CommandBox and you can get a rundown of everything that CommandBox installed.  You'll see there's ColdBox, the basehandler module, and 4 instances of our versioned module from this example.

list

Dependency Hierarchy for Sample Versioned API (1.0.0)
├── coldbox (4.1.0+00002)
├── cbrestbasehandler (1.0.0)
├── coldbox-api-module-v1 (github:bdw429s/coldbox-api-module#v1)
├── coldbox-api-module-v2 (github:bdw429s/coldbox-api-module#v2)
├── coldbox-api-module-v3 (github:bdw429s/coldbox-api-module#v3)
└── coldbox-api-module-vNext (github:bdw429s/coldbox-api-module#master)

The Code

Now, pull up the code in these two repos and read through the rundown of how they work:

bdw429s/coldbox-versioned-api

  • This is the main app that has the ColdBox app in it.  
  • It does not contain the actual ColdBox framework OR any modules (Git-ignored).  Those are specified in box.json and are installed for you by CommandBox
  • This app doesn't actually contain any of the API handlers or routes, they're all in the next repo
  • This app DOES contain an RPIService.cfc which is our data layer that feeds all API versions
  • In the box.json you'll see that we specify the module in our other repo 4 times, each pointing to a different tag in the Git repo
  • You can pull the latest module code by running an "update" command in CommandBox.

bdw429s/coldbox-api-module

  • This module contains the actual REST APIs
  • There is one branch (master) and 3 Git tags (v1, v2, and v3)  Each of those represent a different stage of development on the API.
  • At each tag, the version of the module and the slug in its box.json, the module's entryPoint is different
  • Having a different slug for each module tag is what allows us to install it more than once.
  • Having a different entryPoint for each module is what gives us a different URL routing scheme for each one
  • The HEAD of the master branch uses the "VNext" module entry point which allows you to install and test the latest bleeding edge API module code
  • You can have as many historical versions of your module and the code is still all stored in one repo.  You can remove older versions by simply uninstalling them from the main app.

Wrap Up

Hopefully this gives you some ideas you can take and run with in your own apps.  Obviously  there are many ways to accomplish these things and this is just one example way, but it really shows the power of combining modules, Git, and CommandBox to build your applications.  If you have any questions or ideas, jump on our mailing list, or post in the #box-products channel of the CFML Slack Team.