> ## Documentation Index
> Fetch the complete documentation index at: https://thenile.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Routes

> Routes in the Nile-JS SDK

The Nile-JS SDK includes generated routes for all its operations.
These routes are used by the SDK methods to proxy requests to nile-auth, as well as directly from the React hooks and components in `@niledatabase/react`.

## Generating routes

Route generation depends on the framework you are using.

### Next.js example

<CodeGroup>
  ```ts nile.ts theme={null}
  // app/api/[...nile]/nile.ts
  import { Nile } from "@niledatabase/server";

  export const nile = Nile();

  export const { handlers } = nile;
  ```

  ```ts route.ts theme={null}
  // app/api/[...nile]/route.ts
  import { handlers } from './nile';

  export const { POST, GET, DELETE, PUT } = handlers;
  ```
</CodeGroup>

### Remix example

<CodeGroup>
  ```ts nile.ts theme={null}
  // app/nile.ts
  import { Nile } from "@niledatabase/server";

  export const nile = Nile();
  export const { handlers } = nile;
  ```

  ```ts nile-api.ts theme={null}
  //app/routes/nile-api.ts
  // This is where the routes are exported
  import type { Route } from './+types/home';
  import { handlers } from '~/nile';

  const { GET, POST, PUT, DELETE } = handlers;

  export const loader = async ({ request }: Route.LoaderArgs) => {
    switch (request.method.toUpperCase()) {
      case 'GET':
        return GET(request);
      case 'POST':
        return POST(request);
      case 'PUT':
        return PUT(request);
      case 'DELETE':
        return DELETE(request);
      default:
        return new Response('Method Not Allowed', { status: 405 });
    }
  };

  export const action = async ({ request }: Route.ActionArgs) => {
    switch (request.method.toUpperCase()) {
      case 'POST':
        return POST(request);
      case 'PUT':
        return PUT(request);
      case 'DELETE':
        return DELETE(request);
      default:
        return new Response('Method Not Allowed', { status: 405 });
    }
  };
  ```

  ```ts routes.ts theme={null}
  // app/routes.ts
  // This routes all the api/* requests to the routes in nile-api.ts
  import { type RouteConfig, index, route } from '@react-router/dev/routes';

  export default [
    index('routes/home.tsx'),
    route('api/*', 'routes/nile-api.ts'),
  ] satisfies RouteConfig;
  ```
</CodeGroup>

### Express example

<CodeGroup>
  ```ts server.mjs theme={null}
  import "dotenv/config";
  import express from "express";
  import { Nile } from "@niledatabase/server";
  import { NileExpressHandler } from "@niledatabase/server/express";

  const startServer = async () => {
  try {
  const app = express();
  const nile = Nile();
  // This is where the route handlers are imported
  const { paths, handler } = await NileExpressHandler(nile);

          app.use(express.json());
          app.use(express.urlencoded({ extended: true }));

          // This is where the routes are exposed in your express app
          app.get(paths.get, handler);
          app.post(paths.post, handler);
          app.put(paths.put, handler);
          app.delete(paths.delete, handler);

          // Your own routes go here

          const PORT = process.env.PORT || 3040;
          app.listen(PORT, () => {
              console.log(`Server is running on port ${PORT}`);
          });
      } catch (error) {
          console.error("Error starting server:", error);
          process.exit(1);
      }

  };

  startServer();

  ```
</CodeGroup>

## Using routes

You typically don't need to use the routes directly.
The SDK methods and react/web components use the routes under the hood to communicate with nile-auth.

The generated routes are available via `nile.paths`. For reference, here are the paths for the default routes:

```ts theme={null}
export const appRoutes = (prefix = '/api'): Routes => ({
  SIGNIN: `${prefix}/auth/signin`,
  PROVIDERS: `${prefix}/auth/providers`,
  SESSION: `${prefix}/auth/session`,
  CSRF: `${prefix}/auth/csrf`,
  CALLBACK: `${prefix}/auth/callback`, // this path has a route per enabled provider (e.g. google, github, etc.)
  SIGNOUT: `${prefix}/auth/signout`,
  ERROR: `${prefix}/auth/error`,
  VERIFY_REQUEST: `${prefix}/auth/verify-request`,
  PASSWORD_RESET: `${prefix}/auth/reset-password`,
  ME: `${prefix}/me`,
  USERS: `${prefix}/users`,
  TENANTS: `${prefix}/tenants`,
  TENANT: `${prefix}/tenants/{tenantId}`,
  TENANT_USER: `${prefix}/tenants/{tenantId}/users/{userId}`,
  TENANT_USERS: `${prefix}/tenants/{tenantId}/users`,
  SIGNUP: `${prefix}/signup`,
  LOG: `${prefix}/_log`,
});
```

A case where you might want to use the routes directly is when these routes are exposed as a REST API of your application backend.

For example, if you use the `@niledatabase/server/express` package, the routes are exposed in the `app` object and
while you may have a frontend that uses `@niledatabase/react` to call these routes from the application UI,
you may want to also use them as a REST API for another backend or external service.

In this case, you need to use the routes directly. The key is to:

1. Include both the session cookie and the CRSF cookie in the request headers.
2. Include the CSRF token in the request body.

Here are a few examples of how to call the routes directly:

<CodeGroup>
  ```bash getCSRFToken theme={null}
  # This gets the CSRF token from an API exposed by your application, and also saves the cookies to a file
  csrf_token=$(curl -s -X GET "http://localhost:3040/api/auth/csrf" -c "csrf_cookies.txt" | jq -r '.csrfToken')
  ```

  ```bash signup theme={null}
  # This signs up a new user with email/password credentials
  # The CSRF token is included in the request body and in the cookies
  # The cookies are saved to a file so they can be used in subsequent requests
  curl -X POST "http://localhost:3040/api/signup" \
    -H "Content-Type: application/json" \
    -b "csrf_cookies.txt" \
    --cookie-jar "login_cookies.txt" \
    -d "{\"csrfToken\":\"$csrf_token\",\"email\":\"newuser@example.com\",\"password\":\"foobar\"}"
  ```

  ```bash login theme={null}
  # This logs in a user with email/password credentials
  # The CSRF token is included in the request body and in the cookies
  # The cookies are saved to a file so they can be used in subsequent requests
  curl -X POST "http://localhost:3040/api/auth/callback/credentials" \
    -H "Content-Type: application/json" \
    -b "csrf_cookies.txt" \
    --cookie-jar "login_cookies.txt" \
    -d "{\"csrfToken\":\"$csrf_token\",\"email\":\"newuser@example.com\",\"password\":\"foobar\"}"
  ```

  ```bash getMe theme={null}
  # This gets the user's profile information
  curl -X GET "http://localhost:3040/api/me" -b "login_cookies.txt"
  ```

  ```bash createTenant theme={null}
  # This creates a new tenant
  curl -X POST 'localhost:3040/api/tenants' \
    -H 'Content-Type: application/json' \
    -d '{"name":"my first customer"}' \
    -b login_cookies.txt
  ```
</CodeGroup>

## Overriding routes

Sometimes you might want to intercept or override the routes used by the SDK in order to inject your own logic.
For example, adding your own logging or metrics, adding debugging information, or perhaps injecting your own cookies during login.

There are three ways to override routes:

1. **Route wrappers**: Wrap the route in your own logic. This can be done in the routes file, and is useful for minor modifications or debugging.
2. **Route overrides**: Override the route for a specific operation.

### Route wrappers

In the examples below, we'll use route wrappers to log the headers before every request and the body if it's a POST request.
We'll also log the status code of the response.

<CodeGroup>
  ```ts NextJS route wrappers theme={null}
  // app/api/[...nile]/route.ts
  import { handlers } from "./nile";

  // Middleware function to log request and response details
  const logRequestAndResponseDetails = (handler) => async (req, res) => {
  // Log the request method and URL
  console.log(`Request Method: ${req.method}, Request URL: ${req.url}`);

  // Log the request headers
  console.log('Request Headers:', req.headers);

  // Clone the request to safely read the body
  const clonedReq = req.clone();

  // Log the request body if it's a POST or PUT request
  if (req.method === 'POST') {
  const body = await clonedReq.text();
  console.log('Request Body:', body);
  }
  // Call the original handler and return its result
  const result = await handler(req, res);

  // Log the response status after the handler has executed
  console.log('Result Status:', result.status);

  return result;
  };

  // Wrap each handler with the logging middleware
  export const POST = logRequestAndResponseDetails(handlers.POST);
  export const GET = logRequestAndResponseDetails(handlers.GET);
  export const DELETE = logRequestAndResponseDetails(handlers.DELETE);
  export const PUT = logRequestAndResponseDetails(handlers.PUT);

  ```

  ```ts Remix route wrappers theme={null}
  // app/routes/nile-api.ts
  // In Remix, we need to add logging on both the loader and the action
  import type { Route } from "./+types/home";
  import { handlers } from "~/nile";

  const { GET, POST, PUT, DELETE } = handlers;

  export const loader = async ({ request }: Route.LoaderArgs) => {
    // Log request headers
    console.log('Request Headers:', JSON.stringify([...request.headers]));

    let response;
    switch (request.method.toUpperCase()) {
      case "GET":
        response = await GET(request);
        break;
      case "POST":
        // Log request body for POST
        const postBody = await request.text();
        console.log('POST Request Body:', postBody);
        response = await POST(request);
        break;
      case "PUT":
        response = await PUT(request);
        break;
      case "DELETE":
        response = await DELETE(request);
        break;
      default:
        response = new Response("Method Not Allowed", { status: 405 });
    }

    // Log response status
    console.log('Response Status:', response.status);
    return response;
  };

  export const action = async ({ request }: Route.ActionArgs) => {
    // Log request headers
    console.log('Request Headers:', JSON.stringify([...request.headers]));

    let response;
    switch (request.method.toUpperCase()) {
      case "POST":
        // Log request body for POST
        const postBody = await request.text();
        console.log('POST Request Body:', postBody);
        response = await POST(request);
        break;
      case "PUT":
        response = await PUT(request);
        break;
      case "DELETE":
        response = await DELETE(request);
        break;
      default:
        response = new Response("Method Not Allowed", { status: 405 });
    }

    // Log response status
    console.log('Response Status:', response.status);
    return response;
  };
  ```

  ```ts Express route wrappers theme={null}
  // server.mjs
  import 'dotenv/config';
  import express from 'express';
  import { Nile } from '@niledatabase/server';
  import { NileExpressHandler } from '@niledatabase/server/express';

  const startServer = async () => {
    try {
      const app = express();
      const nile = Nile({
        debug: true,
      });
      const { paths, handler } = await NileExpressHandler(nile);
      console.log(paths);

      app.use(express.json());
      app.use(express.urlencoded({ extended: true }));

      // Create logging wrapper
      const withLogging = (handler) => async (req, res, next) => {
        console.log('Request Headers:', req.headers);
        if (req.method === 'POST') {
          console.log('Request Body:', req.body);
        }
        return handler(req, res, next);
      };

      // Apply wrapper to specific routes
      app.get(paths.get, withLogging(handler));
      app.post(paths.post, withLogging(handler));
      app.put(paths.put, withLogging(handler));
      app.delete(paths.delete, withLogging(handler));

      const PORT = process.env.PORT || 3040;
      app.listen(PORT, () => {
        console.log(`Server is running on port ${PORT}`);
      });
    } catch (error) {
      console.error('Error starting server:', error);
      process.exit(1);
    }
  };

  startServer();
  ```
</CodeGroup>

### Route overrides

In the examples below, we'll add a new route that will override the default route for `\auth\google\callback` with
custom logic. We are using `handlersWithContext` to get the `nile` object in the route handler.
`handlersWithContext` returns a tuple with the `nile` object, configured based on the response from the route handler,
and the response from the route handler.

<CodeGroup>
  ```ts NextJS route overrides theme={null}
  // app/api/auth/google/callback/route.ts
  import { NextRequest } from "next/server";
  // make sure you export handlersWithContext from nile
  import { handlersWithContext } from "../../../[...nile]/nile"; 
  import { registerTenants } from "@/lib/TenantRegistration";

  export async function GET(req: NextRequest) {
    // call the original route
    const {nile, response} = await handlersWithContext.GET(req);

  if (nile) {
  const me = await nile.users.getSelf();
  if ("id" in me) {
  // custom logic is here
  await registerTenants(me.id);
  }
  }
  // return the original response from the route
  return response;
  }

  ```

  ```ts Remix route overrides theme={null}
  // app/routes/auth-callback-google.tsx
  // Also, add to routes.ts:
  // route("api/auth/google/callback", "routes/auth-callback-google.tsx")
  import type { Route } from "./+types/home";
  import { handlers } from "~/nile";

  const { GET } = handlers;

  export const loader = async ({ request }: Route.LoaderArgs) => {
    // Call Nile's GET handler
    const nileResponse = await GET(request);

    const setCookie = nileResponse.headers.get('set-cookie');
    const hasSession = setCookie && setCookie.includes("nile.session-token");

    if (hasSession) {
      // If login was successful, register the tenants
      const headers = new Headers();
      headers.append('cookie', setCookie);

      // Set the headers for the Nile API
      const nileCtx = await nile.withContext({ headers });

      // Fetch user information
      const me = await nileCtx.users.getSelf();
      if ("id" in me) {
        // Custom logic: register tenants
        await registerTenants(me.id);
      }
    }

    return nileResponse;
  };
  ```

  ```ts Express route overrides theme={null}
  // server.mjs
  // One big difference between express and other frameworks is that Nile's route handlers
  // do not return a response by default. In order to use them in a custom handler,
  // you need some extra configuration and extra handling of all routes

  import 'dotenv/config';
  import express from 'express';
  import { Nile } from '@niledatabase/server';
  import { NileExpressHandler } from '@niledatabase/server/express';

  const startServer = async () => {
    try {
      const app = express();
      const nile = Nile({
        // debug: true,
      });
      // muteResponse is set to true to avoid sending the response back to the client from the route handler
      // this is useful when you want to override the route with your own logic
      const { paths, handler } = await NileExpressHandler(nile, {
        muteResponse: true,
      });
      console.log(paths);

      app.use(express.json());
      app.use(express.urlencoded({ extended: true }));

      // Add custom Google OAuth callback route - This has to be done before all the default routes
      app.get('/auth/google/callback', async (req, res, next) => {
        const { status, headers, body } = await handler(req, res, next);
        if (status === 200) {
          // Fetch user information
          const nileCtx = nile.withContext({ headers });
          const me = await nileCtx.users.getSelf();
          if ('id' in me) {
            // Custom logic: register tenants
            await registerTenants(me.id);
          }
        }
        res.status(status).set(headers);
        if (typeof body === 'string') {
          res.send(body);
        } else {
          res.json(body ?? {});
        }
        return;
      });

      // Create response logging wrapper
      // This is critical to ensure that the response is sent back to the client from the route handlers
      // So make sure you use with `{ muteResponse: true }` even if you don't want to log all the responses.
      const withResponseLogging = (handler) => async (req, res, next) => {
        try {
          // Wait for the handler to complete
          const result = await handler(req, res, next);
          // Log the result
          console.log('Handler result:', result);
          const { status, headers, body } = result;
          res.status(status).set(headers);
          if (typeof body === 'string') {
            res.send(body);
          } else {
            res.json(body ?? {});
          }
          return;
        } catch (error) {
          console.error('Handler error:', error);
          throw error;
        }
      };

      // Apply wrapper to routes
      app.get(paths.get, withResponseLogging(handler));
      app.post(paths.post, withResponseLogging(handler));
      app.put(paths.put, withResponseLogging(handler));
      app.delete(paths.delete, withResponseLogging(handler));

      const PORT = process.env.PORT || 3040;
      app.listen(PORT, () => {
        console.log(`Server is running on port ${PORT}`);
      });
    } catch (error) {
      console.error('Error starting server:', error);
      process.exit(1);
    }
  };

  startServer();
  ```
</CodeGroup>
