A Rubyist explores Typescript, Node, and ESM

I recently had a small housekeeping task I needed to do which simply involved hitting JIRA’s REST API and creating some issues based on a template. Ruby tends to be my dynamically typed language of choice, with C# historically being my favorite statically typed language. This time though, I figured I’d give Typescript a shot. I had several goals in mind.

Understand the latest in Javascript modules

Since I last played with Javascript, there has been some progress in this area. ESM (ECMAScript Modules) exists and is now supported by most browsers. An NPM package contains one or more modules and a module exists to encapsulate code and avoid namespace pollution. This Redfin Engineering piece seems to do a better job than I could at explaining this. It’s also cool that, with the advent of HTTP 2, we can skip bundling entirely. That said, I noticed a few annoyances that are confusing as an outsider:

  • Too many ways/concepts to ‘export’ stuff - Default exports, named exports for example

  • Still caught halfway in between Common JS modules and ESM - A lot of packages out there are still CJS.

  • Some packages, like the openapi-typescript-fetch package I needed, are published with both, but the package was not published with the typical cjs vs. mjs file convention, so it did not behave how Node (I used 17.6.0) expected it to. The various package.json changes (type: “module”, etc.) also seemed confusing. In this case, when I tried to use 3.x.x of node-fetch with my package using type: “module” along with some other TS settings, I kept getting errors that “The requested module ‘openapi-typescript-fetch’ is a CommonJS module”, which is not technically true, but this was difficult to get around given I do not control that package.

Generated OAS/Swagger Typescript

The interfaces created by openapi-typescript seemed to accurately capture all of the details in Atlassian’s JIRA OAS spec. The fetch package above did prove somewhat useful and it was very easy to hand our generated code off to that fetcher. This is not too far off from the generated SOAP client code (AXIS, C# svcutil) or RAML Java generators.

The only real downside here was that the fetcher depends on the browser Fetch API that does not exist natively in NodeJS. While this is understandable (and solved by using the node-fetch package), it can still be awkward when crossing the browser/NodeJS divide.

index.ts
1    import './fetch-polyfill'
2    import  from "./jira"
3    import  from "openapi-typescript-fetch"
4    import * as getBasicAuthHeader from 'basic-authorization-header'
5    
6    const fetcher = Fetcher.for<paths>()
7    const env = process.env;
8    fetcher.configure({
9        baseUrl: "https://stuff.atlassian.net",
10       init: {
11           headers: {
12               Authorization: getBasicAuthHeader(env.JIRA_USERNAME,
13                   env.JIRA_API_KEY)
14           }
15       }
16   });
17   
18   (async () => {
19       const findProjects = fetcher.path('/rest/api/3/project')
20           .method('get')
21           .create()
22       let contentProjectId = (await findProjects({})).data.find(proj => {
23           return proj.key == 'BTC'
24       }).id
25   })();
26   

IDE support

I tend to use Jetbrains tooling so I decided to check out Webstorm. What was good:

  • Syntax completion with Typescript interfaces - This was helpful

  • Differentiation of generated code (JS) from source code (TS)

  • IDE respects same tsconfig.json that tsc compiler uses for compilation

What could use improvement:

  • Building and running was more confusing. The IDE did not seem to add much value over just shelling with npm run. This is not that big of a deal.

  • The ‘External libraries’ feature is a bit awkward since it does not show introspected package.json dependencies. This is also not a big deal since node_modules mostly accomplishes that. In other languages where the derived set of packages does not live in a git ignored directory inside the project, ‘External libraries’ is more important than it is here.

Visual Studio Code is obviously a popular, if not the choice for most folks instead of Webstorm. I’ve just had too much comfort in the Jetbrains world to try it out.

Summary

All in all, the experience was OK. The module experience and errors that went with it were quite frustrating. Once that was dealt with, the Typescript, IDE, and generated Typescript code from OAS worked pretty well.

Brady Wied