t4-app Documentation

repository·master·Indexed 23 days ago

https://github.com/timothymiller/t4-app

A full-stack, typesafe, universal application scaffold targeting Next.js (Web), Expo (Native), and optionally Tauri (Desktop). The T4 Stack integrates Solito for unified routing, Tamagui for styling, tRPC and TanStack Query for data fetching, Hono on Cloudflare Workers for the backend, and Cloudflare D1 with Drizzle ORM for the database.

Tokens
10.4K
Snippets
20
Records
65
Agent score
82%

What's inside t4-app

  1. Overview of the T4 Stack technologies

    master

    The T4 Stack is a universal web and native stack designed for high performance and rapid development. It integrates several key technologies:

    • Frontend: Next.js (Web) and Expo (Native) using Solito for unified routing.
    • UI Kit: Tamagui for responsive, cross-platform styling.
    • Data Fetching: tRPC for end-to-end typesafe APIs, combined with TanStack Query for caching and state management.
    • State Management: Jotai for global state.
    • Backend: Hono running on Cloudflare Workers.
    • Database: Cloudflare D1 (SQLite) managed via Drizzle ORM.
    • Type Validation: Valibot (with support for generating Valibot Type Guards from Drizzle schemas).
    • Performance: Million.js (Virtual DOM replacement) and PattyCake (zero-runtime pattern matching).
    • Authentication: Supabase Auth.
  2. Supported Platforms

    master

    T4 allows you to build a single codebase that targets multiple platforms:

    • Mobile: iOS and Android (via Expo).
    • Web: Progressive Web App (PWA) support via Next.js.
    • Desktop: macOS, Windows, and Linux via PWA or experimental Tauri support.
  3. Install and scaffold a T4 App

    master

    To start a new full-stack, typesafe, universal Expo & Next.js application, use the create-t4-app CLI.

    Prerequisites

    • bun v1.0 or higher is required.

    Standard Setup Run the following command to scaffold a standard web and native app:

    bun create t4-app

    Tauri Setup If you want to include experimental desktop support via Tauri, use the --tauri flag:

    bun create t4-app --tauri
  4. Authenticate users via JWT in the API context

    master

    Authentication is performed by inspecting the authorization header of the incoming request. The createContext function expects a Bearer token (e.g., Authorization: Bearer <token>).

    Authentication Logic:

    1. Extracts the token from the authorization header.
    2. Verifies the token using the HS256 algorithm and the provided JWT_VERIFICATION_KEY.
    3. Checks the exp (expiration) claim against the current timestamp.
    4. Extracts the sub (subject) claim from the decoded payload to identify the userId.

    If verification fails, the token is expired, or the header is missing, the user property in the context will be null.

  5. Understand the difference between ThemeVariant and CurrentThemeVariant

    master

    The application distinguishes between the intended theme setting and the active rendered theme:

    1. ThemeVariant: Represents the user's selection or preference. It includes 'system', which allows the UI to react to operating system settings.
    2. CurrentThemeVariant: Represents the actual theme currently applied to the UI. This is a subset of ThemeVariant and only includes 'light' or 'dark', as the 'system' preference must resolve to one of these two concrete states.
  6. Configure Tamagui using the @t4/ui config

    master

    The T4 stack provides a pre-configured Tamagui configuration via @t4/ui. To use it in a Next.js or web project, import config from @t4/ui and export it as the default export of your tamagui.config.ts file.

    To ensure full TypeScript support and autocompletion for your Tamagui components (such as theme colors, spacing, and sizes), you must augment the tamagui module by extending the TamaguiCustomConfig interface with the type of the imported @t4/ui config.

    import { config } from '@t4/ui'
    
    type Conf = typeof config
    
    declare module 'tamagui' {
      interface TamaguiCustomConfig extends Conf {}
    }
    
    export default config
  7. Configure Tamagui with the @t4/ui config

    master

    To ensure full type safety when using Tamagui components with the T4 stack, you must extend the TamaguiCustomConfig interface with the @t4/ui configuration. This allows TypeScript to recognize the custom themes, tokens, and component properties defined in the @t4/ui library within your Tamagui setup.

    In your tamagui.config.ts file, import the config from @t4/ui and use a module declaration to merge it into the tamagui module.

    import { config } from '@t4/ui'
    
    export type Conf = typeof config
    
    declare module 'tamagui' {
      interface TamaguiCustomConfig extends Conf {}
    }
    
    export default config
  8. Configure the Tamagui configuration object

    master

    The config object is created using createTamagui and defines the core design system for the application, including fonts, themes, tokens, animations, and media queries. This configuration is used to drive the styling engine across the UI kit.

    Key configuration properties include:

    • defaultFont: Sets the fallback font family (e.g., 'body').
    • animations: Provides the animation definitions.
    • shouldAddPrefersColorThemes: Enables support for system color preference media queries.
    • themeClassNameOnRoot: Applies theme class names to the root element.
    • shorthands: Enables shorthand property names for styles.
    • fonts: A mapping of font names to createInterFont instances.
    • themes: The set of available color themes.
    • tokens: The design tokens (colors, spacing, etc.).
    • media: A set of media query breakpoints defined via createMedia.
    export const config = createTamagui({
      defaultFont: 'body',
      animations,
      shouldAddPrefersColorThemes: true,
      themeClassNameOnRoot: true,
      shorthands,
      fonts: {
        body: bodyFont,
        heading: headingFont,
      },
      themes,
      tokens,
      media: createMedia({
        // ... media queries
      }),
    })
  9. Configure Metro for the Expo monorepo

    master

    The metro.config.js in the apps/expo directory is configured to support a monorepo structure. It enables CSS support for web builds, extends the resolver to include .cjs files, and configures Metro to watch the entire workspace root. It also ensures that dependencies are resolved correctly by looking into both the local projectRoot and the workspaceRoot node modules.

    const { getDefaultConfig } = require('@expo/metro-config')
    const path = require('path')
    
    const projectRoot = __dirname
    const workspaceRoot = path.resolve(__dirname, '../..')
    
    const config = getDefaultConfig(projectRoot, {
      isCSSEnabled: true,
    })
    
    config.resolver.sourceExts.push('cjs')
    
    config.watchFolders = [workspaceRoot]
    config.resolver.nodeModulesPaths = [
      path.resolve(projectRoot, 'node_modules'),
      path.resolve(workspaceRoot, 'node_modules'),
    ]
    config.resolver.disableHierarchicalLookup = false
    
    config.transformer = { ...config.transformer, unstable_allowRequireContext: true }
    config.transformer.minifierPath = require.resolve('metro-minify-terser')
    
    module.exports = config
  10. Configure Drizzle ORM for Cloudflare D1

    master

    The drizzle.config.ts file defines the configuration for Drizzle Kit when working with a Cloudflare D1 database. It specifies the schema location, migration output directory, and the necessary credentials to connect via Wrangler.

    Key configuration keys:

    • schema: Path to the TypeScript file defining the database schema.
    • out: Directory where migration files will be generated.
    • driver: Set to 'd1' for Cloudflare D1 support.
    • dbCredentials: Object containing connection details:
      • wranglerConfigPath: Path to your wrangler.toml file.
      • dbName: The name of the D1 database.
    • verbose: Boolean to enable/disable detailed logging.
    • strict: Boolean to enable strict mode for schema validation.
    import type { Config } from 'drizzle-kit'
    
    export default {
      schema: './src/db/schema.ts',
      out: './migrations',
      driver: 'd1',
      dbCredentials: {
        wranglerConfigPath: 'wrangler.toml',
        dbName: 'production',
      },
      verbose: false,
      strict: true,
    } satisfies Config
  11. Configure the Expo app configuration

    master

    The app.config.ts file defines the configuration for the Expo mobile application. It uses environment variables to manage EAS (Expo Application Services) settings and project ownership.

    Key configuration settings include:

    • extra.eas.projectId: Set via the EXPO_PUBLIC_EAS_PROJECT_ID environment variable.
    • owner: Set via the EXPO_PUBLIC_EAS_OWNER environment variable.
    • plugins: Includes expo-router for file-based routing.
    • experiments: Enables tsconfigPaths and typedRoutes for improved TypeScript integration.
    • platforms: Configured for ios and android.
    • updates.url: Points to the specific Expo update URL for this project.
    • runtimeVersion: Uses the sdkVersion policy.
    import { ConfigContext, ExpoConfig } from '@expo/config'
    
    export default ({ config }: ConfigContext): ExpoConfig => ({
      ...config,
      extra: {
        eas: {
          projectId: process.env.EXPO_PUBLIC_EAS_PROJECT_ID,
        },
      },
      owner: process.env.EXPO_PUBLIC_EAS_OWNER,
      plugins: ['expo-router'],
      experiments: {
        tsconfigPaths: true,
        typedRoutes: true,
      },
      platforms: ['ios', 'android'],
      name: 'T4 App',
      slug: 't4-app',
      updates: {
        url: 'https://u.expo.dev/85fc6ccd-0ce1-4e4d-804c-b15df989f97e',
      },
      runtimeVersion: {
        policy: 'sdkVersion',
      },
    })