JavaScript Object Destructuring Assignment in Different Contexts

Destructuring Assignment can be used in different contexts, some of which require subtle variations.

The most common context is assignment to a constant:

const myObject = {
  a: 5,
  b: 10
}

const { b } = myObject

// b is 10

Destructuring With A Value Returned from a Function

const { c } = f()

This requires knowledge of the object returned from f, and will only work if the function returns a field named c.

Destructuring Assignment Statements with the let Keyword

Destructuring assignment can be used with let, but we must use parentheses to give the interpreter some help so the syntax is not ambiguous.

const myObject = {
  a: 5,
  b: 10,
  c: 15
}

let b

({ b } = myObject)

console.log(b); // Required semicolon.

({ c } = myObject)

console.log(c)

This prints:

10
15

Without the parentheses the interpreter gets confused and we get an error:

SyntaxError: Unexpected token =

Parentheses are required to force the interpreter to treat the braces as destructuring assignment instead of assignment to an object literal.

Destructuring in Objects Passed in as Function Arguments

This is a way of requiring a specific object structure only as an argument.
If we define our function as follows:

const f = ( {x, y} ) => {
  console.log("x is: " + x)
  console.log("y is: " + y)
}

Then attempt to use it as below:

const a = 5
const b = 10
f({a, b})

The function prints:

x is: undefined
y is: undefined

The destructuring inside the argument object forces only the specific object structure {a,b} to be a valid input for the function.
Only the code below behaves as desired:

const x = 5
const y = 10

f({x, y})

This prints:

x is: 5
y is: 10

Destructuring Syntax not to be Confused with Returning Literal Objects

Note that a return statement with a literal object is not destructuring:

return { x, y }

This simply returns an object with fields x and y.

Destructuring and Renaming Fields

We can add a colon (:) and a new name after a named field in destructuring assignment in order to rename a field.
Example:

const myObject = {
  r: 10,
  s: 20,
}

const { r: newName } = myObject

console.log(newName) // Will print 10.

Destructure an Object with Remaining Leftover Fields

We can destructure an object taking specific fields we want, and leaving the rest inside the rest variable.

const obj = {
  a: 1,
  b: 2,
  c: 3,
  d: 4
}

const { a, ...rest } = obj

console.log(rest)

This outputs:

{ b: 2, c: 3, d: 4 }

Note that this does not have to be in any specific order.
If we choose to extract c instead as below:

const { c, ...rest } = obj

Our result rest object will be equal to:

{ a: 1, b: 2, d: 4 }

REST API Design for Usability: Human-Readable Date Fields

An API with great Developer Experience (DX) returns responses as immediately understandable as possible, and one common and easily achieved usability feature is to include human-readable or “pretty” date or timestamp fields in JSON representations, in addition to the Unix Epoch timestamps preferred for machine consumption.

In a software project an API can be used by many kinds of technical people, from developers to QA to Product Owners, to test functionality or examine data. In many cases the timestamps on the data are valuable information, and reading them quickly can be very handy and boost productivity, for example to see how recent data is, if it is valid and sorted correctly, and so on.

The following example shows an updated-at field on a response with an added pretty version of the field:

GET /resources/1

{
  "id": 12345,
  "updatedAt": 1580778703,
  "updatedAtPretty": "Tue, 04 Feb 2020 01:11:43 GMT"
}

If using a specific format, we can also include ISO or something similar in the name, referring to the format:

{
  "id": 12345,
  "updatedAt": 1580778877,
  "updatedAtIso": "2020-02-04T01:14:37Z"
}

We could also use updatedAtIso8601 if using this specific readable format. Two other options for general human-readable date fields:

updatedAtReadable
updatedAtHuman

This extra field should not affect the response size much since all API responses should be gzipped.

However, if small size is very important we can always make the feature optional, so that the readable date is only added if a query string parameter is included. This post discusses this pattern in general. Such a request could look like the following:

GET /resources?prettyDates=true

Readable Versions of Other Types of API Fields

Dates and times are not the only kinds of data which could benefit from readable versions. If we have other kinds of human-readable fields, the parameter could control several fields in addition to just the readable date. Suppose we have a field called fileSizeReadable for example, to complement a field expressed in bytes. For a parameter controlling all human-readable fields we could use:

GET /resources?readable=true

GET /resources?human=true

To show a complete example, a default response:

GET /resources/1

{
  "id": 12345,
  "updatedAt": 1580778877,
  "fileSize": 95098036
}

The same call including the pretty fields:

GET /resources/1?human=true

{
  "id": 12345,
  "updatedAt": 1580778877,
  "updatedAtPretty": "2020-02-04T01:14:37Z",
  "fileSize": 95098036,
  "fileSizePretty": "138.3 MB"
}