AzuraJS Logo
AzuraJSFramework
v2.2 Beta

TypeScript Support

Leverage full TypeScript support in AzuraJS

TypeScript Support 🔷

AzuraJS is built with TypeScript and provides first-class type safety throughout your application.

Full Type Safety 🛡️

All AzuraJS APIs are fully typed:

import type {
  RequestServer,
  ResponseServer,
} from "azurajs";
import type { RequestHandler } from "azurajs/types";
import type { ConfigTypes } from "azurajs/config";
import type { CookieOptions } from "azurajs/cookies";

Request and Response Types 📦

RequestServer

interface RequestServer extends IncomingMessage {
  body?: any;
  params?: Record<string, string>;
  query?: Record<string, string>;
  cookies?: Record<string, string>;
  originalUrl: string;
  protocol: "http" | "https";
  secure: boolean;
  hostname: string;
  subdomains: string[];
  ips: string[];
  get(name: string): string | undefined;
  header(name: string): string | undefined;
}

ResponseServer

interface ResponseServer extends ServerResponse {
  status(code: number): ResponseServer;
  send(body: any): void;
  json(data: any): void;
  set(field: string, value: string | number | string[]): ResponseServer;
  header(field: string, value: string | number | string[]): ResponseServer;
  get(field: string): string | undefined;
  type(contentType: string): ResponseServer;
  contentType(contentType: string): ResponseServer;
}

Typed DTOs 📋

Create strongly-typed Data Transfer Objects:

// User types
interface User {
  id: number;
  name: string;
  email: string;
  role: "user" | "admin";
  createdAt: Date;
}

interface CreateUserDto {
  name: string;
  email: string;
  password: string;
  role?: "user" | "admin";
}

interface UpdateUserDto {
  name?: string;
  email?: string;
  role?: "user" | "admin";
}

// Use in controller
import { Controller, Post, Body, Res } from "azurajs/decorators";
import type { ResponseServer } from "azurajs";

@Controller("/api/users")
export class UserController {
  @Post()
  createUser(@Body() data: CreateUserDto, @Res() res: ResponseServer): void {
    // data is fully typed
    const user: User = {
      id: Date.now(),
      name: data.name,
      email: data.email,
      role: data.role || "user",
      createdAt: new Date(),
    };
    
    res.status(201).json({ user });
  }

  @Patch("/:id")
  updateUser(
    @Param("id") id: string,
    @Body() data: UpdateUserDto,
    @Res() res: ResponseServer
  ): void {
    // TypeScript knows all fields are optional
    res.json({ id, ...data });
  }
}

Generic Response Types 🎯

Create type-safe response wrappers:

interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
  message?: string;
}

interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

// Usage
@Get()
getUsers(@Res() res: ResponseServer): void {
  const response: ApiResponse<User[]> = {
    success: true,
    data: users,
  };
  res.json(response);
}

@Get("/paginated")
getPaginatedUsers(
  @Query("page") page: string,
  @Query("limit") limit: string,
  @Res() res: ResponseServer
): void {
  const response: PaginatedResponse<User> = {
    data: users.slice(0, 10),
    pagination: {
      page: Number(page),
      limit: Number(limit),
      total: users.length,
      totalPages: Math.ceil(users.length / Number(limit)),
    },
  };
  res.json(response);
}

Type Guards 🛡️

Use type guards for runtime type checking:

function isUser(obj: any): obj is User {
  return (
    typeof obj === "object" &&
    typeof obj.id === "number" &&
    typeof obj.name === "string" &&
    typeof obj.email === "string" &&
    ["user", "admin"].includes(obj.role)
  );
}

@Post()
createUser(@Body() body: any, @Res() res: ResponseServer): void {
  if (!isUser(body)) {
    res.status(400).json({ error: "Invalid user data" });
    return;
  }
  
  // body is now typed as User
  res.json({ user: body });
}

Zod for Runtime Validation 📦

Combine TypeScript types with runtime validation:

import { z } from "zod";

// Define schema
const createUserSchema = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
  password: z.string().min(8),
  role: z.enum(["user", "admin"]).optional(),
});

// Infer TypeScript type from schema
type CreateUserDto = z.infer<typeof createUserSchema>;

@Post()
createUser(@Body() body: unknown, @Res() res: ResponseServer): void {
  try {
    // Validate and get typed data
    const data: CreateUserDto = createUserSchema.parse(body);
    
    // data is fully typed and validated
    res.status(201).json({ user: data });
  } catch (error) {
    res.status(400).json({ error: "Validation failed" });
  }
}

Middleware Types 🔌

Create typed middleware:

import type { RequestServer } from "azurajs";
import type { RequestHandler } from "azurajs/types";

interface AuthenticatedRequest extends RequestServer {
  user: User;
}

const authMiddleware: RequestHandler = async (req, res, next) => {
  const token = req.headers.authorization;
  
  if (!token) {
    res.status(401).json({ error: "Unauthorized" });
    return;
  }
  
  const user = await verifyToken(token);
  (req as AuthenticatedRequest).user = user;
  
  await next();
};

// Use in controller
@Get("/profile")
getProfile(@Req() req: AuthenticatedRequest, @Res() res: ResponseServer): void {
  // req.user is typed as User
  res.json({ user: req.user });
}

Configuration Types ⚙️

Configuration is fully typed:

import type { ConfigTypes } from "azurajs/config";

const config: ConfigTypes = {
  environment: "development",  // "development" | "production" | "test"
  server: {
    port: 3000,
    cluster: false,
    ipHost: true,
    https: false,
  },
  logging: {
    enabled: true,
    showDetails: true,
  },
  plugins: {
    cors: {
      enabled: true,
      origins: ["*"],
      methods: ["GET", "POST"],
      allowedHeaders: ["Content-Type"],
    },
    rateLimit: {
      enabled: false,
      limit: 100,
      timeframe: 60000,
    },
  },
};

Enum Types 🎯

Use enums for fixed values:

enum UserRole {
  USER = "user",
  ADMIN = "admin",
  MODERATOR = "moderator",
}

enum OrderStatus {
  PENDING = "pending",
  PROCESSING = "processing",
  COMPLETED = "completed",
  CANCELLED = "cancelled",
}

interface User {
  id: number;
  name: string;
  role: UserRole;
}

@Post("/users")
createUser(@Body() data: any, @Res() res: ResponseServer): void {
  const user: User = {
    id: Date.now(),
    name: data.name,
    role: UserRole.USER,  // Type-safe
  };
  res.json({ user });
}

Utility Types 🛠️

Leverage TypeScript utility types:

// Partial - make all properties optional
type UpdateUserDto = Partial<CreateUserDto>;

// Pick - select specific properties
type UserPublicInfo = Pick<User, "id" | "name" | "role">;

// Omit - exclude specific properties
type UserWithoutPassword = Omit<User, "password">;

// Required - make all properties required
type RequiredConfig = Required<ConfigTypes>;

// Readonly - make all properties readonly
type ImmutableUser = Readonly<User>;

// Usage
@Get("/:id")
getUser(@Param("id") id: string, @Res() res: ResponseServer): void {
  const user: User = findUser(id);
  const publicInfo: UserPublicInfo = {
    id: user.id,
    name: user.name,
    role: user.role,
  };
  res.json({ user: publicInfo });
}

Strict Mode 🔒

Enable strict TypeScript checking:

tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}

Type Inference ✨

Let TypeScript infer types automatically:

// No need to explicitly type these
const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
]; // Type: { id: number; name: string }[]

function getUser(id: number) {
  return users.find(u => u.id === id);
} // Return type: { id: number; name: string } | undefined

const user = getUser(1);  // Type inferred as above

Best Practices ✨

Use interfaces for DTOs - Clear contracts for data shapes

Leverage type inference - Let TypeScript figure out types when obvious

Enable strict mode - Catch more errors at compile time

Avoid any - Use unknown or proper types instead

Next Steps 📖

On this page