> ## 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.

# Remix

> Integrate Nile Auth with Remix applications

# Remix API Integration with Nile Database

This guide explains how to integrate **Nile Database** with **Remix** and set up routes for handling various HTTP requests (`GET`, `POST`, `PUT`, `DELETE`). Additionally, you'll see how to include **client-side components** for user authentication and interaction using **Nile's React SDK**.

***

<Steps>
  <Step title="Create a new Remix project">
    Run the following command in your terminal to create a new Remix project:

    ```bash theme={null}
    npx create-react-router@latest --template remix-run/react-router-templates/node-postgres
    ```

    Follow the prompts and install your app. After creating the project, navigate into the newly created project directory:

    ```bash theme={null}
    cd <your-project-name>
    ```

    ```bash theme={null}
    npm install @niledatabase/server @niledatabase/react @niledatabase/client
    ```
  </Step>

  <Step title="Obtain Database Credentials">
    1. If you haven't signed up for Nile yet, [sign up here](https://console.thenile.dev) and follow the steps to create a database.
    2. Navigate to **Database Settings** in your database's UI at [console.thenile.dev](https://console.thenile.dev).
    3. Go to **Connection** settings.
    4. Select the CLI icon, and click **Generate credentials**
           <img src="https://mintcdn.com/nile/6B-b9nH3DSiJ_Uwi/images/auth/generate-credentials.png?fit=max&auto=format&n=6B-b9nH3DSiJ_Uwi&q=85&s=f9f160a368eccc6ed7f033a4453aaeee" alt="Generate credentials" width="2020" height="898" data-path="images/auth/generate-credentials.png" />
    5. **Copy** the required credentials and **store them in an `.env` file** so they can be used in the application to connect to the Nile auth service.
    6. While you are there, click on the `PostgreSQL` icon and also **copy your database url** for drizzle to use
       ```bash theme={null}
       NILEDB_USER=niledb_user
       NILEDB_PASSWORD=niledb_password
       NILEDB_API_URL=https://us-west-2.api.thenile.dev/v2/databases/<database_id>
       NILEDB_POSTGRES_URL=postgres://us-west-2.db.thenile.dev:5432/<database_name>
       DATABASE_URL=postgres://niledb_user:niledb_password0@us-west-2.db.thenile.dev:5432/<database_name>
       ```
  </Step>

  <Step title="Update create-react-router output">
    Now we must update the output from the default `create-react-router` for use with Nile. We want to switch to using `node-postgres`,  and be able to do a top level `await` for nile configuration.

    <Tabs>
      <Tab title="CLI">
        ```bash bash [expandable] theme={null}
        cat > server/app.ts << 'EOF'
        import { createRequestHandler } from "@react-router/express";
        import { drizzle } from "drizzle-orm/node-postgres";
        import express from "express";
        import postgres from "pg";
        import "react-router";

        import { DatabaseContext } from '~/database/context';
        import * as schema from '~/database/schema';

        declare module "react-router" {
        interface AppLoadContext {
        VALUE_FROM_EXPRESS: string;
        }
        }

        export const app = express();

        if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL is required");

        const client = new postgres.Client(process.env.DATABASE*URL);
        await client.connect();
        const db = drizzle(client, { schema });
        app.use((*, \_\_, next) => DatabaseContext.run(db, next));

        app.use(
        createRequestHandler({
        build: () => import("virtual:react-router/server-build"),
        getLoadContext() {
        return {
        VALUE_FROM_EXPRESS: "Hello from Express",
        };
        },
        })
        );
        EOF

        cat > database/context.ts << 'EOF'
        import { AsyncLocalStorage } from "node:async_hooks";

        import type { NodePgDatabase } from "drizzle-orm/node-postgres";

        import * as schema from './schema';

        export const DatabaseContext = new AsyncLocalStorage<
          NodePgDatabase<typeof schema>
        >();

        export function database() {
          const db = DatabaseContext.getStore();
          if (!db) {
            throw new Error("DatabaseContext not set");
          }
          return db;
        }
        EOF

        cat > drizzle/0000_short_donald_blake.sql << 'EOF'
        CREATE TABLE IF NOT EXISTS "guestBook" (
        "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
        "name" varchar(255) NOT NULL,
        "email" varchar(255) NOT NULL,
        CONSTRAINT "guestBook_email_unique" UNIQUE("email")
        );
        EOF

        cat > vite.config.ts << 'EOF'
        import { reactRouter } from "@react-router/dev/vite";
        import tailwindcss from "@tailwindcss/vite";
        import { defineConfig } from "vite";
        import tsconfigPaths from "vite-tsconfig-paths";

        export default defineConfig(({ isSsrBuild }) => ({
          optimizeDeps: {
            esbuildOptions: {
              target: "esnext",
            },
          },
          build: {
            target: "esnext",
            rollupOptions: isSsrBuild
              ? {
                  input: "./server/app.ts",
                }
              : undefined,
          },
          plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
        }));
        EOF;

        ```
      </Tab>

      <Tab title="Manual">
        Replace `/server/app.ts` with the following

        ```ts theme={null}
        import { createRequestHandler } from "@react-router/express";
        import { drizzle } from "drizzle-orm/node-postgres";
        import express from "express";
        import postgres from "pg";
        import "react-router";

        import { DatabaseContext } from "~/database/context";
        import * as schema from "~/database/schema";

        declare module "react-router" {
          interface AppLoadContext {
            VALUE_FROM_EXPRESS: string;
          }
        }

        export const app = express();

        if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL is required");

        const client = new postgres.Client(process.env.DATABASE_URL);
        await client.connect();
        const db = drizzle(client, { schema });
        app.use((_, __, next) => DatabaseContext.run(db, next));

        app.use(
          createRequestHandler({
            build: () => import("virtual:react-router/server-build"),
            getLoadContext() {
              return {
                VALUE_FROM_EXPRESS: "Hello from Express",
              };
            },
          })
        );
        ```

        Replace `database/context.ts` with the following

        ```ts theme={null}
        import { AsyncLocalStorage } from 'node:async_hooks';

        import type { NodePgDatabase } from 'drizzle-orm/node-postgres';

        import * as schema from './schema';

        export const DatabaseContext = new AsyncLocalStorage<
          NodePgDatabase<typeof schema>
        >();

        export function database() {
          const db = DatabaseContext.getStore();
          if (!db) {
            throw new Error('DatabaseContext not set');
          }
          return db;
        }
        ```

        Update the placeholder table `drizzle/0000_short_donald_blake.sql` to generate correctly

        ```sql theme={null}
        CREATE TABLE IF NOT EXISTS "guestBook" (
            "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
            "name" varchar(255) NOT NULL,
            "email" varchar(255) NOT NULL,
            CONSTRAINT "guestBook_email_unique" UNIQUE("email")
        );

        ```

        Modify `vite.config.ts` to allow for top level awaits

        ```ts theme={null}
        import { reactRouter } from '@react-router/dev/vite';
        import tailwindcss from '@tailwindcss/vite';
        import { defineConfig } from 'vite';
        import tsconfigPaths from 'vite-tsconfig-paths';

        export default defineConfig(({ isSsrBuild }) => ({
          optimizeDeps: {
            esbuildOptions: {
              target: 'esnext',
            },
          },
          build: {
            target: 'esnext',
            rollupOptions: isSsrBuild
              ? {
                  input: './server/app.ts',
                }
              : undefined,
          },
          plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
        }));
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Add Nile to the server">
    Now we need to add the nile instance and route handlers to allow our server to respond to authentication, user, and tenant requests.

    <Tabs>
      <Tab title="CLI">
        ```bash Bash [expandable] theme={null}
        cat > app/nile.ts << 'EOF'
        import { Nile } from "@niledatabase/server";

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

        cat > app/routes/nile-api.ts << 'EOF'
        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 });
          }
        };
        EOF;

        cat > app/routes.ts << 'EOF'
        import { type RouteConfig, index, route } from "@react-router/dev/routes";

        export default [
          index("routes/home.tsx"),
          route("api/*", "routes/nile-api.ts"),
        ] satisfies RouteConfig;
        EOF

        ```
      </Tab>

      <Tab title="Manual">
        Create a file to house the main Nile instance. You can use this file to access nile from a central location any where in the app.

        `app/nile.ts`

        ```ts theme={null}
        import { Nile } from "@niledatabase/server";

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

        Create the API route file at `app/routes/nile-api.ts`. This file will handle different HTTP methods (GET, POST, PUT, DELETE) using the **Nile SDK**.

        ```ts theme={null}
        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 });
          }
        };
        ```

        This code handles different HTTP methods (`GET`, `POST`, `PUT`, `DELETE`) for the `/api/*` route and delegates the logic to Nile Database.

        Update your routes to respond to the api

        `app/routes.ts`

        ```ts theme={null}
        import { type RouteConfig, index, route } from '@react-router/dev/routes';

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

  <Step title="Add Client-Side Code for Authentication">
    You can use the components from `@niledatabase/react` to handle authentication. Replace the boilerplate of the main `_index.tsx` file with the following:

    This component will render:

    * **User info** and the **guest book** if the user is signed in.
    * **Sign-up form** if the user is not signed in.

    <Tabs>
      <Tab title="CLI">
        ```bash Bash [expandable] theme={null}
        cat > app/routes/home.tsx << 'EOF'
        import {
          SignedIn,
          SignedOut,
          SignOutButton,
          SignUpForm,
          UserInfo,
        } from "@niledatabase/react";

        import '@niledatabase/react/styles.css';
        import { database } from '~/database/context';
        import * as schema from '~/database/schema';

        import type { Route } from "./+types/home";
        import { Welcome } from "../welcome/welcome";

        export function meta({}: Route.MetaArgs) {
          return [
            { title: "New React Router App" },
            { name: "description", content: "Welcome to React Router!" },
          ];
        }

        export async function action({ request }: Route.ActionArgs) {
          const formData = await request.formData();
          let name = formData.get("name");
          let email = formData.get("email");
          if (typeof name !== "string" || typeof email !== "string") {
            return { guestBookError: "Name and email are required" };
          }

        name = name.trim();
        email = email.trim();
        if (!name || !email) {
        return { guestBookError: "Name and email are required" };
        }

        const db = database();
        try {
        await db.insert(schema.guestBook).values({ name, email });
        } catch (error) {
        return { guestBookError: "Error adding to guest book" };
        }
        }

        export async function loader({ context }: Route.LoaderArgs) {
          const db = database();

        const guestBook = await db.query.guestBook.findMany({
        columns: {
        id: true,
        name: true,
        },
        });

        return {
        guestBook,
        message: context.VALUE_FROM_EXPRESS,
        };
        }

        export default function Home({ actionData, loaderData }: Route.ComponentProps) {
          return (
            <div className="w-screen h-screen flex items-center justify-center flex-col">
              <SignedIn className="flex flex-col gap-4">
                <UserInfo />
                <SignOutButton />
                <Welcome
                  guestBook={loaderData.guestBook}
                  guestBookError={actionData?.guestBookError}
                  message={loaderData.message}
                />
              </SignedIn>
              <SignedOut>
                <SignUpForm />
              </SignedOut>
            </div>
          );
        }

        EOF

        ```
      </Tab>

      <Tab title="Manual">
        Update the render of `home.tsx` to use the following components:

        `app/routes/home.tsx`

        ```tsx theme={null}
        import { SignedIn, SignedOut, SignUpForm, UserInfo } from "@niledatabase/react";
        import "@niledatabase/react/styles.css";

        {/**rest of the actions/meta/loader*/}

        export default function Home({ actionData, loaderData }: Route.ComponentProps) {
          return (
            <SignedIn>
            <Welcome
              guestBook={loaderData.guestBook}
              guestBookError={actionData?.guestBookError}
              message={loaderData.message}
            />
            </SignedIn>
              <SignedOut>
                <SignUpForm>
              </SignedOut>
          );
        }

        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Running the Project">
    To run your project, execute the following:

    ```bash theme={null}
    npm run db:migrate
    npm run build
    npm run dev
    ```

    This will start the development server at `http://localhost:3000`, and you can test your API endpoints and authentication components.
  </Step>
</Steps>

## Summary

Now you can interact with your Nile Database through Remix API routes and manage authentication in your app!

## Related Topics

* [React Integration](/auth/frameworks/react)
* [Components](/auth/components/signin)
