# Type-Safe API with Hono

This document explains how to use the improved type system in the API, leveraging Hono's best practices for type safety.

## Overview

The API now uses a centralized type system based on [Hono's RPC pattern](https://hono.dev/docs/guides/rpc) for maximum type safety across the entire application.

## Key Files

- **`app.types.ts`** - Centralized environment type definitions
- **`client.ts`** - Exported types for RPC client usage
- **`routers.ts`** - Main router with type exports

## Environment Types

### BaseEnv

The most basic environment with Supabase client access:

```typescript
type BaseEnv = {
  Variables: {
    supabase: SupabaseClient;
  };
};
```

### AuthEnv

Environment with authenticated user:

```typescript
type AuthEnv = BaseEnv & {
  Variables: BaseEnv["Variables"] & {
    user: User;
  };
};
```

### MaybeAuthEnv

Environment where user may or may not be authenticated:

```typescript
type MaybeAuthEnv = BaseEnv & {
  Variables: BaseEnv["Variables"] & {
    user: User | null;
  };
};
```

### AppEnv

Full environment with all services:

```typescript
type AppEnv = AuthEnv & {
  Variables: AuthEnv["Variables"] & {
    streamServerClient: StreamChat;
    s3_client: S3Client;
    transporter: Transporter;
    stripe: Stripe;
    stripeSync: StripeSync;
  };
};
```

## Using Types in Routers

### Example Router

```typescript
import { Hono } from "hono";
import { createFactory } from "hono/factory";
import type { AuthEnv } from "./app.types.js";

// Create a typed factory
const factory = createFactory<AuthEnv>();

// Create handlers with full type inference
const getUser = factory.createHandlers(async (c) => {
  const user = c.get("user"); // Fully typed as User
  const supabase = c.get("supabase"); // Fully typed as SupabaseClient

  return c.json({ user });
});

// Export typed router
export const getUserRouter = () => {
  const router = new Hono<AuthEnv>();
  router.use(middlewareManager.auth);
  router.get("/me", ...getUser);
  return router;
};
```

## RPC Client Usage

### In Tests

```typescript
import { testClient } from "hono/testing";
import type { ApiRoutes } from "../routers.js";
import { getNotesRouter } from "../notes.js";

describe("Notes API", () => {
  it("should get tablo notes", async () => {
    const app = getNotesRouter();
    const client = testClient(app);

    // Full type safety on the client
    const res = await client[":tabloId"].$get(
      { param: { tabloId: "123" } },
      { headers: { Authorization: "Bearer token" } }
    );

    // Response is fully typed
    const data = await res.json();
    // TypeScript knows the shape of data!
  });
});
```

### In Frontend Applications

```typescript
import { hc } from "hono/client";
import type { ApiRoutes } from "@your-api/client";

// Create a fully typed client
const client = hc<ApiRoutes>("https://api.yourdomain.com/api/v1");

// All endpoints are typed with autocomplete
const response = await client.users.me.$get(
  {},
  {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  }
);

// Response types are inferred
if (response.ok) {
  const data = await response.json();
  console.log(data.user); // Fully typed!
}
```

## Benefits

1. **End-to-End Type Safety** - From server to client, all types are inferred
2. **Autocomplete** - Your IDE will suggest all available endpoints and methods
3. **Compile-Time Errors** - Catch API mismatches before runtime
4. **Refactoring Safety** - Rename fields and TypeScript will update all usages
5. **Self-Documenting** - Types serve as documentation for the API

## Best Practices

### 1. Use Shared Environment Types

Instead of defining types in each router:

```typescript
// ❌ Before
type NotesEnv = {
  Variables: {
    user: User;
    supabase: SupabaseClient;
  };
};

// ✅ After
import type { AuthEnv } from "./app.types.js";
const factory = createFactory<AuthEnv>();
```

### 2. Extract Parameter Types

Use `c.req.param()` with type inference:

```typescript
const getTabloNotes = factory.createHandlers(async (c) => {
  const tabloId = c.req.param("tabloId"); // Typed as string
  // ...
});
```

### 3. Export Router Types

Always export the router type for RPC usage:

```typescript
export const getNotesRouter = () => {
  const router = new Hono<AuthEnv>();
  // ... setup routes
  return router;
};

// Export type for RPC
export type NotesRouter = ReturnType<typeof getNotesRouter>;
```

### 4. Use createFactory for Consistency

Always use `createFactory` with the appropriate environment:

```typescript
// For authenticated routes
const factory = createFactory<AuthEnv>();

// For public routes
const factory = createFactory<BaseEnv>();

// For mixed auth routes
const factory = createFactory<MaybeAuthEnv>();
```

## Migration Guide

If you're updating an existing router:

1. Import the appropriate environment type from `app.types.ts`
2. Replace custom environment types with shared ones
3. Update `new Hono<YourEnv>()` to use shared types
4. Use `createFactory<Env>()` for all handlers
5. Update `c.req.param()` calls to use the type-safe version

## Testing Type Safety

Run the TypeScript compiler to verify type safety:

```bash
npm run type-check
```

Run tests to ensure everything works:

```bash
npm test
```

## Resources

- [Hono RPC Documentation](https://hono.dev/docs/guides/rpc)
- [Hono Factory Pattern](https://hono.dev/docs/api/factory)
- [Hono TypeScript Guide](https://hono.dev/docs/guides/best-practices#typescript)
