Today I Learned

A Hashrocket project

10 posts by mattblack @mattblackdev

Protect Yourself Against Firebase Database Refs!

My worst nightmare is to lose production data, thank God for backups. But my nightmare came to life on a project’s Firebase database. This is a noSQL JSON-like document database offered as a service from Google Cloud Platform. You use it like this:

firebase.database.ref('/customers/1')

This is how you get the data at that path. Like a file system.

And when you want to make it dynamic:

firebase.database.ref(`/customers/${customerID}`)

Cool.

But, what if customerID is blank?

cue Nightmare Before Christmas soundtrack

funtion handleDelete(customerID) {
    firebase.database.ref(`/customers/${customerID}`)
    // ...delete()
}

If I ran that function in production with a blank customer ID, well then I just deleted all the customers.

Fear Not!

import protectedRef from './protectedRef.js'

const customerID = ''
protectedRef('customers', customerID) 
// => [ERROR] Bad ref! 1: ""

Use protection kids.

// protectedRef.js

import { sanitize } from './sanitize.js'

function protectedRefReducer(pathSoFar, pathPart, index) {
  const sanitizedPathPart = sanitize(pathPart)


  if (!sanitizedPathPart) {
    throw new Error(`Bad ref! ${index}: "${pathPart}"`)
  }

  return pathSoFar + '/' + sanitizedPathPart
}

export default function protectedRef(...parts) {
    if (!parts.length) {
    throw new Error('noop')
  }
  return firebase.database().ref(parts.reduce(protectedRefReducer, ''))
}

Use Typescript to help migrate/upgrade code

I am tasked with migrating a large javascript codebase from using the beta version of firebase-functions to the latest. Like most major upgrades, there are many API changes to deal with. Here’s an example cloud function:

Beta version with node 6:

exports.dbCreate = functions.database.ref('/path').onCreate((event) => {
  const createdData = event.data.val(); // data that was created
});

Latest version with node 10:

exports.dbCreate = functions.database.ref('/path').onCreate((snap, context) => {
  const createdData = snap.val(); // data that was created
});

The parameters changed for onCreate.

In the real codebase there are hundreds of cloud functions like this and they all have varying API changes to be made. With no code-mod in sight, I’m on my own to figure out an effecient way to upgrade. Enter Typescript.

After upgrading the dependencies to the latest versions I added a tsconfig:

{
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "lib",
    "target": "es2017",
    "allowJs": true,
    "checkJs": true,
  },
  "include": ["src"]
}

The key is to enable checkJs so that the Typescript compiler reports errors in javascript files.

And running tsc --noEmit against the codebase provided me with a list of 200+ compiler errors pointing me to every change required.

How to setup VS Code for Ruby development

After some trial and error with the various extensions available for Ruby, I’ve found the following combination to work well:

Ruby for debugging, syntax highlighting and linting. I use these VS Code User Settings:

"ruby.useLanguageServer": true,
"ruby.lint": {
  "ruby": true
}

Solargraph for intellisense and autocomplete.

endwise for wisely adding end to code structures. (Inspired by tpope’s endwise.vim)

Prettier and plugin-ruby for formatting.

Prettier plugin support is on the way, but for now we have to do this

Bonus for Rails: PostgreSQL for writing queries within the editor.

How to write a render prop

Hi my name is Matt. This is how to write a dependecy-inverted React component using the render prop pattern. It’s useful when you want to encapsulate and share common logic without knowing how it will be used or what children it should render. The render prop pattern is the successor to higher-order components and solves HoC’s problems with naming collisions.

If you’re on the latest version of React (>= 16.8) you should probably use a custom hook instead.

function FullName({ children, firstName, lastName }) {
    const fullName = firstName + ' ' + lastName
    return children(fullName)
}

// Usage:
function App() {
  return (
    <FullName firstName="Thor" lastName="Odinson">
        {fullName => <h1>Hello {fullName}!</h1>}
    </FullName>
  )
}