## Introduction

[Elysia](https://elysiajs.com/) is an ergonomic web framework for building high-performance backend servers with Bun. It offers end-to-end type safety, an expressive API, and exceptional performance. Combined with Prisma ORM and [Prisma Postgres](https://www.prisma.io/postgres), you get a fast, type-safe backend stack.

In this guide, you'll learn to integrate Prisma ORM with a Prisma Postgres database in an Elysia application. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/elysia).

## Prerequisites

- [Bun](https://bun.sh/docs/installation) installed on your system

## 1. Set up your project

Create a new Elysia project using the Bun scaffolding command:

```terminal
bun create elysia elysia-prisma
```

Navigate to the project directory:

```terminal
cd elysia-prisma
```

## 2. Install and configure Prisma

### 2.1. Install dependencies

Install the required Prisma packages, database adapter, and Prismabox (for generated TypeBox schemas):

```terminal
bun add -d prisma bun-types
bun add @prisma/client @prisma/adapter-pg pg prismabox
```

:::info

If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of `@prisma/adapter-pg`. For more information, see [Database drivers](/orm/overview/databases/database-drivers).

:::

Once installed, initialize Prisma in your project:

```terminal
bunx --bun prisma init --db --output ../src/generated/prisma
```

:::info
You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My Elysia Project"
:::

This will create:

- A `prisma/` directory with a `schema.prisma` file
- A `prisma.config.ts` file for configuring Prisma
- A `.env` file with a `DATABASE_URL` already set

### 2.2. Define your Prisma Schema

In the `prisma/schema.prisma` file, mirror the example app structure with Prismabox TypeBox generators and a `Todo` model:

```prisma file=prisma/schema.prisma
generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}

generator prismabox {
  provider                    = "prismabox"
  typeboxImportDependencyName = "elysia"
  typeboxImportVariableName   = "t"
  inputModel                  = true
  output                      = "../src/generated/prismabox"
}

datasource db {
  provider = "postgresql"
}

//add-start
model Todo {
  id        Int      @id @default(autoincrement())
  title     String
  completed Boolean  @default(false)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
//add-end
```

This matches the Prisma Elysia example: it generates Prisma Client to `src/generated/prisma` and Prismabox TypeBox schemas to `src/generated/prismabox`.

#### What is Prismabox?

- A Prisma generator that reads your Prisma schema and emits Elysia-friendly TypeBox models.
- Generates files like `src/generated/prismabox/Todo.ts` (and one per model) with `TodoPlain`, `TodoPlainInputCreate`, etc.
- Use those generated models in routes to validate requests/responses and keep Elysia types in sync with your Prisma schema (also useful for OpenAPI/Eden).

### 2.3. Run migrations and generate Prisma Client

Run the following commands to create the database tables and generate the Prisma Client:

```terminal
bunx --bun prisma migrate dev --name init
bunx --bun prisma generate
```

### 2.4. Seed the database

Add some seed data to populate the database with sample todos (mirrors the example repo).

Create a new file called `seed.ts` in the `prisma/` directory:

```typescript file=prisma/seed.ts
import { PrismaClient } from "../src/generated/prisma/client.js";
import { PrismaPg } from "@prisma/adapter-pg";

if (!process.env.DATABASE_URL) {
  throw new Error("DATABASE_URL is not set");
}

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
const prisma = new PrismaClient({ adapter });

const todoData = [
  { title: "Learn Elysia" },
  { title: "Learn Prisma" },
  { title: "Build something awesome", completed: true },
];

async function main() {
  console.log("Start seeding...");
  for (const todo of todoData) {
    const created = await prisma.todo.create({
      data: todo,
    });
    console.log(`Created todo with id: ${created.id}`);
  }
  console.log("Seeding finished.");
}

main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });
```

Run the seed script:

```terminal
bunx --bun prisma db seed
```

And open Prisma Studio to inspect your data:

```terminal
bunx --bun prisma studio
```

## 3. Integrate Prisma into Elysia

### 3.1. Create a Prisma Client instance

Inside the `src/` directory, create a `lib` directory with a `prisma.ts` file. This file will create and export your Prisma Client instance and add the following code:

```typescript file=src/lib/prisma.ts
import { PrismaClient } from "../generated/prisma/client.js";
import { PrismaPg } from "@prisma/adapter-pg";

const databaseUrl = process.env.DATABASE_URL;

if (!databaseUrl) {
  throw new Error("DATABASE_URL is not set");
}

const adapter = new PrismaPg({ connectionString: databaseUrl });
export const prisma = new PrismaClient({ adapter });
```
### 3.2. Create API routes

Update your `src/index.ts` file to match the Prisma Elysia example, including Prismabox-generated validation types:

```typescript file=src/index.ts
import { Elysia, t } from "elysia";
import { prisma } from "./lib/prisma";
import {
  TodoPlain,
  TodoPlainInputCreate,
  TodoPlainInputUpdate,
} from "./generated/prismabox/Todo";

const app = new Elysia()
  // Health check
  .get("/", () => {
    return { message: "Hello Elysia with Prisma!" };
  })

  // Get all todos
  .get(
    "/todos",
    async () => {
      const todos = await prisma.todo.findMany({
        orderBy: { createdAt: "desc" },
      });
      return todos;
    },
    {
      response: t.Array(TodoPlain),
    }
  )

  // Get a single todo by ID
  .get(
    "/todos/:id",
    async ({ params, set }) => {
      const id = Number(params.id);
      const todo = await prisma.todo.findUnique({
        where: { id },
      });

      if (!todo) {
        set.status = 404;
        return { error: "Todo not found" };
      }

      return todo;
    },
    {
      params: t.Object({
        id: t.Numeric(),
      }),
      response: {
        200: TodoPlain,
        404: t.Object({
          error: t.String(),
        }),
      },
    }
  )

  // Create a new todo
  .post(
    "/todos",
    async ({ body }) => {
      const todo = await prisma.todo.create({
        data: {
          title: body.title,
        },
      });
      return todo;
    },
    {
      body: TodoPlainInputCreate,
      response: TodoPlain,
    }
  )

  // Update a todo
  .put(
    "/todos/:id",
    async ({ params, body, set }) => {
      const id = Number(params.id);

      try {
        const todo = await prisma.todo.update({
          where: { id },
          data: {
            title: body.title,
            completed: body.completed,
          },
        });
        return todo;
      } catch {
        set.status = 404;
        return { error: "Todo not found" };
      }
    },
    {
      params: t.Object({
        id: t.Numeric(),
      }),
      body: TodoPlainInputUpdate,
      response: {
        200: TodoPlain,
        404: t.Object({
          error: t.String(),
        }),
      },
    }
  )

  // Toggle todo completion
  .patch(
    "/todos/:id/toggle",
    async ({ params, set }) => {
      const id = Number(params.id);

      try {
        const todo = await prisma.todo.findUnique({
          where: { id },
        });

        if (!todo) {
          set.status = 404;
          return { error: "Todo not found" };
        }

        const updated = await prisma.todo.update({
          where: { id },
          data: { completed: !todo.completed },
        });

        return updated;
      } catch {
        set.status = 404;
        return { error: "Todo not found" };
      }
    },
    {
      params: t.Object({
        id: t.Numeric(),
      }),
      response: {
        200: TodoPlain,
        404: t.Object({
          error: t.String(),
        }),
      },
    }
  )

  // Delete a todo
  .delete(
    "/todos/:id",
    async ({ params, set }) => {
      const id = Number(params.id);

      try {
        const todo = await prisma.todo.delete({
          where: { id },
        });
        return todo;
      } catch {
        set.status = 404;
        return { error: "Todo not found" };
      }
    },
    {
      params: t.Object({
        id: t.Numeric(),
      }),
      response: {
        200: TodoPlain,
        404: t.Object({
          error: t.String(),
        }),
      },
    }
  )

  .listen(3000);

console.log(
  `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
);
```

This creates the same endpoints as the official example:

- `GET /` - Health check
- `GET /todos` - Fetch all todos (newest first)
- `GET /todos/:id` - Fetch a single todo
- `POST /todos` - Create a todo
- `PUT /todos/:id` - Update a todo
- `PATCH /todos/:id/toggle` - Toggle completion
- `DELETE /todos/:id` - Delete a todo

Prismabox generates the `TodoPlain`/`TodoPlainInput*` TypeBox schemas so responses and request bodies are validated and typed.

### 3.3. Run the application

Start your Elysia server:

```terminal
bun run dev
```

You should see `🦊 Elysia is running at localhost:3000` in the console.

### 3.4. Test the API

Test the endpoints using `curl`:

```terminal
# Health check
curl http://localhost:3000/ | jq

# Get all todos
curl http://localhost:3000/todos | jq

# Get a single todo
curl http://localhost:3000/todos/1 | jq

# Create a new todo
curl -X POST http://localhost:3000/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "Ship the Prisma + Elysia guide"}' | jq

# Toggle completion
curl -X PATCH http://localhost:3000/todos/1/toggle | jq

# Update a todo
curl -X PUT http://localhost:3000/todos/1 \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated title", "completed": true}' | jq

# Delete a todo
curl -X DELETE http://localhost:3000/todos/1 | jq
```

You're done! You've created an Elysia app with Prisma that's connected to a Prisma Postgres database.

## Next steps

Now that you have a working Elysia app connected to a Prisma Postgres database, you can:

- Extend your Prisma schema with more models and relationships
- Add update and delete endpoints
- Explore authentication with [Elysia plugins](https://elysiajs.com/plugins/overview.html)
- Enable query caching with [Prisma Postgres](/postgres/database/caching) for better performance
- Use [Eden](https://elysiajs.com/eden/overview.html) for end-to-end type-safe API calls

### More info

- [Prisma Documentation](/orm/overview/introduction)
- [Elysia Documentation](https://elysiajs.com/)
- [Elysia with Prisma Guide](https://elysiajs.com/integrations/prisma.html)


---

# How to use the Management API with API Clients