Loading...

Follow RisingStack Engineering | Node.js Tutorials & R.. on Feedspot

Continue with Google
Continue with Facebook
or

Valid

Welcome back! In the previous episodes of this React-Native tutorial series, we initialized the routing, added our custom font, and built our first component while working on the home screen.

Now, we are going to work on the main game logic and the Game screen.

Table of contents below:

You can find the whole codebase of our react-native mobile app here!

In the third article, He wrote: “Let there be game!”, and there was a game.

Let’s initialize our Game screen inside our screens directory by creating a Game directory with an index.js and styles.js. Then, in the Routes.js, import the screen so that we can use it in our router:

import Game from "./Game";

Also, inside the first argument of the createStackNavigator, there’s already a Home object: use that as a sort of template to add the Game screen to the router.

const StackNavigator = createStackNavigator(
 {
   Home: {
     screen: Home
   },
   Game: {
     screen: Game
   }
 },
…

After you save your code, the app will crash. (If it didn’t, good luck debugging it.) That’s because the Game/index.js is empty but we are already importing and using it in our router. Let’s initialize it with some boilerplate to silence the error!

import React, { Component } from "react";
import { View } from "react-native";
import { Header } from "../../components";
import styles from "./styles";

export default class Home extends Component {
 render() {
   return (
     <View style={styles.container}>
       <Header />
     </View>
   );
 }
}

Notice how it’s already using the ./styles - let’s define it! In the styles.js, add the following code:

import { StyleSheet } from "react-native";

export default StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#0a0a0a",
   justifyContent: "center",
   alignItems: "center",
 }
});

Also, the Header is a reusable component, but we need to modify it so that it suits our needs. As you can see on the picture below, the font size is slightly smaller.

You may want to work around it with a fontSize number property so that the size can be modified any time, or with an isMini boolean property that you can simply pass for the component, and it will automatically decide the font size.

Both approaches are totally valid, but I’ll go with the fontSize number property approach because I think it’s more flexible and future-proofed, since we can pass in any number we’d like.


How about PropTypes?

In React, you may already be familiar with the concept of PropTypes - you can type-check the components properties with it. In React-Native, you can use the very same method for type checking like in React: you just import the PropTypes with the line import PropTypes from ‘prop-types’ and then at the end of the file, you just add the .propTypes and .defaultProps properties. After that, everything will be all set:

Header.propTypes = {
 fontSize: PropTypes.number
}

Header.defaultProps = {
 fontSize: 55
}

However, we are not applying this property to the text itself - yet. Delete the fontSize property from the StyleSheet to make sure that the two properties won’t have a battle in the background and overwrite each other, and since we used a stateless functional component to declare the Header, we can’t use this.props. We can, however, use the arguments of the function to access the props by modifying the declaration line as it follows:

const Header = ({ fontSize }) => ( … } 

And from now on, you can just add the fontSize to every Text components style property like this:

<Text style={[styles.header, { fontSize }]}>blinder</Text>

Now, pass the desired fontSize prop to the Header component in the Game screen. After reloading the app, you’ll see that the Header component is now rendering properly on both screens -

or you’d see if you could go there, but we can’t yet, so let’s fix that!


Navigating from One Screen to Another

Before we start building our game screen, it is a good idea to add routing so that we can get there and see what we are building. It couldn’t be any simpler with react-navigator: we just need to add this.props.navigation.navigate('Game'); to our onPlayPress event handler: the react-navigator already managed to pass a navigation object as a property to our Home screen, and we can use its functions to navigate between screens. If you save the code and tap on the Play button, you are going to be routed to the Game screen.

Notice that by swiping back, you can get back to the Home screen. This may be the expected behavior when building an app, but it would be very nerve-racking to accidentally swipe back to the home screen while playing the game so it may be a good idea to disable this feature for now.

Please note that when you disable both the swipe navigation and the navigation bar, you need to be sure that have your own button on the UI that the user can use to navigate back to the previous screen!

You can read more about good navigation UX in Apple’s Human Interface Guidelines.

You can easily disable the swipe navigation on a particular screen by disabling the gesturesEnabled property in the navigationOptions of the Game screen in the Router.js, as it follows:

Game: {
     screen: Game,
     navigationOptions: {
       gesturesEnabled: false,
     },
   }

If you reload the app and try to swipe back from the Game screen, you’ll notice that you can’t, and that’s the behavior we wanted to achieve, so let’s move on.

We’ll get started by understanding the underlying game logic before trying to build the UI.


How will this work, exactly?

When the player starts the game, they will see a 2x2 grid with one tile slightly off:

They will have 0 points and 15 seconds after starting the game. When touching the correct tile, they’ll get +1 point and +2 seconds. If they touch the wrong tile, they get -2 seconds as a punishment. You can never win this game - it’s endless.

The grid will grow over time, but the maximum is a 5x5:

The colors are going to be randomly generated by generating the 0-255 values and passing these as an RGB color to the tiles.

The differentiating tile will have its RGB values mutated with a random value between 10 and 20.


Let’s create our random RGB value generator!

Since we are trying to make our code clean, we don’t want to create this in the Game directory. We’ll also have some other utilities, so let’s create a utilities directory in the root of the project, create an index.js and a color.js, and initialize the index.js before moving on:

export * from './color'

export default {}

And create our RGB value generator and the mutator in the color.js:

export const generateRGB = () => {
   const r = Math.floor(Math.random() * 255);
   const g = Math.floor(Math.random() * 255);
   const b = Math.floor(Math.random() * 255);
   return { r, g, b }
};

export const mutateRGB = ({ r, g, b }) => {
   const newR = r + Math.floor(Math.random() * 20) + 10;
   const newG = g + Math.floor(Math.random() * 20) + 10;
   const newB = b + Math.floor(Math.random() * 20) + 10;
   return { r: newR, g: newG, b: newB }
};

The mutator may seem a bit hacky:

it creates a random number between 10 and 20 and adds it to the original RGB value passed as a prop, then returns the new colors.


Defining the Main Logic

Now that we have some utilities for working with colors, we should set some basic things up on the Game screen, too - for example, defining the initial state is a good place to start off:

 state = {
   points: 0,
   timeLeft: 15,
 };

Also, adding a timer that divides the timeLeft in the state by one after every second can be done with setInterval(). Component lifecycle methods work the same way as in React, thus we can use componentWillMount() and componentWillUnmount() to create and destroy our timer:

 componentWillMount() {
   this.interval = setInterval(() => {
     this.setState(state => ({ timeLeft: state.timeLeft - 1 }));
   }, 1000);
 }

 componentWillUnmount() {
   clearInterval(this.interval);
 }

Notice how I added the interval to the Game screens scope (or this) - it’s in order that we can destroy it later in the componentWillUnmount(). If this arrow function thingy in the this.setState() looks a bit weird, be sure to check out the React docs -

it will convince you on why you shouldn’t use this.setState({ timeLeft: this.state.timeLeft - 1 }).


Let’s build the grid with some flex magic ✨

The key component on the screen is the grid with all them colorful tiles hangin’ out there, so let’s build that with flex. We are trying to keep the code needed as small as we can - so we are going to generate the grid instead of hardcoding it. Go to the screens/Game/index.js and import our shiny new utilities:

import { generateRGB, mutateRGB } from '../../utilities';

Then when declaring the state, initialize the first color, too:

 state = {
   points: 0,
   timeLeft: 15,
   rgb: generateRGB()
 };

Next off, add some constants to our render() function - we will use these later:

const { rgb } = this.state;
const { width } = Dimensions.get("window");

If you are not familiar with this syntax yet, it’s called object destructuring. With this, you can access an objects (e.g. this.state) properties without writing out this.state.rgb, but by destructuring and then just typing in rgb.

Dimensions is yet another very useful React-Native class (and you need to import it before you can use it, so be sure to add it to the top of the file): you can get the devices’ width and height and add event listeners to when the dimensions change (e.g. window resize, screen orientation change). Be sure to check out the related docs!

Now, add the container that will wrap our tiles:

<View style={{ height: width * 0.875, width: width * 0.875, flexDirection: 'row' }}>
</View>

Notice that it uses width * 0.875 for both width and height: I decided to style like this so that it’s always a perfect square and the containers dimensions match. You can style differently if you want to, but I recommend going with this.

Now that the container and the helper functions are finished, we can generate the grid inside the container with the following code:

{Array(2).fill().map((val, columnIndex) => (
  <View style={{ flex: 1, flexDirection: 'column' }} key={columnIndex}>
     {Array(2).fill().map((val, rowIndex) => (
        <TouchableOpacity
          key={`${rowIndex}.${columnIndex}`}
          style={{
             flex: 1,
             backgroundColor: `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,
             margin: 2
          }}
          onPress={() => console.log(rowIndex, columnIndex)}
        />
      ))}
  </View>
))}

This solution may seem a little bit hacky for first, but it’s flexible and definitely shorter than hard-coding every possible grid layout. It creates an empty array with 2 items with the Array(2), fills it so that we can map over it, and creates a <View> with the style properties flex: 1 to fill in the available space and it makes sure that the rows are under each other with flexDirection: column.

Then, inside these rows, it generates the tiles by creating another empty array and iterating over it and adding a TouchableOpacity for every iteration. This view has a flex: 1 property to flexibly fill up the space available, the backgroundColor is just a string with the RGB values (more about passing colors in React-Native in the docs), and the margin is added to make sure that there’s some space between the tiles.

You can add more margin to make the game harder or get rid of it to make the game very-very easy, but I think the 2-pixel margin is just ideal for a fun gameplay.

For now, the game looks like this:

It’s a nice grid - but let’s just don’t stop there. We need a differentiating tile, too! But before we dive into that, make sure that we have the size of the grid in our state:

state = {
  points: 0,
  timeLeft: 15,
  rgb: generateRGB(),
  size: 2
};

Then in the grid generators, replace every Array(2) with Array(size). Use object destructuring to get the size out of the this.state.

After you replaced them and you still get a 2x2 grid, so you’re good to go to generate the position and the color of the differentiating tile:

generateSizeIndex = size => {
 return Math.floor(Math.random() * size);
};

generateNewRound = () => {
 const RGB = generateRGB();
 const mRGB = mutateRGB(RGB);
 const { points } = this.state;
 const size = Math.min(Math.max(Math.floor(Math.sqrt(points)), 2), 5);
 this.setState({
   size,
   diffTileIndex: [this.generateSizeIndex(size), this.generateSizeIndex(size)],
   diffTileColor: `rgb(${mRGB.r}, ${mRGB.g}, ${mRGB.b})`,
   rgb: RGB
 });
};

The generateSizeIndex() generates a new number between 0 and the size passed as an argument. The generateNewRound() creates the data needed for a new round (differentiating tile index and color) and modifies the state to match it. This will run every time the user taps on the correct tile, and when initializing the game for the first time.

Call the this.generateNewRound() at the top of the componentWillMount() to make sure that the game initializes a round when the player opens the screen.

Now, we have a dynamic grid, and the properties of the differing tile: now, the only thing we need to do is to merge these two so that the differing tiles' color actually differs from the others.

First, in the render() where we destructure the state, add diffTileIndex and diffTileColor. Then, when passing the backgroundColor to the TouchableOpacity, you can check if the tile that’s being generated is the differing tile with the following: rowIndex == diffTileIndex[0] && columnIndex == diffTileIndex[1]. This checks if the row and the column of the tile and the differing tile match. We can use a ternary operator to modify the color of the tile, as it follows:

backgroundColor:
  rowIndex == diffTileIndex[0] && columnIndex == diffTileIndex[1]
     ? diffTileColor
     : `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,

If you refresh the app now, you’ll have one tile with the color slightly off:

If you don’t see the difference, increase the brightness on your screen.

At this point, we are just a few steps away from finishing off the grid -

a proper onPress handler is the only thing needed before we can move on to the bottom bar, so let’s finish off with that!


Handling Taps on Tiles

We need to add 1 point and 3 seconds to the timer if the player taps on the right tile, then generate a new color and increase the size of the grid if needed. If the player taps on the wrong tile, we decrease the timer by 1 second as a punishment and shake the grid (shaking the grid will be implemented later in the “Shaking the grid - animating in React Native” section, so don’t worry about that now!).

First, just defining a placeholder event handler and passing it as a prop onto our TouchableOpacity will do it:

onTilePress = (rowIndex, columnIndex) => {
  console.log(`row ${rowIndex} column ${columnIndex} pressed!`)
}

And in the <TouchableOpacity>:

onPress={() => this.onTilePress(rowIndex, columnIndex)}

And boom, when you press a tile, you will see which one was pressed in the console.

In the onTilePress(), we can use the same ternary code as we used when passing the backgroundColor to determine if the user has tapped on the differing tile:

onTilePress = (rowIndex, columnIndex) => {
  const { diffTileIndex, points, timeLeft } = this.state;
  if(rowIndex == diffTileIndex[0] && columnIndex == diffTileIndex[1]) {
    // good tile
    this.setState({ points: points + 1, timeLeft: timeLeft + 2 });
  } else {
    // wrong tile
    this.setState({ timeLeft: timeLeft - 2 });
  }
}

If you add console.log(this.state) to the end of the onTilePress(), you’ll be able to see how the state mutates over time. However, the grid does not change, so let’s call this.generateNewRound() in the good tile block, and modify the function a little bit so that it generates new color, too:

generateNewRound = () => {
   const RGB = generateRGB();
   const mRGB = mutateRGB(RGB);
   this.setState({
     diffTileIndex: [this.generateSizeIndex(), this.generateSizeIndex()],
     diffTileColor: `rgb(${mRGB.r}, ${mRGB.g}, ${mRGB.b})`,
     rgb: RGB,
   });
 };

Now tapping the correct tile will give you some extra time, points, and a new color. The only thing left is to increase the grid size.

Before generating a new index for the winner tile, calculate the grid size:

 generateNewRound = () => {
   const RGB = generateRGB();
   const mRGB = mutateRGB(RGB);
   const { points } = this.state;
   const size = Math.min(Math.max(Math.floor(Math.sqrt(points)), 2), 5);
   this.setState({
     size,
     diffTileIndex: [
       this.generateSizeIndex(size),
       this.generateSizeIndex(size),
     ],
     diffTileColor: `rgb(${mRGB.r}, ${mRGB.g}, ${mRGB.b})`,
     rgb: RGB,
   });
 };

This sizing method may look a bit odd, so let’s break it down to see a clearer picture of how it works: I wanted to achieve a one-liner, well-balanced solution for making the game harder by increasing the grid size over time.

My initial idea was to make the grids size the square root of the points - so when you reach 4 points it is 2, at 9 points it is 3, at 16 points it is 4, and so on. But I needed a minimum size for the grid (2) because before reaching that, there would be either zero or one tile. I also wanted a hard cap on the maximum size of the grid (it’s 5 in the example, but it could be any number you’d like).

Now if you play the game, you’ll start with a 2x2 grid, and as your points increase, you’ll see the tiles getting smaller, expanding into a 5x5 grid over time. Great job there!


Summing it Up

If you lost your way somewhere through this article or just want to clone the directory and pick up from there, you can access the code that’s finished as of now on the GitHub repo.

If you'd like to read the previous 2 parts of this article, I suggest you start here:


Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Node.js v12 is out on schedule! It is going into LTS in October and will be maintained until 2022.

Here is a list of changes we consider essential to highlight:

  • V8 updated to version 7.4
    • Async stack traces arrived (more info..)
    • Faster async/await implementation (more info..)
    • New JavaScript language features
    • Performance tweaks & improvements (more info..)
    • Progress on Worker threads, N-API
  • Default HTTP parser switched to llhttp
  • New experimental “Diagnostic Reports” feature

You can browse the full changelog here.

FYI: Back then we broke down the new features in Node.js v10 which you can read here.


assert module adjustments in Node 12

Let’s start with one of the most neglectable but important adjustment in the assert module. Starting from v12.0.0 the assertion methods validate the required arguments.


v11.14.0


v12.0.0

Instead of returning a misleading ERR_ASSERTION error, the methods indicate if there are arguments missing with the ERR_MISSING_ARGS error code.

Error messages got an update as well which remind us all to always use the error code to check for specific failures in the code base.


TLS 1.3 is now default in Node.js 12

TLS 1.3 is now the default max protocol supported by Node.js.

After 8 years, TLS has been updated and it offers enhanced security and performance. Support for RSA has been removed because of its history of bugs and it also did not support forward secrecy. The protocol also got safer cipher options and modes while halved the number of handshake roundtrips contrary to its 1.2 predecessor which required two roundtrips.


JavaScript language features in Node 12

Let's go one by one..

Async stack traces

So far, developers faced the problem of V8 truncating the stack trace up to the most recent await. Thanks to a recent update to the engine, Node.js now tracks the asynchronous call frames in the error.stack property.

async function wait_1(x) {
 await wait_2(x)
}

async function wait_2(x) {
 await wait_3(x);
}

async function wait_3(x) {
 await x;

 throw new Error("Oh boi")
}

wait_1(1).catch(e => console.log(e.stack));

This code example prints the following outputs before and after async stack traces got into Node.js.


v11.14.0


v12.0.0


Public class fields

Instead of listing all variables with default values in the constructor, you can define them on the class level.

class Vehicle {
 constructor(type) {
   this.type = type;
   this.speed = 80;
 }
}

Thus, you can omit the constructor if no parameter is needed or just focus on the required variables on initialization.

class Vehicle2 {
 speed = 80;

 constructor(type) {
   this.type = type;
 }
}

Private class fields

JavaScript brought in the concept of private class fields which finally landed in Node.js v12. To mark fields private just give them a name starting with #.

class Counter {
 #count = 0

 increment() {
   this.#count++;
 }

 get value() {
   return this.#count;
 }
}

const counter = new Counter()
counter.increment()
counter.value // 1

counter.#value // SyntaxError

Watch out, if You try to access a private field outside of the class it throws a SyntaxError error!


llhttp parser in Node.js 12

llhttp is a port of http_parser that improves on maintainability and benchmark results. The library claims to be faster by 116%.

The implementation was first introduced in v11.2.0 and it will be taken out from experimental in this release.


Diagnostic Reports

This utility tool is known as node-report that was recently brought into the Node.js core. It helps to detect abnormal terminations, memory leaks, high CPU usage, unexpected errors and more.

Run the node --experimental-report --report-on-fatalerror index.js to get a JSON summary on native stack traces, heap statistics, resource usage, etc.

$ node --help | grep report

 --experimental-report       enable report generation
 --report-directory=...      define custom report pathname.
 --report-filename=...       define custom report file name.
 --report-on-fatalerror      generate diagnostic report on fatal
 --report-on-signal          generate diagnostic report upon
 --report-signal=...         causes diagnostic report to be produced
 --report-uncaught-exception generate diagnostic report on uncaught

Node.js got a bunch of diagnostic utilities in the recent versions to aid the investigation on errors and bottlenecks that are difficult to pinpoint. If you want to create runtime statistics of the heap usage you can do that by calling v8.getHeapSnapshot() that was added in v11.13.0.


Worker threads in Node 12

The worker_threads module got into Node.js in v10.5.0. It’s still in experimental but a lot of effort has gone into its progress.

Node.js was designed single-threaded which fits I/O heavy use cases well. CPU heavy operations, however, increase execution time and lead to slow performance.

PSA: If you're facing performance problems with Node, reach out to us!

Now, 12factor says that these long-running operations should be offloaded to individual processes. However, this might not be a valid solution, when you need to expose the result of CPU heavy computations such as data-mining and crypto over HTTP. Workers open the possibility to utilize more threads at once to execute these actions parallel.

It’s not a stable solution though but it might be game-changing for the Node.js community. Workers offer Node.js an opportunity to become a player on the field of data science beside R, Scala, Python and more.


Get started with Node.js v12

You can download the latest Node.js version here.

Support Node.js by reporting issues you bump into!


Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

It's been a while since I've last written for the RisingStack blog, almost exclusively on Node.js. Since then, I've kept busy with getting into Design Systems, and I've wanted to share with you all what I've learned.

In this post I'll provide a brief introduction to design systems and describe the advantages and use-cases for having one. After that, I'll show Base Web, the React implementation of the Base Design System which helps you build accessible React applications super quickly.


What are Design Systems?

That's a hard question. I like to think about design systems as a collection of reusable components, constants and best practices that help both designers and engineers to build accessible applications with ease.

Let's dive a bit deeper into those three categories.


Constants, or Design Tokens

Design tokens codify values that are reused across applications to make them look and behave consistently. These tokens can include colors, grids or fonts, just to name a few.

By having these in your design system, application developers and designers can build consistent applications easier.


Reusable Components

Reusable components enable you to bootstrap applications fast, by reusing a set of already implemented inputs, pickers or surfaces. It has quite a few benefits:

  • accessibility can be built into the component library, so not all designers and developers have to master it,
  • each new feature to a component or a bug fix can be rolled out to all the applications depending on the component library, without having to ask all teams to update them,
  • it also enables you to design more consistently - you don't have to ask each team to re-implement a component.

Best Practices

Design tokens and reusable components are just the very foundations your developers and designers will have to build on top of.

It is also essential to provide the best practices around how the components and colors or spacing should be used, and how they should not be used.


Are Design Systems for me?

Depends. If you are a small team, working on a small project, design systems would probably take up a lot of your time, while product would suffer. To decide if it's worth starting to invest in a design systems team, I'd recommend the article Why Do Organizations Need a Platform Team?.

Even if you don't think it's time to build a dedicated team for your design efforts now, it helps a lot to start to invest in it.

One step you can take today is to start to limit your choices when it comes to design. Define a set of colors (3-5 distinct ones) and shades, and always use those. A good example can be found here. The same goes for fonts or spacing too. It's a bit of work upfront but will save you a lot of time in the longer run, as you won't have to figure these values out every time.

To learn more about this process, I highly recommend reading the book titled Refactoring UI, especially the section "Limit your choices".


Design System Examples

I believe learning by examples is one of the most powerful learning technique - it helps you put the theory into context. To do so with design systems, I'd like to show you a few examples.


Atlaskit by Atlassian

Atlaskit is Atlassian's official UI library, built according to the Atlassian Design Guidelines.

What's really interesting about this design system is the structure of the repository and the published packages - they use a monorepo for the components, but they are published as separate packages to npm. This enables them to version the components independently.


Polaris by Shopify

Polaris is the design system built by Shopify, to power their merchant experiences.

What's really amazing in Polaris is the way they document the design system for the web, android and ios on the same page. A great example of this is the documentation site for their date picker component.


Base Web by Uber

Base Web is the design system created by Uber, to serve as the base design system for applications.

One of the most powerful features of Base Web is its override patterns. This is the system I'd like to tell you more about in the next section and will dive deeper into overrides.


Introducing the Base Web Design System

In the past year, I've been involved in the Base Design System, and I'd like to introduce you to it. It helps you build accessible React applications super quickly.

Base Web is the React implementation of the Base Design System. It ships with a lot of opinions but also takes customizability to an entirely new level.


CSS Engine

Under the hood, Base Web uses Styletron for component-oriented styling. It was built from the ground up for high-performance while also producing the smallest possible CSS output.

The key idea is that rather than generating CSS classes that simply map 1:1 to the source style objects, Styletron breaks down everything into unique declarations and creates a corresponding “atomic CSS” class for each unique property/value pair.
Ryan Tsao

If you'd like to dive deeper into how Styleron works, please read the Virtual CSS with Styletron article.


Base Web's Components

As of today, Base Web ships with 37 components, ranging from simple components like the Avatar to more complex ones like the Select component.

To make sure the components work and look as they should for each release, we've dropped Jest's snapshot tests and started using a visual regression testing solution, called screener.io. What's really nice about this solution is that it integrates very well with GitHub pull requests, so if a component changes its visuals, your design team has the chance to review the changes before merging.


The Overrides Pattern

What makes Base Web extremely powerful is its overrides pattern. Base Web components typically consist of many subcomponents. Let's take a look at the drag and drop component:

import {StatefulList} from 'baseui/dnd-list';

export default () => (
  <StatefulList
    initialState={{
      items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
    }}
    onChange={console.log}
  />
);

Under the hood, it consists of multiple subcomponents like the Item, DragHandle or the Label.

With overrides, you can target all of them to change their

  • styles,
  • props,
  • or the whole subcomponent.

To make this work, every top-level Base Web component exposes a property called overrides, that maps to the subcomponents. Let's take a look at how it works in practice!

Let's change the background color of the items, and also add a left border to them. To make this work, we will use the style overrides. Under the hood, Styletron will merge the original styles with the overrides we provide.

import { StatefulList } from 'baseui/dnd-list';

export default () => (
  <StatefulList
    initialState={{
      items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'],
    }}
    overrides={{
      Item: {
        style: {
          backgroundColor: '#eee',
          borderLeft: '1px solid blue'
        }
      }
    }}
  />
);

The style property is just the tip of the iceberg - if you want to get more familiar with the override pattern, please check out the Overrides section of the documentation of Base Web.


How Design Systems Help

If you are building a product or a suite of products, and both consistency and accessibility are of paramount interest for your company, design systems can help a lot by:

  • reducing the time needed to bootstrap new applications, as there is a reusable set of components,
  • enable you to roll out bug fixes and new features to all products quickly.

If you'd like to get started with Base Web, check out the documentation site here: https://baseweb.design/.


Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

In the previous part of this React-Native series, we talked about what’s React-Native and Expo, what are some key similarities and differences between React and React-Native, and we also initialized our project.

In this episode, we’ll build the home screen of our React-Native application.

For a quick reminder, here’s the sketch of it:

Table of contents:


Let's start with building our React-Native HomeScreen

First things first, let’s split up the Home screen into two files: an index.js and a styles.js, just so that the main logic and the stylesheets are well-separated.

# the screens’ directory structure as of now
screens
├── Home
│   ├── index.js
│   └── styles.js
└── Routes.js

Let’s initialize the styles.js with a basic container style:

import { StyleSheet } from "react-native";

export default StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#0a0a0a",
   justifyContent: "center",
   alignItems: "center"
 }
});

Next, import the styles in the index.js, delete the StyleSheet and modify the JSX a bit, so that the index.js looks like this:

// basic imports ...
import styles from './styles';

export default class Home extends Component {
 render() {
   return (
     <View style={styles.container}>
	{/* // this View is empty on purpose! */}
     </View>
   );
 }
}

// notice the deleted styles - they are imported from the styles.js!


Creating the Header

Let’s create the header! We want it to be visible on the Home and the Game screen too, so we will build a component named Header that will display the rainbow text, and re-use it on both screens.

Simply create a Header.js file in the components directory and copy-paste the code below.

import React from "react";
import { Text, View, StyleSheet } from "react-native";

const Header = () => (
 <View style={{ flexDirection: "row" }}>
   <Text style={[styles.header, { color: "#E64C3C" }]}>c</Text>
   <Text style={[styles.header, { color: "#E57E31" }]}>o</Text>
   <Text style={[styles.header, { color: "#F1C431" }]}>l</Text>
   <Text style={[styles.header, { color: "#68CC73" }]}>o</Text>
   <Text style={[styles.header, { color: "#3998DB" }]}>r</Text>
   <Text style={styles.header}>blinder</Text>
 </View>
);

const styles = StyleSheet.create({
 header: {
   fontSize: 50,
   color: "#ecf0f1",
   fontFamily: "dogbyte"
 }
});

export { Header };

Because the Header does not need an internal state, we can define it as a stateless functional component. If you aren’t familiar with the idea of using SFCs yet, Hackernoon has a really great summary of what they are and how they work.

Next off, let’s initialize Components/index.js:

export * from './Header'

And import the Header component in the Home/index.js:

import { Header } from '../../components'
// …
<View style={styles.container}>
       <Header />
     	</View>

If you check your Expo Client at this point, your app will look like this:

This looks cool, but there’s a small error that we should fix before moving on to our next component: the iOS status bar blends into the background. We can fix this in a few lines of code on the root level, at the App.js,

with the StatusBar component.


Enabling our Status Bar

First, import the StatusBar from react-native and the Fragment from react (if you don’t know about React.Fragment yet, be sure to check the documentation, but in a nutshell, you can use Fragments when you don’t want to add another div to the DOM, but you need to return two or more components from somewhere for React).

import React, { Component, Fragment } from 'react';
import { StatusBar } from 'react-native';

Then add the StatusBar component to our app:

else {
   return (
      <Fragment>
        <StatusBar barStyle="light-content" />
        <Routes />
      </Fragment>
    )
}

It’s just a few lines of code that’s definitely worth adding - the app will now look like this:

That’s one small step for the code, one giant leap for the overall UX.


Adding Interactive Elements to our React-Native App

The logo looks pretty cool, but maybe it’s time to make some elements that the user can interact with - so let’s start with the big Play button that will dominate the screen:

Since we aren’t going to make a traditional button (it will contain an image, a text, and won’t have its own background or border), we won’t use a <Button> - instead, we’ll use a <TouchableOpacity>. It’s a react-native component that gives any component the ability to be tappable and respond to the interactions by dimming the opacity of it. You can read more about it in the React-Native docs.

Let’s import Text, Image, and TouchableOpacity along with View:

import { View, Text, Image, TouchableOpacity } from "react-native";

Create a callback named onPlayPress. Until we figure out what to do with it, a placeholder console.log() will be fine:

onPlayPress = () => {
  console.log("onPlayPress event handler");
};

And finally, the button itself:

<TouchableOpacity onPress={this.onPlayPress} style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Image
    source={require("../../assets/icons/play_arrow.png")}
    style={styles.playIcon}
  />
  <Text style={styles.play}>PLAY!</Text>
</TouchableOpacity>

Notice how you can import images with the require() function. We will use it along this course because the documentation features it, however, there are better solutions out there, so be sure to check them out if you are aiming for production.

If you run the app, you’ll have to realize that we already passed the styles, but they are not defined yet, so let’s go to styles.js and create them:

play: {
   fontSize: 45,
   fontFamily: "dogbyte",
   color: "#ecf0f1",
   marginTop: 5
 },
 playIcon: {
   height: 60,
   width: 60,
   marginRight: 15
 }

There’s nothing special there that would need any explanation, so let’s move on. The app should look like this at this point:

Let’s continue with the Hi-score component:

we can worry about the layout later.


Building the High Score Panel

The high score panel has a similar layout to the Play button, but it’s not tappable, and it’s slightly smaller too - thus we’ll need another bunch of styles for the new, smaller sizes:

hiscore: {
  fontSize: 28.5,
  fontFamily: "dogbyte",
  color: "#ecf0f1",
  marginTop: 5
},
trophyIcon: {
  height: 45,
  width: 45,
  marginRight: 12.5
}

And include the new <Image /> and <Text> in a <View>:

<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Image
    source={require("../../assets/icons/trophy.png")}
    style={styles.trophyIcon}
  />
  <Text style={styles.hiscore}>Hi-score: 0</Text>
</View>

We will make this section show the real high score later in the 5th post of this series in the “Persisting data -

storing the high scores” section.


DIY: Build the Leaderboard Button!

This button will look and behave very similar to the Play button:

At this point, try to pause reading and copy-pasting things from the article, and try to build this on your own! Practice makes perfect. If you are stuck with it, try to look back at the Play button and the Hi-score panel’s source for some inspiration, or if you really don’t feel like it,

check the finished code in the GitHub repo.


Let’s add a copyright banner & speaker icon!

In the sketch, there were a few smaller things on the bottom of this screen: a copyright banner, and a speaker icon for muting the game.

The container is just an absolute positioned View, nor the text style isn’t too exciting:

bottomContainer: {
   position: "absolute",
   left: 15,
   right: 15,
   bottom: 12.5 // the 2.5px bottom margin from the text is subtracted from the 15px spacing
 },
 copyrightText: {
   fontSize: 16,
   fontFamily: "dogbyte",
   marginBottom: 2.5
 }

And the JSX isn’t a big hack either:

<View style={styles.bottomContainer}>
  <Text style={[styles.copyrightText, { color: "#E64C3C" }]}>
    Music: Komiku
  </Text>
  <Text style={[styles.copyrightText, { color: "#F1C431" }]}>
    SFX: SubspaceAudio
  </Text>
  <Text style={[styles.copyrightText, { color: "#3998DB" }]}>
    Development: RisingStack
  </Text>
</View>

You may notice that toggling the speaker icon only updates an internal state as of now. We will add music and SFX later, in the 5th post of this series. Let’s define our initial state:

state = {
  isSoundOn: true
};

Toggling the sound will also change the icon, and as it has two possible states (music muted and enabled), we’ll have two corresponding icons:

In the render function, we want to dynamically import the icon based on the state:

 render() {
   const imageSource = this.state.isSoundOn
     ? require("../../assets/icons/speaker-on.png")
     : require("../../assets/icons/speaker-off.png");
   // ...

We need to add a TouchableOpacity with an image in it. It will display the speaker icon, but to push it to the right side of the screen, you can either play with adding margins, or adding a <View style={{ flex: 1 }} /> before the button.

The empty view will fill up all the empty space on the screen because of its flex: 1 property. It may seem a bit odd for first, but it’s a commonly used practice in React-Native development to use this when doing MVPs, but in production, you should probably stick with using margins or any other solution that’s idiomatic to your use case.

<View style={{ flex: 1 }} />
<TouchableOpacity onPress={this.onToggleSound}>
    <Image source={imageSource} style={styles.soundIcon} />
</TouchableOpacity>

Currently, our app looks like this:

You may have already noticed that the spacing is messed up, so let’s fix that by adding some spacing with margin:

  • For the TouchableOpacity wrapping the play button, add marginTop: 80 to the style property
  • For the View wrapping the Hi-score, add marginTop: 20
  • For the TouchableOpacity wrapping the Leaderboard button, add marginTop: 80
What to expect in Part 3 of this React-Native Tutorial Series

Now that the UI elements can breathe and our home screen looks nice, we can move on the screen that the players are going to spend most of their time on - the game screen.

You can access the code that’s written at this point here.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Whether you want to experiment with developing a mobile app or have a business idea in mind, kicking off mobile development with React-Native is a great idea, and it’s especially an obvious choice if you are already familiar with React.

In this series, we’ll cover the basics of React-Native development, compare some ideas with React, and develop a game together. By the end of this tutorial, you’ll become confident with using the built-in components, styling, storing persisting data, animating the UI and many more.

If you want to check out the full sourcecode, here's the GitHub repo.

This series is recommended for developers already familiar with React, so we won’t take too much time explaining basic React concepts.

In Part I. of this series we'll discuss the following:

  • React-Native vs real-native: what’s the deal?
    • What is React-Native, who is behind it?
    • Why would you want to develop in React-Native?
    • Key differences and similarities between RN and native apps
    • A few words about real-time reload over recompiling
    • How does RN compile and work in the background?
  • What’s Expo?
    • The core concept behind Expo: extending React-Native features with pre-written native features
    • The brief history of how it became the main interface for developing React-Native apps
    • Pros and cons of using Expo over developing purely in React-Native
  • Differences and similarities between React and React-Native:
    • States and props
    • Flexbox (how it works in React-Native)
    • Components & most important pre-written components
  • Initializing the environment:
    • Installing the CLI, creating the project
    • Exploring directory structure
    • Assets (icons, fonts, music, sfx)


React-Native vs real-native: what’s the deal?

You may look down on hybrid apps because of their poor performance in the past (I’m looking at you Ionic!), but you can forget them for now.

In the background, React-Native uses the same native building blocks as you would use while developing native apps with Swift, Java or Kotlin. The only difference is that you put these building blocks together with JavaScript.

But if it’s just a simple API wrapper, why do developers love it, then?

React-Native’s real power comes from giving you a load of features that improve the developer experience, like Live Reload that reloads the app in a few seconds when saving, or Hot Reloading that injects code changes without reloading the app and losing your state.

Here’s a video that compares these two.


What’s Expo?

As you may have already noticed, the React-Native documentation features Expo CLI as the default toolchain to get started. Let’s take a look at what’s Expo, where does it come from, and why is there a 3rd party in the official documentation.

Expo is an SDK that started as an unofficial toolkit, but then it grew into a much bigger project with features like OTA app updates (bypassing the long App Store review times), social authentication, camera access, push notifications and many more services out of the box, for totally free!

They also have an online playground named Snacks where you can try out other people’s code online, or work on bite-sized projects without the need of creating a project in the CLI.

The core concept behind Expo is to extend React-Native with pre-written components like the ones mentioned above. Expo has awesome documentation with an exhaustingly long list of APIs in the sidebar, be sure to check it out!

However, Expo also has its downsides: your app will have a considerable size when you build it (about 20-30MB .apk for a Hello World project) because it has all of these features built-in, not only the ones that you use; and at this time, you can’t explicitly write or use native Swift/Kotlin code.

Since we aren’t aiming production nor planning to write native code in our project as we’re only building an example project, Expo is the fastest way to start off, thus we’re going with it.

(However, if you want to, you can always detach your project from Expo.)


Does React-Native have a different approach for the same concepts as React?

In short: no.

For example, states and props work the same way, components have the same underlying idea to them as in React, and you can even access React APIs (like the Context API) within React-Native. However, some things couldn’t be implemented the same way as in React, simply because web development’s logic differs from mobile.

For example, flexbox works the same way in CSS with a few exceptions: the flex-direction is defaulting to column instead of row, and the flex only supports a single number as a parameter. Also, it’s the default method to build UIs. If it isn’t already familiar for you, consider practicing the use of flexbox: Flexbox Froggy is a cool game that you should try before moving on with the tutorial.

Since you aren’t writing for the web, you cannot use default HTML tags like div or img - you’ll need native UI components.

Please keep in mind that the default components may look and behave differently on different platforms, so you should always test them on all platforms before releasing something. (Oh, and negative margins don’t work on Android)

But what are the building blocks of the UI if not HTML elements?

There are plenty of useful pre-written components that React-Native provides us. For example, instead of <div>s, you use <View>s, for displaying texts, you use <Text>, for buttons, you use <Button>, and so on. Please be sure to check out the official documentation that highlights the most important components (and the full list is always available from the sidebar) before moving on.


But what are we building, exactly?

Good question. We are building a fun minigame I call “ColorBlinder”. The main game screen is pretty simple: it’s built up from a few tiles that are colored the same, except one. The player’s task is to tap the differing tile as fast as possible.

Some mockups I made before starting the development, just to illustrate the main idea. We’ll use these illustrations along the course when building components.


Let’s just start developing already! a.k.a. setting up our Development Environment

Okay, okay, geez, don’t be so impatient! We’ll get to it soon enough, but first, we’ll need some tools to kick off the development. Make sure that you have Node.js installed, and install the Expo CLI:

npm i -g expo-cli

Then let’s init an empty project. I’ll call it colorblinder as this is the name of the game we’ll work on along the series, but you can call it whatever you want.

expo init colorblinder

## choose `empty` template for this project
## choose `managed` workflow
## write in `ColorBlinder`
## if it asks, use yarn to install dependencies. (If you don’t have it, [be sure to check it out](https://yarnpkg.com/), it’s a fast alternative to npm)

cd colorblinder
npm start ## or yarn start if you have yarn, or expo start, whichever you prefer

This command will start the development server with an empty project for you, and open a new Chrome tab with the Expo DevTools. You can use a simulator or a device with Expo Client installed, whichever you prefer.

If you want to check out the full source code in one piece, check out the GitHub repo.


Project Structuring our React-Native Game

First, let’s explore the directory structure of our new project.

├── App.js
├── app.json
├── assets
│   ├── icon.png
│   └── splash.png
└── package.json
# a few more files that we don’t care about yet

The structure may seem simple, but just to make sure that it all makes sense, we’ll look through it.

First, let’s explore the app.json. It’s a configuration file for Expo: it contains the SDK version, name, logo/splash screen directions, platforms, GitHub URL, and a lot more. Explore it in details from the documentation.

If you want to develop the game on your own, download the assets and copy-paste it into your assets directory. Make sure that you are overwriting icon.png and splash.png! The sound effects originate from here, and we’ll use music made by Komiku - be sure to check them out and donate for their work if possible, they deserve it.

After you pasted in the assets and open the app, you may notice that the sizing of the splash screen is a bit off, so before we start initializing our home screen, we should fix that. In the app.json under the splash property, set the backgroundColor to #0a0a0a.

(If you don’t see it yet, restart the packager and/or the client).


Let’s Start Coding!

As you may have seen, we’ll have a few different screens (two, to be exact), so we are going to need a router to navigate between them. Expo’s documentation has a pretty good explanation on why you may need a router if you aren’t familiar with the idea of using routers already. We are going to use react-navigation, so I suggest to check out their Getting Started guide.

Add react-navigation as a dependency to the project:

yarn add react-navigation
## if you don’t have yarn: npm install react-navigation

Create a components directory for our components that we’ll build (like the header or the grid), and a screens directory for the screens we’ll compose (the Home and the Game screen exactly). Make sure that both of the directories are in the root of the project, and create Home.js in the screens directory. Just to make sure that it renders something, copy-paste this into it:

import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default class Home extends Component {
 render() {
   return (
     <View style={styles.container}>
       <Text>ColorBlinder - Home screen!</Text>
     </View>
   );
 }
}

const styles = StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: '#fff',
   alignItems: 'center',
   justifyContent: 'center',
 },
});

Notice that you are extending the React.Component class, so nothing new there. Instead of divs, you are using <View>s that are imported from the react-native package, and to show some text to the user, you use a <Text> tag.

Also, the StyleSheet on the bottom of the file may look weird: it’s a React-Native specific API. It’s basically an abstraction for CSS: you can create new styles in the object and name them anything you’d like, and you can use basic CSS properties (just camelCased) to define properties.

You can use them in your JSX simply by putting the name of the style in the style={} prop. (If you want to separate the JSX and the styling, you can, but for now, we’ll have them in the same file - we will move the StyleSheet to a different file later, though!)


Initializing the Routing

Now that Home.js is rendering something, let’s initialize the routing in the App.js:

import { createStackNavigator, createAppContainer } from "react-navigation";
import Home from './screens/Home';

const AppNavigator = createStackNavigator({
 Home: {
   screen: Home
 }
});

export default createAppContainer(AppNavigator);

We are using react-navigation’s createStackNavigator: it initializes a new stack navigator that returns a React component that we can render - thus we can export it from App.js and except it to work.

When you run the code, you’ll see something like this:

You can see that there’s an (empty) navigation bar on the top of the screen: we definitely don’t want it to be visible as it would make the game look terrible, so in the App.js, let’s spice up our stack navigator with some properties:

const AppNavigator = createStackNavigator(
 {
   Home: {
     screen: Home
   }
 },
 {
   initialRouteName: "Home",
   headerMode: "none",
 }
);

The top navigation bar will now be hidden.


Using Custom Fonts

The Expo documentation’s guide gives you a straightforward idea on how to use custom fonts in your app, but we will need some major changes in our app structure to be able to integrate them into the project since we are using a navigator.

As you can see, you need to render a different component while you are loading the font (the splash screen would be perfect!), so first, rewrite your App.js as the following:

import React, { Component } from 'react'
import { AppLoading, Font } from 'expo';
import Routes from './screens/Routes'

class App extends Component {
 state = {
   isFontLoaded: false
 }

 async componentDidMount() {
   await Font.loadAsync({
     'dogbyte': require('./assets/fonts/dogbyte.otf'),
   });
   this.setState({ isFontLoaded: true })
 }

 render() {
   if(!this.state.isFontLoaded) {
     return <AppLoading />
   } else {
     return <Routes />
   }
 }
}

export default App;

We are asynchronously importing the font dogbyte. The state has an isFontLoaded property that will be true when the font is loaded. While it’s false (or in other words, while the font is not loaded), it will render the <AppLoading /> component that comes from the Expo SDK. This component shows the splash screen of the app when rendered.

When the this.state.isFontLoaded is true (when the font is loaded), it will render the Routes component, that is undefined as of now, so let’s quickly create a file under screens named Routes.js and copy-paste our previous navigator code into it:

import { createStackNavigator, createAppContainer } from "react-navigation";
import Home from "./Home";

const StackNavigator = createStackNavigator(
 {
   Home: {
     screen: Home
   }
 },
 {
   initialRouteName: "Home",
   headerMode: "none"
 }
);

export default createAppContainer(StackNavigator);

Prepare for the next part of our React-Native Guide!

At this point, we have basic navigation initialized in our app and can use our custom font. It’s time to call it a day, pat yourself on the back for making it this far into your journey and have a rest.

In the next session, we’ll build the home screen of the app!

The next episode will be released on April 9!

If you weren’t following along or you got stuck somewhere, you can access the code that’s written at this point here.


Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Today we announce our collaboration with IBM on the Cloud Native JS project, which aims to provide best practices and tools to build and integrate enterprise-grade Cloud Native Node.js applications.

As a first step of contribution to the project, we released an article on CNJS’s blog written by Tamas Hodi, senior engineer of RisingStack.

In our latest article titled “How to Build and Deploy a Cloud Native Node.js App in 15 minutes” we show how you can turn a simple Hello World Node.js app into a Dockerized application running on Kubernetes with all the best-practices applied - using the tools provided by CNJS in the process.

By the end of this tutorial, you will:

  • Implement lifecycle management for a Node.js application
  • Set up monitoring and tracing
  • Build the Docker image for the application
  • Prepare the application to be deployed into Kubernetes with Helm

Click to read the tutorial.


Why We Support Cloud Native JS

We warmly welcome IBM's CNJS project as we have been using Cloud Native technologies since their conception, and we firmly believe that this is the way apps should be built.

Node.js developers overwhelmingly deploy ther apps to the cloud, but only around 12% are building fully Cloud Native applications. The goal of the CNJS project is to help developers leverage the true potential of current cloud services by integrating with the CNCF's open source technologies.

The Cloud Native JS project currently provides the following assets:

  • Node.js module insights
  • Node.js module LTS policy
  • Template Dockerfiles
  • Template Helm charts
  • Health Check support
  • Prometheus monitoring
  • OpenTracing request tracking

We are thrilled to join IBM's CNJS initiative and we're looking forward to contribute to the project!

At RisingStack we've been an active member of the Node.js community from the start of the company, and our engineers have deployed thousands of services in the past 5 years. We are glad that tooling is being built for our favorite server-side JavaScript framework that enables developers to use it more easily on the ecosystem of our choice.


RisingStack and Cloud Native Technoloiges

When our clients ask us to create a highly available distributed system, we usually recommend Kubernetes, because it's easy to use and even though it has a steep initial learning curve, it's still a lot more simple than other competing orchestration engines. In the meantime it is more than just an orchestration system but a platform as well with a vast ecosystem.

This ecosystem gains a lot from the existence of CloudNative foundation as they provide a place for several tools that enhance the use of Kubernetes. We at RisingStack use Helm charts to quickly plug in other tools we use such as Jaeger or Promethues, technologies that provide observability. We also use Linkerd to have more control over the communication layer between the services or Harbor to make sure our container images are secure.

We also like to blog about Kubernetes:


More About the Cloud Native JS Project

The CNJS Project provides several tools that help you with utilizing cloud native Node.js apps:

  • The Module Insights page helps you choose what popular Node modules you should depend on, by gathering info about their stability, license, and version information. It also executes a modules' own test suite against all current LTS versions of Node.js across multiple platforms

  • The 'cloud-health' module is a core library that provides health checking and gives you more control over application lifecycle management (startup, running, shutdown).

  • OpenTracing enables vendor neutral instrumentation of distributed applications with minimal effort.

  • Enable powerful monitoring for your distributed application and configure rule-based alerting using the Prometheus open source project.

  • The 'docker' repository provides a set of template Dockerfiles that implement the best-practices for building development and production Docker images for your Node app.

  • The 'helm' repository provides a template Helm chart that simplifies the process of taking a Docker image of your application and deploying it to Kubernetes along with configuration for replicas and autoscaling.

If you'd like to contribute, join the #cloudnative channel within the Node.js Slack.


Guidance with Cloud Native Node.js

If you need help with Cloud Native JavaScript Apps, feel free to ping us on Twitter or at info@risingstack.com!

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

In the following blogpost, we will walk you through how we chased down a DNS resolution issue for one of our clients. Even though the problem at hand was very specific, you might find the steps we took during the investigation useful. Also, the tools we used might also prove to be helpful in case you'd face something similar in the future. We will also discuss how the Domain Name System (works), so buckle up!


The Symptoms

Our client hosts their static assets and webshop at a hosting provider. They also provide a Wordpress like CMS and nameservers as well. The client wanted to reduce costs, so they asked us to move their static assets to AWS S3 and setup CloudFront to cache them. The client set up a CNAME record pointing to the CloudFront, thus it was available at assets.clients-domain.com as well as distribution-id.cloudfront.net

Setting up CloudFront was pretty straightforward. However, we were pretty surprised when we got a ticket claiming that images are not visible when the site is visited from a mobile browser. Another subcontractor, who handles the development of the static pages reported that they can only access them from certain locations, but from others, they are not available.


Forming the DNS resolution error hypothesis, or why 'curl' is better than browsers

The first method we tried to reproduce the error was accessing the resource from the browser at https://assets.clients-domain.com/img/test-image.png. It was pretty difficult as browsers had no problem loading the assets in our office. Then we used a VPN to test them from other locations.

The results were inconsistent: with disabling the browser cache the images we tested were loaded from one location without issues, from others it failed at first with 502 Bad Gateway. Then, at some point, it started working and we weren't able to break it again no matter how hard we tried. Then we tried using curl. 50% of the time it worked, but the other 50% it reported:

$ curl https://assets.clients-domain.com/img/test-image.png --output test.png
% Total	% Received % Xferd  Average Speed   Time	Time 	Time  Current
                             	Dload  Upload   Total   Spent	Left  Speed
  0 	0	0 	0	0 	0  	0  	0 --:--:-- --:--:-- --:--:-- 	0curl: (6) Could not resolve host: assets.clients-domain.com

Once we saw Could not resolve host: assets.clients-domain.com it was clear that we are facing a DNS issue. Or at least it was an educated guess worth verifying.


'dig'-ing deep for verification

To verify our hypothesis, we tried to reach CloudFront straight. It worked fine, so we knew we were on the right track..

First, we thought there might be a problem with the way we set up the CNAME record in CloudFront, so we started digging. We opened two panels in our terminals next to each other and ran watch curl https://assets.clients-domain.com/img/test-image.png --output test.png and watch dig assets.clients-domain.com.

dig reported the following when curl failed to reach the server:

$ watch dig assets.clients-domain.com
; <<>> DiG 9.13.5 <<>> assets.clients-domain.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 24152
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;assets.clients-ip.com.   	 IN    A

;; AUTHORITY SECTION:
clients-ip.com.   	 300    IN    SOA    virga.hosting-service.com. root.virga.hosting-service.com. 2018091202 10800 3600 604800 86400

;; Query time: 183 msec
;; SERVER: 213.46.246.53#53(213.46.246.53)
;; WHEN: Fri Feb 01 17:18:12 CET 2019
;; MSG SIZE  rcvd: 106

When we got a proper answer section, curl managed to download the asset.

$ watch dig assets.clients-domain.com

; <<>> DiG 9.13.5 <<>> assets.clients-domain.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51530
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;assets.clients-domain.com.   	 IN    A

;; ANSWER SECTION:
assets.clients-domain.com.    297    IN    CNAME    distribution-id.cloudfront.net.
distribution-id.cloudfront.net. 57 IN    A    13.32.22.20
distribution-id.cloudfront.net. 57 IN    A    13.32.22.108
distribution-id.cloudfront.net. 57 IN    A    13.32.22.112
distribution-id.cloudfront.net. 57 IN    A    13.32.22.152

;; Query time: 22 msec
;; SERVER: 213.46.246.53#53(213.46.246.53)
;; WHEN: Fri Feb 01 17:17:51 CET 2019
;; MSG SIZE  rcvd: 156

Now we started to suspect the problem is not on our side after all. However, let's dissect the output of dig first.

Dig is a DNS lookup utility program that you can use to gain information on how a domain name is mapped to an IP address. You can pass it several options, such as +cmd which prints dig's version and the command you entered to the terminal. To omit it, you can use dig +nocmd assets.clients-domain.com.

There are several other options such as +short which will give you a terser, parseable output, or +trace which will trace the nameservers that were used for the domain name resolution. After the issued command you can also see the ->>HEADER<<- printed. We either got NXDOMAIN stating that the domain we are looking for is non-existent, or NOERROR, when we get back the IP address for the query.

The QUESTION SECTION reminds us of the domain and subdomains we were looking for and of the fact that we were looking for an A record, thus essentially for an IP address.

When the DNS resolution fails, we are only given an AUTHORITY SECTION which tells us that dig was able to find the domain authority (SOA), but was not able to find anything that points to the subdomain.

However, when dig is able to resolve the subdomain, it tells us that it found a CNAME record on the authoritative nameserver pointing to CloudFront, and provides us with the IP addresses of CloudFront’s nameservers, as you can see highlighted below.

;; ANSWER SECTION:
assets.clients-domain.com.    297    IN    CNAME    distribution-id.cloudfront.net.
distribution-id.cloudfront.net. 57 IN    A    13.32.22.20
distribution-id.cloudfront.net. 57 IN    A    13.32.22.108
distribution-id.cloudfront.net. 57 IN    A    13.32.22.112
distribution-id.cloudfront.net. 57 IN    A    13.32.22.152

Now that we understand the output of dig, let's continue with the investigation.


'+trace'-ing nameservers

We wanted to see where the domain name resolution got stuck when we encountered failures, so we ran dig +trace assets.clients-domain.com. Again, we had two different kinds of output. One where the resolution failed:

$ dig +trace assets.clients-domain.com

; <<>> DiG 9.13.5 <<>> +trace assets.clients-domain.com
;; global options: +cmd
.   		 84782    IN    NS    h.root-servers.net.
.   		 84782    IN    NS    a.root-servers.net.
.   		 84782    IN    NS    e.root-servers.net.
.   		 84782    IN    NS    l.root-servers.net.
.   		 84782    IN    NS    f.root-servers.net.
.   		 84782    IN    NS    c.root-servers.net.
.   		 84782    IN    NS    g.root-servers.net.
.   		 84782    IN    NS    b.root-servers.net.
.   		 84782    IN    NS    k.root-servers.net.
.   		 84782    IN    NS    j.root-servers.net.
.   		 84782    IN    NS    d.root-servers.net.
.   		 84782    IN    NS    m.root-servers.net.
.   		 84782    IN    NS    i.root-servers.net.
.   		 84782    IN    RRSIG    NS 8 0 518400 20190214050000 20190201040000 16749 . K8k6KqovcMQnSsYoh+9rLiBK2423be5fvZb06NdcRz1tGqsigMQEZg2k IzOv9iPmqqcS0eB5mVxdm1NXcoRYuGQcSwTA9yBWcgs1AZxiEMOIJLNT JTxyiClPo2KKFe32pfJN1ljzZhSP26KI+/htBbRsX0qZARs80cfXOo5v ZUMO875h4ldHI9+UbR9PpkFtfmSHINkiatMQRczFScV0e0Zelqwd9QUq mBzU3vnhw+gqtxyTowkQ4hGictt/KVdJDPqkrV0BFqWocmaoryORGDnv 2IRLyV1uNB0jJrXnzwP492L4d1OhSRslGNfJPotsfNY7cMb2Z6xfO9RL Hvylgg==
;; Received 540 bytes from 213.46.246.53#53(213.46.246.53) in 14 ms

com.   		 172800    IN    NS    f.gtld-servers.net.
com.   		 172800    IN    NS    b.gtld-servers.net.
com.   		 172800    IN    NS    h.gtld-servers.net.
com.   		 172800    IN    NS    g.gtld-servers.net.
com.   		 172800    IN    NS    k.gtld-servers.net.
com.   		 172800    IN    NS    c.gtld-servers.net.
com.   		 172800    IN    NS    a.gtld-servers.net.
com.   		 172800    IN    NS    m.gtld-servers.net.
com.   		 172800    IN    NS    j.gtld-servers.net.
com.   		 172800    IN    NS    e.gtld-servers.net.
com.   		 172800    IN    NS    d.gtld-servers.net.
com.   		 172800    IN    NS    l.gtld-servers.net.
com.   		 172800    IN    NS    i.gtld-servers.net.
com.   		 86400    IN    DS    30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
com.   		 86400    IN    RRSIG    DS 8 1 86400 20190214050000 20190201040000 16749 . OTMp18F8zF6IFwUeH4PC4owXLtMIxejaBs+r2PkgIxM5dDtGIj+JXF6R kXmxsQi7FlMhQq/OxU7B3HksQ8CCXVb4rYEo+6vz8ugElRkGKBZf0tkd 4C/JjleSX5kAHdgYnK5m/0bWq4wxMw+R0sSdsbnVmc+Jzv/S3T+Aj4la 0heACCqQYY+2rrGBJ7pxTWjR2JvK8p8NgVvx6k3fZlG0p5QbnajnGMMY vyB/GtYv3uvLnS4JLQvUMU7meIq6hm+wqpI1kp676ypu+KvoqQVFaO/E u4Rbv7ie5CsQOT4H/7jc8pw6IQqqD3FjdFX2yoW4u9pSwy8LrDgYHix7 AielIA==
;; Received 1208 bytes from 192.112.36.4#53(g.root-servers.net) in 55 ms

clients-domain.com.   	 172800    IN    NS    ns2.hosting-nameserver.com.
clients-domain.com.   	 172800    IN    NS    ns1.hosting-nameserver.com.
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A NS SOA RRSIG DNSKEY NSEC3PARAM
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 8 2 86400 20190208054243 20190201043243 16883 com. LzTH+svBHgjuQk3FHAvNO72auLxibi3E6gzqFOkT7BwVqEYSLLd6b2fn r+y8A5fYEsR7VDRS6F+fODsOXlvfAR/Dr4oww/8FYMlAG7eYr5g4Bcsv TTnAqdXhPDoJgfZ6+BTodLgbY6tYZWNNnV2wS/iv0xfZ3BpAVnXEqgmD GrE=
FJ6U8VTBKL1C7L340CMMNNFNL3DP254G.com. 86400 IN NSEC3 1 1 0 - FJ70PP8IH3U3OPHIR4INIE39O39HKIVK NS DS RRSIG
FJ6U8VTBKL1C7L340CMMNNFNL3DP254G.com. 86400 IN RRSIG NSEC3 8 2 86400 20190207061242 20190131050242 16883 com. P5v6fKCuxOuzfmR2IXXZgns/m+NkvDJ2Ph4Az/Rbs+VkOV8jTHlPr/FZ k7EvoW06jHUbDLqa0UdY92IFcK/Z0kEO3t76mcQtd/0WXvVQkBHCyb0Q UfaxxPe00oeEh8Ic/6u5Zz/Co0i7rYXoVKQIprTqngs+x3g5luUogp/Y iLE=
;; Received 612 bytes from 192.48.79.30#53(j.gtld-servers.net) in 278 ms

clients-domain.com.   	 300    IN    SOA    virga.hosting-nameserver.com. root.virga.anticsdms.com. 2018091202 10800 3600 604800 86400
;; Received 106 bytes from 50.56.75.143#53(ns2.hosting-nameserver.com) in 217 ms

And another when the domain name got resolved properly:

⇣96% ➜ dig +trace assets.clients-domain.com

; <<>> DiG 9.13.5 <<>> +trace assets.clients-domain.com
;; global options: +cmd
.   		 79456    IN    NS    e.root-servers.net.
.   		 79456    IN    NS    b.root-servers.net.
.   		 79456    IN    NS    d.root-servers.net.
.   		 79456    IN    NS    j.root-servers.net.
.   		 79456    IN    NS    m.root-servers.net.
.   		 79456    IN    NS    i.root-servers.net.
.   		 79456    IN    NS    l.root-servers.net.
.   		 79456    IN    NS    g.root-servers.net.
.   		 79456    IN    NS    c.root-servers.net.
.   		 79456    IN    NS    k.root-servers.net.
.   		 79456    IN    NS    f.root-servers.net.
.   		 79456    IN    NS    a.root-servers.net.
.   		 79456    IN    NS    h.root-servers.net.
.   		 79456    IN    RRSIG    NS 8 0 518400 20190214050000 20190201040000 16749 . K8k6KqovcMQnSsYoh+9rLiBK2423be5fvZb06NdcRz1tGqsigMQEZg2k IzOv9iPmqqcS0eB5mVxdm1NXcoRYuGQcSwTA9yBWcgs1AZxiEMOIJLNT JTxyiClPo2KKFe32pfJN1ljzZhSP26KI+/htBbRsX0qZARs80cfXOo5v ZUMO875h4ldHI9+UbR9PpkFtfmSHINkiatMQRczFScV0e0Zelqwd9QUq mBzU3vnhw+gqtxyTowkQ4hGictt/KVdJDPqkrV0BFqWocmaoryORGDnv 2IRLyV1uNB0jJrXnzwP492L4d1OhSRslGNfJPotsfNY7cMb2Z6xfO9RL Hvylgg==
;; Received 540 bytes from 213.46.246.53#53(213.46.246.53) in 18 ms

com.   		 172800    IN    NS    k.gtld-servers.net.
com.   		 172800    IN    NS    h.gtld-servers.net.
com.   		 172800    IN    NS    g.gtld-servers.net.
com.   		 172800    IN    NS    a.gtld-servers.net.
com.   		 172800    IN    NS    d.gtld-servers.net.
com.   		 172800    IN    NS    m.gtld-servers.net.
com.   		 172800    IN    NS    i.gtld-servers.net.
com.   		 172800    IN    NS    l.gtld-servers.net.
com.   		 172800    IN    NS    e.gtld-servers.net.
com.   		 172800    IN    NS    j.gtld-servers.net.
com.   		 172800    IN    NS    f.gtld-servers.net.
com.   		 172800    IN    NS    c.gtld-servers.net.
com.   		 172800    IN    NS    b.gtld-servers.net.
com.   		 86400    IN    DS    30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
com.   		 86400    IN    RRSIG    DS 8 1 86400 20190214050000 20190201040000 16749 . OTMp18F8zF6IFwUeH4PC4owXLtMIxejaBs+r2PkgIxM5dDtGIj+JXF6R kXmxsQi7FlMhQq/OxU7B3HksQ8CCXVb4rYEo+6vz8ugElRkGKBZf0tkd 4C/JjleSX5kAHdgYnK5m/0bWq4wxMw+R0sSdsbnVmc+Jzv/S3T+Aj4la 0heACCqQYY+2rrGBJ7pxTWjR2JvK8p8NgVvx6k3fZlG0p5QbnajnGMMY vyB/GtYv3uvLnS4JLQvUMU7meIq6hm+wqpI1kp676ypu+KvoqQVFaO/E u4Rbv7ie5CsQOT4H/7jc8pw6IQqqD3FjdFX2yoW4u9pSwy8LrDgYHix7 AielIA==
;; Received 1208 bytes from 199.9.14.201#53(b.root-servers.net) in 188 ms

clients-domain.com.   	 172800    IN    NS    ns2.hosting-nameserver.com.
clients-domain.com.   	 172800    IN    NS    ns1.hosting-nameserver.com.
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0Q1GIN43N1ARRC9OSM6QPQR81H5M9A NS SOA RRSIG DNSKEY NSEC3PARAM
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 8 2 86400 20190208054243 20190201043243 16883 com. LzTH+svBHgjuQk3FHAvNO72auLxibi3E6gzqFOkT7BwVqEYSLLd6b2fn r+y8A5fYEsR7VDRS6F+fODsOXlvfAR/Dr4oww/8FYMlAG7eYr5g4Bcsv TTnAqdXhPDoJgfZ6+BTodLgbY6tYZWNNnV2wS/iv0xfZ3BpAVnXEqgmD GrE=
FJ6U8VTBKL1C7L340CMMNNFNL3DP254G.com. 86400 IN NSEC3 1 1 0 - FJ70PP8IH3U3OPHIR4INIE39O39HKIVK NS DS RRSIG
FJ6U8VTBKL1C7L340CMMNNFNL3DP254G.com. 86400 IN RRSIG NSEC3 8 2 86400 20190207061242 20190131050242 16883 com. P5v6fKCuxOuzfmR2IXXZgns/m+NkvDJ2Ph4Az/Rbs+VkOV8jTHlPr/FZ k7EvoW06jHUbDLqa0UdY92IFcK/Z0kEO3t76mcQtd/0WXvVQkBHCyb0Q UfaxxPe00oeEh8Ic/6u5Zz/Co0i7rYXoVKQIprTqngs+x3g5luUogp/Y iLE=
;; Received 612 bytes from 192.12.94.30#53(e.gtld-servers.net) in 29 ms

assets.clients-domain.com.    300    IN    CNAME    distribution-id.cloudfront.net.
;; Received 92 bytes from 162.242.147.111#53(ns1.hosting-nameserver.com) in 268 ms

The majority of both answers are the same. To understand them first let's take a look at how the DNS system works.


Domain Name System (DNS) in short

There are two types of nameservers: non-authoritative or resolver cache servers and authoritative servers.

First, clients will send a request to the default nameserver, which is either provided by your ISP, or anything you set up in your router's settings. If you ever fiddled with it, you probably changed it to 8.8.8.8 or 8.8.4.4 which are Google's resolver cache servers. These will return the cached address for the requested domain if it's still valid or refer the request up the chain. This chain is what we traced with dig.

First, we are directed to the root servers. They are at the top of the DNS hierarchy and are the ones that will refer requests to the appropriate TLD servers. In our case, we have a .com domain, so we are redirected to the nameservers that manage .com TLDs. It could have an authoritative answer to the query or will have an NS record, referring to another nameserver down the chain. You can check the authoritative nameservers of any TLD, domain name or even subdomain by using dig -t ns.

$ dig -t ns com

; <<>> DiG 9.13.5 <<>> -t ns com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9967
;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;com.   			 IN    NS

;; ANSWER SECTION:
com.   		 86400    IN    NS    d.gtld-servers.net.
com.   		 86400    IN    NS    g.gtld-servers.net.
com.   		 86400    IN    NS    l.gtld-servers.net.
com.   		 86400    IN    NS    m.gtld-servers.net.
com.   		 86400    IN    NS    f.gtld-servers.net.
com.   		 86400    IN    NS    k.gtld-servers.net.
com.   		 86400    IN    NS    b.gtld-servers.net.
com.   		 86400    IN    NS    h.gtld-servers.net.
com.   		 86400    IN    NS    a.gtld-servers.net.
com.   		 86400    IN    NS    i.gtld-servers.net.
com.   		 86400    IN    NS    e.gtld-servers.net.
com.   		 86400    IN    NS    j.gtld-servers.net.
com.   		 86400    IN    NS    c.gtld-servers.net.

;; Query time: 36 msec
;; SERVER: 213.46.246.53#53(213.46.246.53)
;; WHEN: Mon Feb 04 14:10:51 CET 2019
;; MSG SIZE  rcvd: 256

As you can see, these are the same as the ones we got in the second section with +trace. These namservers contain NS records pointing to the appropriate domains may it be google.com or our clients-domain.com in question.

In both cases, we are referred to

 clients-domain.com.   172800  IN  NS  ns2.hosting-nameserver.com.
 clients-domain.com.   172800  IN  NS  ns1.hosting-nameserver.com.

These are the authoritative nameservers of the client's domain. During our investigation, we either got stuck here, or we were further referred to CloudFront.

The fact that we got two different nameservers was not surprising as usually there are at least two of them for high-availability and load balancing. But now we started to understand where the problem is coming from. You see, browser have their own DNS cache, to make requests to frequently used domains faster, but curl does not, and having one would, of course, ruin the purpose in case of dig. Thus we guessed that browsers cached the server for the requested domain and that's why we got reliable responses after the first time it worked, but we got 50-50 error rate with from the terminal.

So we thought that maybe the CNAME record is only present on one of the authoritative nameservers of the client's hosting service provider. To test that we used nslookup specifying the nameserver we wanted to use.


Coup de grace with 'nslookup'

With nslookup you can query nameservers. To figure out what's happening we specified the domain name to be resolved with the nameservers of the hosting service, one by one.

$ nslookup assets.clients-domain.com ns1.hosting-nameserver.com
Server:   	 ns1.hosting-nameserver.com
Address:    162.242.147.111#53

assets.clients-domain.com    canonical name = distribution-id.cloudfront.net.
** server can't find distribution-id.cloudfront.net: REFUSED

Specifying ns1.hosting-nameserver.com we got back the CNAME (canonical name) record pointing to CloudFront. Of course, it refused the resolution of distribution-id.cluodfornt.net as it is not an authoritative nameserver of CloudFront, but at least we saw that this nameserver has the proper record.

$ nslookup assets.clients-domain.com ns2.hosting-nameserver.com
Server:   	 ns2.hosting-nameserver.com
Address:    50.56.75.143#53

** server can't find assets.clients-domain.com: NXDOMAIN

Then when we queried ns2.hosting-nameserver.com we got NXDOMAIN just as when the domain name resolution broke with dig or when we weren't able to load the images from the browsers. Finally, these results were consistent across different locations no matter how many times we ran nslookup.

Thus, we were able to conclude that the problem stemmed from the fact that one of the nameservers was missing the CNAME record pointing to CloudFront. We let the client know to handle this with their hosting service, and a day later the issue got resolved.


Conclusion

In this blogpost we described how we saw symptoms of assets loading inconsistently from AWS S3 with CloudFront, how we used curl to get a more simple answer than what was provided by the browsers. We went on to investigate the issue with dig +trace only to find out that the DNS resolution was stuck at the authoritative nameserver of the domain. Then we finally put an end to the investigation by using nslookup querying the two authoritative nameservers one by one.


Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Service oriented architecture and microservices have fundamentally changed the way we design and develop our applications in the past years. It’s up to us to adapt to this change and enrich our developer toolkit with one new addition: GraphQL.

This is an introductory talk to GraphQL I delivered for the Node.js Meetup community of Budapest in February 26, 2019. In this presentation I shared some of my experiences using GraphQL. Viewers are going to get familiar with GraphQL and related technologies and when (and when not) to apply them.

Presentation:

REST in Peace. Long Live GraphQL! - Part 1 by Peter Czibik - YouTube

The presentation is separated into four ~10 min-long clips. You can find subsequent parts of this talk in the youtube playlist.

If you'd like to join the Node.js Budapest meetup - do it here!.
If you're a JavaScript developer looking for new opportunities in Budapest, RisingStack is Hiring!

In case you need guidance with Node.js, GraphQL or related technologies, RisingStack is here to help you. Feel free to ping us on Twitter or mail us at info@risingstack.com


Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Separate tags by commas
To access this feature, please upgrade your account.
Start your free month
Free Preview