Javascript code

Javascript code.

Introduction

I’m about to start a new project in January 2023. I have a couple of weeks between projects so I thought that I will make a demonstration / learning application for myself regarding the technologies the project will use:

This blog post describes some experiences regarding the backend development. I will later on write another blog posts regarding the frontend development and deployment.

The demo app is in my Github repo: js-node-ts-react.

Webstore Demo App

I use the same Webstore application that I have used before, see Five Languages - Five Stories (well, there was also an implementation using Kotlin but I never bothered to update the blog post). I actually implemented the backend also using Javascript at that time, but now, I wanted to re-implement the demo-app using the Serverless local development environment and latest dependencies. I now also have more functional programming experience working with Clojure, so I wanted to see if this experience has had some influence in my Javascript programming.

Starter templates

There are several good templates for this kind of full-stack app. For the backend development I used:

Choosing bits and pieces from various starter templates you can quickly create a good skeleton backend app for your needs.

Backend Endpoints

The backend is quite simple. There are three endpoints (see: serverless.yml):

      - httpApi: 'GET /product-groups'
      - httpApi:
          path: /products/{pgId}
          method: get
      - httpApi:
          path: /product/{pgId}/{pId}
          method: get

So, we simulate a webstore: you can query product groups, get a listing of products belonging to a product group, and query a specific product.

Backend Development

Here I list some lessons learned for myself when I implemented the backend. Nothing new or special here - all these things are bread and butter for any Javascript developer. But I wanted to write down these lessons for myself so that I remember them better in my future Javascript projects.

Nodemon

You can keep your serverless backend up and running with nodemon:

"dev": "nodemon --exec serverless offline start",

VSCode Debugger

You can use VSCode debugger with this launch.json configuration:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": [
        "<node_internals>/**"
      ],
      "program": "${workspaceFolder}/backend/src/domaindb/domain.mjs"
    }
  ]
}

At the end of your Javascript file under debugging add code to call the function you want to debug, example:

// For debugging using the node Run and Debug REPL.
const debugRet = await getProductGroups();
logger.debug('debugRet: ', debugRet);

Then in VSCode:

Use Run and Debug (VSCode: left panel). Then Run Javascript debug terminal (VSCode upper left corner). Set a breakpoint in domain.jms file. Open terminal, cd backend and run e.g. node src/domaindb/domain.mjs => debugger stops in the breakpoint.

This way you can debug an individual file using the VSCode debugger.

Experimenting with Quokka

You can use Quokka VSCode extension for various experimentations.

ESLint

Install ESLint and configure it - you get various useful warnings in VSCode while you are programming.

Initialize the ESLint configuration by running:

pnpm create @eslint/config

I used the airbnb style guide (recommended by the one and only source of truth: Koodiklinikka-slack).

Import vs Require Hassle

There were quite a lot of hassle and linter warnings regarding whether to use import or require. Once I fixed the linter warnings, the Serverless local development broke. Finally, I managed to find the right import setup to satisfy both the ESLint and Serverless local development.

Then I tried the VSCode Debugger and the imports broke once again. Example:

import pkg from 'fs';
const { promises: fs } = pkg;
// NOTE: This is not working when using Node Run and Debug REPL.
// const fs = require('fs').promises;

I’m not a Javascript guru so I won’t dive any deeper into this mess.

Asynchronous Programming

Since the Javascript engine runs with just one thread the engine uses an event loop. Therefore most libraries use Promises. You can get a bit “synchronous” nature to this programming model using async/await model. Example:

...
// Internal function to load the products to the domain db.
async function loadProducts(pgId) {
  logger.debug(`ENTER domain.loadProducts, pgId: ${pgId}`);
  const productsKey = `pg-${pgId}-products`;
  if ((domain[productsKey] === null) || (domain[productsKey] === undefined)) {
    const productsCsvFile = `resources/pg-${pgId}-products.csv`;
    const csvContents = await fs.readFile(productsCsvFile, 'utf8'); // ==> HERE !!!!
...

fs.readFile is asynchronous. But we need the data here, so we await. Therefore we need to mark the function as async. Any caller of this function needs to remember this:

async function getProducts(pgId) {
  logger.debug(`ENTER domain.getProducts, pgId: ${pgId}`);
  const productsKey = `pg-${pgId}-products`;
  let products = domain[productsKey];
  if ((products === null) || (products === undefined)) {
    await loadProducts(pgId);   // ==========> HERE !!!!!!!!!!!!!!!!
    products = domain[productsKey];
  }
  logger.debug('EXIT domain.getProducts');
  return products;
}

Functional Programming

Functional programming is quite nice with Javascript. If you have been programming e.g. Clojure you have no issues using map/reduce/filter with Javascript. Example:

    const ret = rows.reduce((acc, row) => {
      if (row.length === 2) {
        const [key, val] = row;
        acc[key] = val;
      }
      return acc;
    }, {});

Serverless Local Development

This demo app uses instructions provided in the Serverless local development blog post and the Serverless Offline plugin.

Using the Serverless local development you can run your server in your local development simulating the actual serverless production environment (in our case, AWS Lambda).

Javascript Programming Experience

Javascript is a quite nice functional programming language. I especially like the functional features and literal data structures. The asynchronous nature of Javascript (just one thread) is a bit eccentric. What I miss is a good REPL-driven development DX as you have with Clojure - not possible since Javascript is not a Lisp (not a homoiconic language) - a real REPL is not just possible with Javascript. You need to read more about Lisp REPLs - even other programming languages call their interactive tools as REPLs, they are not real REPLs in the sense we have a REPL in a Lisp language.

Thanks

I got wonderful help from specialists who are active in the Finnish Koodiklinikka Slack, in channels #javascript, #typescript and #react. I have mentioned some names in the project README with their permission. If you are a Finnish developer, I really recommend using the excellent Koodiklinikka Slack if you want to learn new programming languages or technologies - there are a lot of competent experts in the Koodiklinikka Slack willing to help each other.

Conclusions

Javascript is a quite nice functional programming language. With Node and a plethora of Node libraries you can create what ever backend you need for your project. One word of criticism, though. Javascript is nowhere near Clojure when we are talking about the Developer Experience.

The writer is working at a major international IT corporation building cloud infrastructures and implementing applications on top of those infrastructures.

Kari Marttila