Neutrino Docs
Concepts

Tech Stack

The key technologies you encounter when building on Neutrino — IDs, validation, state management, and more.

Tech Stack

This page covers the stack pieces you encounter as a developer building on or with Neutrino. Internal infrastructure choices (Drizzle ORM, TanStack Router, Module Federation) are mentioned briefly with links to the full internal reference.


NanoID — Platform and resource identifiers

Every platform, stack, tenant, and resource in Neutrino has a 10-character NanoID as its canonical identifier. The alphabet is [a-z0-9] (lowercase letters and digits only — no uppercase, no hyphens, no special characters).

// Example IDs you will see everywhere
const platformId = "k3m9p2xw7q"; // platform
const stackId = "x7y8z9w0q1"; // stack
const tenantId = "m3n4p5q6r7"; // tenant

IDs appear in:

  • DNS hostnames: auth.svc.default.k3m9p2xw7q.nno.app
  • Cloudflare resource names: k3m9p2xw7q-default-auth-db
  • API paths: /api/nno/platforms/k3m9p2xw7q/...

Always use generateId() from @neutrino-io/core/naming — never generate IDs by hand.

import { generateId, buildResourceName } from "@neutrino-io/core/naming";

const platformId = generateId();
// → 'k3m9p2xw7q'

buildResourceName(platformId, "default", "auth");
// → 'k3m9p2xw7q-default-auth'

NanoID was chosen over UUID v4 because 10-character IDs are compact enough to embed in DNS labels, URL paths, and Cloudflare resource names without exceeding length limits — while still providing sufficient collision resistance for the expected platform count.


Zod — Request and response validation

Neutrino uses Zod for all request and response validation across backend services. Zod schemas are the single source of truth: they define the TypeScript types and enforce runtime correctness in the same declaration.

Where you encounter Zod

When you call Neutrino APIs, the Gateway validates your request body against a Zod schema before the handler runs. Invalid requests receive a structured error response:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "requestId": "req_01j..."
  }
}

In feature SDK hooks

Feature hooks that accept user-supplied data validate inputs with Zod before passing them to the API:

import { z } from "zod";

const CreateTenantSchema = z.object({
  name: z.string().min(2).max(64),
  slug: z
    .string()
    .regex(/^[a-z0-9-]+$/)
    .max(32),
});

// In your feature — this is validated before the API call is made
const result = await createTenant(CreateTenantSchema.parse(formData));

Error codes

The NnoErrorEnvelopeSchema and ErrorCode union are exported from @neutrino-io/core/errors and used across all services to ensure consistent error shapes.


TanStack Query — Server state in console and features

The Neutrino console shell and all feature packages use TanStack Query (@tanstack/react-query) for server state management — fetching, caching, and synchronising data from backend APIs.

Why TanStack Query

  • Features bring their own query keys and cache logic without central coordination
  • Stale-while-revalidate behaviour works out of the box
  • Natural pairing with the feature SDK: each feature manages its own data without knowing about other features

Typical pattern in a feature hook

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { useShell } from "@neutrino-io/sdk/feature";

export function useTenants() {
  const { platform } = useShell();

  return useQuery({
    queryKey: ["tenants", platform.id],
    queryFn: () => fetchTenants(platform.id),
    staleTime: 30_000,
  });
}

export function useCreateTenant() {
  const queryClient = useQueryClient();
  const { platform } = useShell();

  return useMutation({
    mutationFn: (data: CreateTenantInput) => createTenant(platform.id, data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["tenants", platform.id] });
    },
  });
}

Query key convention: prefix your query keys with your feature ID (e.g. ['analytics', ...]) to avoid collisions with other features sharing the same shell query cache.


Internal stack — brief reference

These choices are internal to how Neutrino is built. You interact with their effects (typed routes, generated API clients, module-level isolation) but do not configure them directly.

TechnologyRoleWhere it lives
Drizzle ORMType-safe DB queriesservices/iam, services/registry only
TanStack RouterType-safe routing in the console shellapps/console
Module Federation (Phase 2)Hot-swappable remote feature loadingNot yet implemented
Hono.jsBackend service framework on CF WorkersAll services/*
Better AuthPer-platform auth Workersservices/auth template
ViteConsole shell build tooling + feature auto-discovery pluginapps/console

For the full rationale behind each choice see ADR-009.

On this page