#
Essentials
Outdated
The tech stack is constructed upon a set of fundamental concepts where possessing their basic understanding is essential for a seamless comprehension of the content presented in the Development section.
#
Terms
Before going deeper, it is essential to review the understanding of terminology used throughout the Development section.
#
Application
There are three types of entities classified as applications: orchestrator, service, and package. The latter is not an application in the traditional sense; however, it is included in this category for the sake of clarity.
#
Orchestrator
Typically, only one orchestrator exists per project, as it serves as the sole provider of a public API and is the only application capable of communicating with the services.
All security measures must be enforced at this level, as no additional checks are performed on the services.
Info
Orchestator is built using the Nitro framework and hosted as a Cloudflare Worker.
#
Service
One of many other services per project. Each designed to cater to a specific area based on the Single Responsibility Principle, ensuring all services operate independently, without knowledge of one another, and do not share databases, buckets, or any other resources.
Communication with the outside world occurs exclusively through the orchestrator and strictly via RPC, utilizing the WorkerEntrypoint. Each request is treated as if it has been approved by the orchestrator, allowing the service to perform the action with elevated permissions.
Info
Service is built using TypeScript and hosted as a Cloudflare Worker.
#
Package
Public or private, centralized location for types and utilities that are appropriate to share across all applications.
#
Structure
All applications are housed within a single monorepository that leverages Bun workspaces. This consolidation ensures development consistency, facilitates the sharing of types, enables unified dependency management, and streamlines the refactoring process.
#
Technologies
Reinventing the wheel is discouraged, and instead, the use of widely-used and recognized libraries that solve common problems is encouraged. The list of these backbone technologies can be found below, along with a summary of the motivation behind their use and an explanation of the configuration:
#
Cloudflare Developer Platform
Cloudflare serves as the primary provider of the infrastructure, offering a highly reliable, affordable, scalable, and DX-friendly platform for building virtually anything. Although it may currently have limited support for certain concepts that are typically found in more mature ecosystems, the Develit Backend SDK helps to fill these gaps.
Info
Utilities provided by Develit Backend SDK are marked with ⚙️ emoji throughout the documentation.
The following Cloudflare services are used across the tech stack:
Info
Since Workers leverage the edge network, they do not run on traditional runtimes such as Node, Bun, or Deno. Instead, Cloudflare has developed a Workers-specific runtime, called workerd.
#
TypeScript
Type safety is a crucial concept of the tech stack, and statically checking code before merging it is an essential step in writing maintainable code. The configuration of TypeScript, residing in tsconfig.json, prioritizes strictness and compatibility with modern JavaScript.
#
erasableSyntaxOnly
Erasable syntax refers to the ability to remove certain syntax without affecting the runtime behavior of the code. Enabling this option is crucial for writing forward-compatible code that will remain maintainable in the future, even if non-erasable syntaxes or TypeScript itself are no longer used.
For instance, several proposals are currently underway to add type annotations to JavaScript, including the popular types as comments proposal, which would enable JavaScript to treat types in code as ignorable at runtime.
#
esModuleInterop
Historically, JavaScript has had two primary options for exporting and importing: CommonJS and ESM. Modern JavaScript tends to shift towards using the ESM approach. Enabling this option allows for the use of default imports from modules that still export using the CommonJS syntax.
#
experimentalDecorators
Introspection utilities like Function.caller or arguments.callee are not a part of the modern JavaScript, but the demand for such functionality still exists. This is why the decorators propsal is nearing implementation. Enabling this option allows for the use of features from the Backend SDK, that utilize decorators, that enable more concise code to be written.
#
isolatedModules
JavaScript files without any imports or exports are considered legacy. Enabling this option ensures that each file can be safely transpiled without relying on other imports.
#
module
The module system in JavaScript has a different versioning strategy than the ECMAScript specification. Enabling this option with the ESNext value ensures that the newest features are immediately available as soon as they are available in the runtime.
#
moduleResolution
The use of Bundler value is not a strongly held preference, but rather a default setting that is applied when creating a new Cloudflare Worker project.
#
noEmit
By default, the TypeScript compiler is designed to transpile TypeScript code into JavaScript code. Enabling this option causes the compiler available solely for type checking purposes, preventing it from attempting to compile the code and thereby avoiding the generation of unnecessary files.
#
skipLibCheck
Libraries may define copies of the same type in an inconsistent manner. Enabling this option causes the compiler to ignore all declaration files that are not explicitly referenced.
#
strict
The compiler is inherently lenient, as its primary purpose is to compile TypeScript code into JavaScript code. For TypeScript-first codebases, it is beneficial to establish specific TypeScript rules that must be adhered to from the outset. Enabling this option enforces a multitude of rules, resulting in the production of standardized, type-safe code.
#
target
The features available when programming in JavaScript are defined by the ECMAScript specification. Enabling this option with the ESNext value ensures that the newest features are immediately available as soon as they are available in the runtime.
#
Vite
To deploy the code on the Cloudflare infrastructure, it must be bundled. Cloudflare provides the @cloudflare/vite-plugin Vite plugin, which brings the workerd environment to localhost, ensuring that the code behaves consistently both locally and in production.
In addition to that, it provides a delightful DX by leveraging Vite's hot module replacement and supporting vite preview, enabling testing of the build output in the Workers runtime prior to deployment.
#
Nitro
The Nitro team provides the nitro-cloudflare-dev Nitro module, which facilitates access to Workers runtime bindings within the development server. In the context of the orchestrator, bindings primarily represent the service workers.
Another widely recognized concept that Nitro lacks is the use of guards. Develit addresses this by offering the @develit-io/nitro-guards Nitro module, which effectively integrates this feature into Nitro.
Info
Utilities provided by Develit @develit-io/nitro-guards Nitro module are marked with 🔒 emoji throughout the documentation.
#
Drizzle ORM
Accessing the database through the same language as the one used in the codebase brings the benefit of type safety when manipulating with tables or its records.
Drizzle ORM provides native D1 integration.
#
Zod
Validating input and output offers benefits akin to static type checking, as early error detection enhances both DX and UX.
Nitro utilizes h3, which features built-in utilities for validating REST inputs. These utilities can be effectively used in conjunction with Zod, allowing it to handle all validation tasks.
#
Vitest
Quality ensurance is yet another important factor of our tech stack thus writing tests is an important part of the development. Cloudflare maintains @cloudflare/vitest-pool-workers Vitest plugin that enables to run tests in the context of workerd, ensuring the code will behave as close to the production as possible.
#
Bun
As the tech stack is not dependent on Node, it does not make sense to utilize it just to be able to install packages.
Bun offers monorepository support, Dependabot compatibility and ultra-fast package managing.