Blog

Eric Peterson

December 25, 2017

Spread the word


Share your thoughts

Merry Christmas!

Welcome to the last day of (ForgeBox) Christmas! I hope you have enjoyed this crash course in these different modules. The ForgeBox ecosystem is vast and wonderful. Next time you have a need, open up https://www.forgebox.io and check to see what the community has already contributed. Let's end today with an introduction to CFFractal, an API marshalling module.

CFFractal

The docs for CFFractal are pretty awesome, so please forgive me if large sections of this blog post are copy-and-pasted.

Why CFFractal?

CFFractal in two sentences:

Views take models and return HTML. CFFractal takes models and returns JSON (or your favorite serialized format).

You would want to use CFFractal if:

  1. You need to transform your business models to json in many different places.
  2. You need to include and exclude relationships depending on the endpoint.
  3. You don't want to repeat yourself all over the place.

Here are some more in-depth reasons:

Conventions around nested resources

CFFractal is able to parse your includes and excludes for you. That means no more having to litter your transformers with if statements. No more huge parameter lists. Just one argument to pass: includes. We'll handle the rest.

Also, includes are in the hands of the caller. Does the caller not want the user? Great. Don't include it. Do they want 6 levels of nested relationships? Okay. We'll do it. Ultimate flexibility.

Default includes

Some includes are opt-in. Others should always be included. CFFractal makes this easy while still delegating transformation to each resource.

Nested includes

Do you want to get your post's author's comments? No worries! Your includes string can get that with includes=author.comments. Have more levels? Have at it!

Sharing resource transformations

Adding a field to an entity? If you used CFFractal, you only need to add it to your transformer. Every included resource will get it automatically.

You have a user entity that you want to serialize, but not ever with the password? Just exclude it in your transform() function. All calls to CFFractal, including nested includes, will benefit from your attention to security for free.

Consistency

It is frustrating as a consumer of an API to have to make multiple requests where one should have sufficed. One situation where this is sadly the case is inconsistent output between resources. Does the user struct include the last_logged_in key when requesting from one endpoint but not another? It is an easy mistake to make. CFFractal helps to reduce these mistakes by creating a single source of truth for resource transformations — a Transformer.

Another place that is easy to mess up consistency is the response output. Is your data nested in a data key? Are you separating resources into their primary keys and a map? It's too easy to have one endpoint behave differently than another. In CFFractal, Serializers define the structure of the response and can help ensure a consistent output across your API endpoints.

Encapsulation

Playing with your model formats or your data layer code? It won't affect your API if you used CFFractal. Your Transformers are your single source of truth for model transformations. If changes need to be made, they are encapsulated in those files. Your API output will be unchanged from the consumer's point of view.

Flexible

From Jon Clausen:

Having written API’s against MongoDB, ORM, and even a few that use legacy DAO/Gateway patterns, the benefit for me of the fractal transformation module is that I can use it for any of them, because of the transformer. If the models already know how to accomplish the transformations to a degree, like our mementos in ORM, then that’s great.

You don’t always have that, though, and you don’t always want a fully normalized expansion. Sometimes you need only a small subset when dealing with secondary one-to-one relationships that need some normalization of the top level element.

This eliminates three methods that I always found myself writing (or copy/pasting and adapting) for every API handler:

  1. The collection marshalling method
  2. The single entity response marshalling method
  3. The format entity method, which handles the expansion parameters in the collection

That's enough of the "Why". Let's look at the "How":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var fractal = new cffractal.models.Manager(
    itemSerializer = new cffractal.models.serializers.SimpleSerializer(),
    collectionSerializer = new cffractal.models.serializers.ResultsMapSerializer()
);
 
var book = {
    id = 1,
    title = "To Kill A Mockingbird",
    author = "Harper Lee",
    author_birthyear = "1926"
};
 
var resource = fractal.item( book, function( book ) {
    return {
        id = book.id,
        title = book.title,
        author = {
            name = book.author,
            year = book.author_birthyear
        },
        links = {
            uri = "/books/" & books.id
        }
    };
} );
 
var transformedData = fractal.createData( resource ).toJSON();
 
// {"data":{"id":1,"title":"To Kill A Mockingbird","author":{"name":"Harper Lee","year":"1926"},"links":{"uri":"/books/1"}}}

This can be made even easier with the builder pattern and Transformer components:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// models/transformers/BookTransformer.cfc
component extends="cffractal.models.transformers.AbstractTransformer" {
    function transform( book ) {
        return {
            id = book.id,
            title = book.title,
            links = {
                uri = "/books/" & books.id
            }
        };
    }
 
    function includeAuthor( book ) {
        return item( book.getAuthor(), "AuthorTransformer" );
    }
}
 
// models/transformers/AuthorTransformer.cfc
component {
    function transform( author ) {
        return {
            name = author.name,
            year = author.birthyear
        };
    }
}
 
// handlers/Main.cfc
component {
    property name="fractal" inject="Manager@cffractal";
    property name="bookService" inject="entityService:Book";
    function index( event, rc, prc ) {
        prc.response.setData(
            fractal.builder()
                .item( bookService.get( rc.id ) )
                .withTransformer( "BookTransformer" )
                .withIncludes( "author" )
                .convert()
        );
    }
}
// {"data":{"id":1,"title":"To Kill A Mockingbird","author":{"name":"Harper Lee","year":"1926"},"links":{"uri":"/books/1"}}}

ODW Video Recording

Jon Clausen did a wonderful presentation on CFFractal at ODW. Check it out below for more detail on CFFractal and its use in an application.

ODW2017 - CFFractal Marshall Your Business from Luis Majano on Vimeo.

Wrap Up

Thanks again for coming along for the ride. I hope to see you (and your code) on ForgeBox soon!

Add Your Comment

Recent Entries

CommandBox 6.2.0 Released!

CommandBox 6.2.0 Released!

We are pleased to announce the release of CommandBox 6.2.0, a minor release in our CLI, Server, and Package Manager.

Download

You can download the latest version by using the upgrade command from the CLI for an in-place upgrade.  It's also available on HomeBrew, and our download page:

https://www.ortussolutions.com/products/commandbox

What's New

This rel...

Brad Wood
Brad Wood
April 01, 2025
TestBox v6.3.0 Release

TestBox v6.3.0 Release

We are excited to announce the release of TestBox 6.3.0. This version emphasizes the all-new BoxLang Reporter, now with vibrant color outputs and seamless execution through BoxLang, improving the testing experience. To benefit from these improvements, update now and ensure your testing remains reliable and efficient.

Luis Majano
Luis Majano
March 31, 2025
BoxLang Virtual Machines Now on Azure – Power Your Applications with Ease

BoxLang Virtual Machines Now on Azure – Power Your Applications with Ease

We’re excited to announce that BoxLang Virtual Machines (VMs) are now available on the Microsoft Azure Marketplace. With just a few clicks, you can now deploy pre-configured, high-performance BoxLang environments in the cloud — including the ability to run legacy and modern CFML applications using the powerful BoxLang CFML engine.

BoxLang is a modern, flexible, and scalable programming language for the JVM, purpose-bui...

Cristobal Escobar
Cristobal Escobar
March 31, 2025