actionhero javascript node.js typescript
2019-10-21T16:23:42.121Z
↞ See all posts
Welcome to the fourth installment of The Illustrated Actionhero Community Q&A!
Every week in October I’ll be publishing a conversation from the Actionhero Slack community that highlights both a feature of the Actionhero Node.JS framework and the robustness of the community’s responses… and adding some diagrams to help explain the concept.
October 21st, 2019
Actionhero community member Nick asks:
I’ve noticed when running the latest AH, if I destructure the data param in an action run function to
{params, response, connection}
, when I write the output to response my endpoint returns nothing, unless I do anObject.assign()
. Is this expected behavior?
After some back and forth with other members of the community:
…honestly I’ve seen this behavior for some time, since the move to async and ES6… I believe around AH 17
First… what is destructuring?
Destructuring is a programming shorthand to simply variable assignment by "breaking" the structure of complex objects or arrays.
For example, these are valid examples of destructing:
In both cases, we’ve set the variables firstName
and lastName
without having to reach "into" the complex array or object. To learn more about all the cool things destructuring can do, I recommend this excellent article by the team at Mozilla.
Lets take a look at the Action in question:
1const { Action, api } = require("actionhero"); 2 3module.exports = class ListAvailableLessonDays extends Action { 4 constructor() { 5 super(); 6 this.name = "ListAvailableLessonDays"; 7 this.description = "Description"; 8 this.inputs = { 9 gradeNumber: { required: true }, 10 }; 11 } 12 13 async run({ params, response }) { 14 const { gradeNumber } = params; 15 const { LessonService } = api.services; 16 const { getAvailableLessonDays } = LessonService; 17 const lessons = await getAvailableLessonDays(gradeNumber); 18 response = lessons; // <-- problem! 19 } 20};
Nick is building a tool to help teachers manage their curriculums. A requestor provides a gradeNumber
and the API then returns a list of saved lessons. They are destrucuring the input object data
to his run method into params
and response
.
We can see the data passed into an an Action’s run method:
Since Actonhero can handle connections from many different types of connections (http, websocket, direct TCP socket, etc), we need a generic way to represent the request to an action. Inside Actionhero, we have multiple types of servers responsible for handling each type of connection, and building a generic connection
object, and figuring out what the request parameters
(or params
for short) are. The server is also responsible for sending the response
of your action back to the client. To make a simple API for all of this, your actions run
method is passed one big data
object with everything you might need.
1data = { 2 connection: connection, 3 action: "randomNumber", 4 toProcess: true, 5 toRender: true, 6 messageId: 123, 7 params: { action: "randomNumber", apiVersion: 1 }, 8 actionStartTime: 123, 9 response: {}, 10};
To learn more about how actions work, the Action Tutorial has a lot of great information.
Nick continues his investigation:
This code returns an empty response If I leave it as data, and then do
data.response = lessons
, it returns the array as expected or if I do anObject.assign(response, lessons)
, it will return the data but with the array converted to an object, for obvious reasons
Said another way…
… why?
Community member Chad saves the day:
This is standard ES6 behavior, a common gotcha. When you destructure you take a reference to the property in question. It is a pointer to it. If you say
response = lessons
you overwrite the POINTER, not the VALUE OF IT.
You are "repointing" your local var response to point to a local value lessons, not altering the pointer within the original data object. You could safely set
response.someValue
. But not overwrite the entire response itself.
So, if you are adding properties to response
, (like response.message)
, you can use a restructured response, but if you are overwriting the entire response object, you should not destructure the inputs to your Action’srun
method.
I write about Technology, Software, and Startups. I use my Product Management, Software Engineering, and Leadership skills to build teams that create world-class digital products.
Get in touch