Skip to main content
Bazel handles JavaScript code, dependencies, and build configuration automatically. Before diving into the detailed part of the course, you’ll build your first Hello world app.

What you’ll learn

  • Setting up a development environment using a starter repository
  • Generating dependency and executable targets with Gazelle
  • Understanding how npm_link_all_packages and js_binary work together
  • Building and running your first JavaScript application with Bazel

Get started

The following steps will guide you through setting up your development environment and running your first Bazel app.
1

Setup a development environment

Navigate to the aspect starter repository, to access a ready to use developer environment already set up for you.starter
The dev container doesn’t have Node.js tooling installed globally. Bazel provides Node.js, pnpm, buildozer, ibazel and the other tools, and bazel_env.bzl puts them on your PATH through direnv. The Codespace runs direnv allow and bazel run //tools:bazel_env for you when it starts. On your own machine, run those two commands once after cloning.
2

Create a project

Create a new project using one of these methods. Option A allows you to create a permanent project, while option B gives you a playground to experiment with.Option A: Create a new repository
  1. Click the Use this template green button at the top right corner of the starter repository
  2. Select Create a new repository from the dropdown menu
  3. Pick a name for your new repository
  4. Click Create repository - you’ll be redirected to your new repository with the Bazel template
  5. Press , (comma key) to open the project in a GitHub Codespace
GitHub Codespaces comes with Bazel preinstalled via Bazelisk, so no manual Bazel installation is required.
starterOption B: Test on a remote playground
  1. Click the Use this template green button at the top right corner of the starter repository
  2. Select Open in a codespace to open the project directly
Once you’re in the codespace, wait a few minutes for the machine to boot up. Ignore any errors in the terminal - they will be resolved once all setup scripts complete.
By default, codespaces launch with a smaller machine. For a faster, more responsive development experience, create a codespace with a larger machine.
3

Install VsCode extensions

Install the VS Code extension when prompted. This primarily gives you access to syntax highlighting in Bazel configurations.extensions
4

Install packages & dependencies

  1. Create the folders packages/hello in your project root folder. The starter ships its own sample under hello/, so packages/ does not exist yet.
  1. Navigate to the hello folder
  1. Initialize the package
  1. Set the package to use the ESM module syntax (“import” rather than “require”)
  1. Install chalk so it’s ready to use by running
The pnpm-workspace.yaml file at the repository root lists packages/*, so every folder created under packages is treated as a local monorepo package.
5

Create the app

  1. Create an index.js file in the hello folder.
  2. Paste this code in your index.js file.
  1. Add a shortcut to your package.json file, to test the program with plain Node.js first.
  1. Run the app to test that the program works with Node.js alone.
You should see Hello World! printed out in the terminal in green.
Now proceed to running the app with Bazel.
6

Generate the Build file

Run the command below from the Bazel module root to generate the BUILD file using Gazelle. Bazel needs this declarative file to understand the application’s inputs and dependencies.
Gazelle creates packages/hello/BUILD.bazel. The starter’s Orion extension recognizes the hello script you added earlier and generates the js_binary alongside the dependency links. The completed file should be:
npm_translate_lock in MODULE.bazel has already converted the pnpm lockfile into targets in the external @npm repository. npm_link_all_packages selects the packages needed here and exposes them through the package-local node_modules target.js_binary then creates a hermetic launcher for index.js. Its data attribute defines the runtime files: the package metadata and linked npm dependencies. Undeclared files are not silently available, which keeps execution reproducible and cacheable.
The hello script must have the simple form node <file> for the starter’s Orion extension to infer a binary. Other script forms are skipped; for those, declare the appropriate rules_js target directly.
This works for simple flat package structures. For projects using tsconfig outDir or rootDir to manipulate output paths, you’ll most likely want to add # gazelle:generation_mode update_only to a parent BUILD file and create BUILD files manually at the package level. The next guide covers this.
7

Run the program with Bazel

  1. Run this command to verify that the app runs with Bazel. You should see Hello World printed on the terminal. The label //packages/hello is short for //packages/hello:hello, the js_binary target Gazelle generated.
  1. Return to the package, then add a shortcut to its package.json so you can run the program without typing the full label.
  1. Run the program
Bazel analyzes the target, builds it, and runs the result:
That’s it, you’ve successfully built and run a basic JavaScript app with Bazel.

What’s next

The next guide, TypeScript and Web, introduces a browser-based app, TypeScript, and monorepo package dependencies.