﻿# HTTP context

With `@lazarv/react-server` you have access to everything related to the context of server-side rendering. This guide provides information for you how to access the HTTP context.

The following hooks / functions are available for you to access and manipulate the HTTP context.

All of these functions are available also in middlewares and route handlers with the file-system based router as these are runtime specific and not related to React.

With `useHttpContext()` you can get access to the full HTTP context.

```jsx
import { useHttpContext } from "@lazarv/react-server";

export default function MyComponent() {
  const context = useHttpContext();

  return (
    <div>
      <p>Method: {context.request.method}</p>
      <p>URL: {context.request.url.toString()}</p>
      <p>Request Headers: {JSON.stringify(context.request.headers)}</p>
      <p>Request Cookies: {JSON.stringify(context.request.cookie)}</p>
    </div>
  );
};
```

## Request

With `useRequest()` you can get access to the full HTTP request.

```jsx
import { useRequest } from "@lazarv/react-server";

export default function MyComponent() {
  const request = useRequest();

  return (
    <div>
      <p>Method: {request.method}</p>
      <p>URL: {request.url.toString()}</p>
      <p>Headers: {JSON.stringify(request.headers)}</p>
      <p>Cookies: {JSON.stringify(request.cookie)}</p>
    </div>
  );
};
```

## Response

With `useResponse()` you can get access to the full HTTP response. This is only available after the response has been sent to the client, in a React component which was suspended and streamed to the client later than the response was sent.

```jsx
import { useResponse } from "@lazarv/react-server";

export default async function MyComponent() {
  const response = await useResponse();

  return (
    <div>
      <p>Headers: {JSON.stringify(response.headers)}</p>
      <p>Cookies: {JSON.stringify(response.cookie)}</p>
    </div>
  );
};
```

## URL

With `useUrl()` you can get access to the URL of the current request.

```jsx
import { useUrl } from "@lazarv/react-server";

export default function MyComponent() {
  const url = useUrl();

  return <p>URL: {url.href}</p>;
};
```

## Pathname

With `usePathname()` you can get access to the pathname of the current request.

```jsx
import { usePathname } from "@lazarv/react-server";

export default function MyComponent() {
  const pathname = usePathname();

  return <p>Pathname: {pathname}</p>;
};
```

## Search Params

With `useSearchParams()` you can get access to the search params of the current request. This is an object with key-value pairs of the search params. In case of multiple values for the same key, the value will be an array.

```jsx
import { useSearchParams } from "@lazarv/react-server";

export default function MyComponent() {
  const searchParams = useSearchParams();

  return (
    <p>Search params: {JSON.stringify(searchParams)}</p>
  );
};
```

## Headers

With `headers()` you can get access to the headers of the current request.

```jsx
import { headers } from "@lazarv/react-server";

export default function MyComponent() {
  const requestHeaders = headers();

  return <p>Headers: {JSON.stringify(requestHeaders)}</p>;
};
```

You can also modify the headers of the current response by passing an object of key-value pairs:

```jsx
import { headers } from "@lazarv/react-server";

export default function MyComponent() {
  headers({
    "X-My-Header": "My value",
  });

  return <p>Headers: {JSON.stringify(headers())}</p>;
};
```

Or by passing a `Headers` object:

```jsx
import { headers } from "@lazarv/react-server";

export default function MyComponent() {
  headers(new Headers({
    "X-My-Header": "My value",
  }));

  return <p>Headers: {JSON.stringify(headers())}</p>;
};
```

Or by passing an array of key-value pairs:

```jsx
import { headers } from "@lazarv/react-server";

export default function MyComponent() {
  headers([
    ["X-My-Header", "My value"],
  ]);

  return <p>Headers: {JSON.stringify(headers())}</p>;
};
```

Modifying the headers with the `headers()` function will override the headers of the current response. If you want to mutate the response headers directly, you can use three addition helper functions to set, append or delete headers. These functions are `setHeader()`, `appendHeader()` and `deleteHeader()`.

```jsx
import { setHeader, appendHeader, deleteHeader } from "@lazarv/react-server";

export default function MyComponent() {
  setHeader("X-My-Header", "My first value");
  appendHeader("X-My-Header", "My second value");
  deleteHeader("X-My-Header");

  return <p>Check the response headers!</p>;
}
```

> **Note:** Keep in mind that HTTP headers are case-insensitive!

## Cookies

With `cookie()` you can get access to the cookies of the current request.

```jsx
import { cookie } from "@lazarv/react-server";

export default function MyComponent() {
  const requestCookies = cookie();

  return <p>Cookies: {JSON.stringify(requestCookies)}</p>;
};
```

You can also set or delete cookies in the context of the current response.

```jsx
import { setCookie, deleteCookie } from "@lazarv/react-server";

export default function MyComponent() {
  setCookie("my-cookie", "my-value");
  deleteCookie("other-cookie");

  return <p>Cookies: {JSON.stringify(cookie())}</p>;
};
```

## Status

With `status()` you can set the status code and text of the current response.

```jsx
import { status } from "@lazarv/react-server";

export default function MyComponent() {
  status(404, "Not found");

  return <p>Not Found</p>;
};
```

## Form Data

With `useFormData()` you can get access to the form data of the current request.

```jsx
import { useFormData } from "@lazarv/react-server";

export default function MyComponent() {
  const formData = useFormData();

  return (
    <p>Form data: {JSON.stringify(Object.fromEntries(formData.entries()))}</p>
  );
};
```

## Redirect

With `redirect()` you can redirect the current request to another URL.

> **Warning:** the `redirect()` function will throw an error which will be caught by the runtime and will result in a redirect. When you want to use `redirect()` in a `try`/`catch` block, make sure you rethrow the error if it's a redirect error.

```jsx
import { redirect } from "@lazarv/react-server";

export default function MyComponent() {
  redirect("https://example.com");
};
```

The `redirect()` function accepts an optional third argument `kind` that controls how the redirect is performed on the client. The available kinds are:

| Kind | Description |
| --- | --- |
| `"navigate"` | **(default)** Performs an RSC navigation using `replaceState`. The browser URL changes without adding a history entry. |
| `"push"` | Performs an RSC navigation using `pushState`. The browser URL changes and a new history entry is added, so the user can navigate back. |
| `"location"` | Forces a full browser navigation via `location.href`. Useful for redirecting to external URLs or when a full page reload is needed. |
| `"error"` | Throws the redirect error on the client instead of navigating. This allows custom handling via `try`/`catch` in server action calls. |

```jsx
import { redirect } from "@lazarv/react-server";

// RSC navigation with pushState (adds history entry)
redirect("/dashboard", 302, "push");

// Full browser navigation
redirect("/oauth/authorize", 302, "location");

// Throw on client for custom handling
redirect("/login", 302, "error");
```

When using the `"error"` kind in a server action, the client can catch the redirect error and handle it:

```jsx
"use client";

import { myServerAction } from "./actions";

export function MyComponent() {
  const handleClick = async () => {
    try {
      await myServerAction();
    } catch (e) {
      if (e?.digest?.startsWith("Location=")) {
        const url = e.digest.split("Location=")[1]?.split(";")[0];
        console.log(`Redirect to: ${url}`);
      }
    }
  };

  return <button onClick={handleClick}>Submit</button>;
}
```

## Rewrite

With `rewrite()` you can rewrite the current request to another URL. This is useful to modify the URL pathname of the current request in a middleware function.

```jsx
import { rewrite, useUrl } from "@lazarv/react-server";

export function init$() {
  return async () => {
    const { pathname } = useUrl();

    if (pathname === "/old-pathname") {
      rewrite("/new-pathname");
    }
  };
}

export default function MyComponent() {
  const { pathname } = useUrl();

  return <p>Current pathname: {pathname}</p>;
}
```

## Outlet

With `useOutlet()` you can get access to the outlet of the current request. This is useful to get the name of the outlet where the current request is rendered.

```jsx
import { useOutlet } from "@lazarv/react-server";

export default function MyComponent() {
  const outlet = useOutlet();

  return <p>Outlet: {outlet}</p>;
}
```

## Render Lock

With `useRender()` you can get access to the render lock of the current request. This is useful if you want to lock rendering of a React Server Component while the async function is running or until the lock is released, as React Server Components are rendered using streaming by default. This is especially useful for handling HTTP headers and cookies in an async React Server Component. Without locking the rendering, the headers and cookies will be sent to the client before the async function is finished. When a lock is detected in the rendering process, the rendering will wait for the lock to be released before beginning to send the headers and cookies to the client and starting the streaming of the React Server Component.

```jsx
import { headers, useRender } from "@lazarv/react-server";

export default function MyComponent() {
  const { lock } = useRender();

  await lock(async () => {
    // Do something async
    await new Promise((resolve) => setTimeout(resolve, 1000));
    headers({
      "x-lock": "works",
    });
  });

  return <p>Render lock</p>;
}
```

You can also use the `lock()` function to get an `unlock()` function to release the lock later.

```jsx
import { headers, useRender } from "@lazarv/react-server";

export default function MyComponent() {
  const { lock } = useRender();

  const unlock = lock();
  // Do something async
  await new Promise((resolve) => setTimeout(resolve, 1000));
  headers({
    "x-lock": "works",
  });
  unlock();

  return <p>Render lock</p>;
}
```

## Logger

With `logger` you can log messages using the runtime's built-in logger. The `logger` object provides `info`, `warn`, `error`, and `debug` methods that integrate with the runtime's logging system, providing consistent and formatted output.

```jsx
import { logger } from "@lazarv/react-server";

export default function MyComponent() {
  logger.info("Rendering MyComponent");

  return <p>Hello World</p>;
}
```

The `logger` automatically uses the runtime's Vite-integrated logger in development mode for nicely formatted output, and falls back to `console` in production. It is context-aware — when called inside an `after()` callback, the log output is annotated with an `(after)` label so you can distinguish post-response logs from rendering logs.

```jsx
import { after, logger } from "@lazarv/react-server";

export default function MyComponent() {
  logger.info("Rendering component");

  after(() => {
    logger.info("Response sent"); // logged with (after) label in dev
  });

  return <p>Hello World</p>;
}
```

The available methods are:

| Method | Description |
|---|---|
| `logger.info(msg, ...args)` | Log an informational message |
| `logger.warn(msg, ...args)` | Log a warning message |
| `logger.error(msg, ...args)` | Log an error message or `Error` object |
| `logger.debug(msg, ...args)` | Log a debug message |

> **Note:** The `logger` can be used anywhere on the server — in components, server functions, middleware, route handlers, workers, and `after()` callbacks. It does not require a request context, but when one is available, it uses the context-specific logger instance.

## After

With `after()` you can register a callback function that runs **after the response has been sent** to the client. This is useful for performing cleanup tasks, logging, analytics, or any side effects that should not delay the response.

```jsx
import { after, logger } from "@lazarv/react-server";

export default function MyComponent() {
  after(() => {
    logger.info("Response sent to client.");
  });

  return <p>Hello World</p>;
}
```

The `after()` hook can be called multiple times to register multiple callbacks. All registered callbacks run concurrently via `Promise.allSettled` after the response stream completes, so one failing callback does not prevent the others from running. If the request failed with an error, the error is passed to each callback as the first argument:

```jsx
import { after, logger } from "@lazarv/react-server";

export default function MyComponent() {
  after((error) => {
    if (error) {
      logger.error("Request failed:", error.message);
    } else {
      logger.info("Request completed successfully");
    }
  });

  return <p>Hello World</p>;
}
```

```jsx
import { after } from "@lazarv/react-server";

export default function MyComponent() {
  after(async () => {
    await saveAnalytics({ page: "/home", timestamp: Date.now() });
  });

  after(async () => {
    await cleanupTempFiles();
  });

  return <p>Home</p>;
}
```

You can also use `after()` in server functions, middleware, route handlers, or any server-side code that runs within a request context:

```jsx
import { after } from "@lazarv/react-server";

export async function submitForm(formData) {
  "use server";

  const data = Object.fromEntries(formData.entries());
  await saveToDatabase(data);

  after(async () => {
    await sendNotificationEmail(data.email);
  });
}
```

> **Note:** The `after()` hook can only be called during a request. Calling it outside of a request context (e.g., at module scope or in a standalone script) will throw an error.