Lithia
Fundamentals

Request

Understanding the Request object in Lithia

The Request object in Lithia provides access to all incoming HTTP request data, including headers, query parameters, body, and more.

Basic Usage

src/routes/users/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  // Access request data
  const method = req.method;
  const url = req.url;
  const headers = req.headers;
  
  return res.json({ method, url, headers });
};

Request Properties

Method

The HTTP method of the request (GET, POST, PUT, DELETE, etc.)

src/routes/api/route.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  if (req.method === 'GET') {
    return res.json({ message: 'This is a GET request' });
  }
  
  if (req.method === 'POST') {
    return res.json({ message: 'This is a POST request' });
  }
  
  return res.status(405).json({ error: 'Method not allowed' });
};

URL and Path

Access the full URL and path segments

src/routes/users/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const fullUrl = req.url();       // http://localhost:3000/users/123?name=john
  const pathname = req.pathname;   // /users/123
  const query = req.query;         // { name: 'john' }
  
  return res.json({
    fullUrl,
    pathname,
    query
  });
};

Headers

Access request headers

src/routes/auth/route.post.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const contentType = req.headers['content-type'];
  const userAgent = req.userAgent();
  const authorization = req.headers['authorization'];
  
  return res.json({
    contentType,
    userAgent,
    authorization
  });
};

Body

Access request body data

src/routes/users/route.post.ts
// POST /users - JSON data
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const body = await req.body<{ name: string; email: string }>();
  const { name, email } = body;
  
  return res.status(201).json({ name, email });
};
src/routes/form/route.post.ts
// POST /form - Form data
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const formData = await req.body<Record<string, string>>();
  
  return res.json({ received: formData });
};
src/routes/text/route.post.ts
// POST /text - Plain text
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const text = await req.body<string>();
  
  return res.json({ received: text });
};

Path Parameters

Access dynamic route parameters

src/routes/users/[id]/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const userId = req.params.id; // Access the [id] parameter
  
  return res.json({ userId });
};

Query Parameters

Access URL query parameters with automatic type conversion

src/routes/posts/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  // Query parameters are automatically parsed and typed
  const page = req.query.page || 1;      // Automatically converted to number
  const limit = req.query.limit || 10;   // Automatically converted to number
  const search = req.query.search;       // String
  const active = req.query.active;       // Boolean (if 'true' or 'false')
  const users = req.query.users;         // Array of strings when ?users=1,2,3
  
  return res.json({
    page,
    limit,
    search,
    active
  });
};

You can enable/disable this behavior on your lithia.config.ts file:

lithia.config.ts
import { defineLithiaConfig } from 'lithia';

export default defineLithiaConfig({
  server: {
    request: {
      queryParser: {
        array: {
          enabled: true,
          delimiter: ',',
        },
        number: {
          enabled: true,
        },
        boolean: {
          enabled: true,
        },
      },
      maxBodySize: 1048576,
    },
  },
});

Cookies

Access request cookies

src/routes/profile/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const sessionId = req.cookie('sessionId');
  const theme = req.cookie('theme') || 'light';
  const allCookies = req.cookies();
  
  return res.json({
    sessionId,
    theme,
    allCookies
  });
};

Request Helpers

Lithia provides helpful methods to inspect request properties

src/routes/info/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  return res.json({
    // Content type checks
    isJson: req.isJson(),
    isFormData: req.isFormData(),
    isAjax: req.isAjax(),
    
    // Network info
    ip: req.ip(),
    userAgent: req.userAgent(),
    host: req.host(),
    protocol: req.protocol(),
    isSecure: req.isSecure(),
    
    // Content acceptance
    acceptsJson: req.accepts('application/json'),
    acceptsHtml: req.accepts('text/html')
  });
};
src/routes/conditional/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  // Different responses based on request type
  if (req.isAjax()) {
    return res.json({ message: 'AJAX request' });
  }
  
  if (req.accepts('text/html')) {
    return res.html('<h1>HTML Response</h1>');
  }
  
  return res.json({ message: 'Default response' });
};

Request Context Storage

Store and retrieve data during request processing

src/routes/storage/route.get.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  // Store data in request context
  req.set('userId', 123);
  req.set('timestamp', Date.now());
  
  // Retrieve data from request context
  const userId = req.get<number>('userId');
  const timestamp = req.get<number>('timestamp');
  
  return res.json({ userId, timestamp });
};

This context is available through the req object in all routes and middleware, including on hooks.

Request Validation

Validate incoming request data

src/routes/auth/login/route.post.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }
  
  const body = await req.body();
  
  if (!body.email || !body.password) {
    return res.status(400).json({ 
      error: 'Email and password are required' 
    });
  }
  
  // Process valid request
  return res.json({ message: 'Login successful' });
};

Middleware Integration

Use request data in middleware

middlewares/auth.ts
import { UnauthorizedError, type LithiaMiddleware } from 'lithia';
import { getUserByToken } from '@/services/auth';

export function auth(): LithiaMiddleware {
  return async (req, _res, next) => {
    const token = req.headers.authorization;

    if (!token) {
      throw new UnauthorizedError(
        'You are not authorized to access this resource',
      );
    }

    const user = await getUserByToken(token);
    
    if (!user) {
      throw new UnauthorizedError(
        'You are not authorized to access this resource',
      );
    }

    req.set('user', user);

    next();
  };
}
src/routes/protected/route.get.ts
import type { LithiaRequest, LithiaResponse, LithiaMiddleware } from 'lithia';
import type { User } from '@/types/user';
import { auth } from '@/middlewares/auth';

export default async (req: LithiaRequest, res: LithiaResponse) => {
  // User is available from middleware
  const user = req.get<User>('user');
  
  return res.json({ user });
};

export const middlewares: LithiaMiddleware[] = [auth()];

Best Practices

  1. Always validate input data before processing
  2. Handle errors gracefully with proper status codes
  3. Sanitize user input to prevent security issues
  4. Use TypeScript for better type safety
src/routes/users/route.post.ts
import type { LithiaRequest, LithiaResponse } from 'lithia';
import { createUser } from '@/services/user';
import { z } from "zod";

const createUserSchema = z.object({
  name: z.string(),
  email: z.string(),
  age: z.number(),
});

type CreateUserRequest = z.infer<typeof createUserSchema>;

export default async (req: LithiaRequest, res: LithiaResponse) => {
  const body = await req.body<CreateUserRequest>();
  const result = createUserSchema.safeParse(body);

  if (!result.success) {
    return res.status(400).json({
      error: 'Invalid request body',
      message: result.error.message,
    });
  }
  
  const user = await createUser(result.data);

  return res.status(201).json(user);
};