alepha@docs:~/docs/guides/data$
cat 1-database-access.md
1 min read
Last commit:

#Database Access

We use Postgres (or SQLite for testing/local dev). We use Drizzle ORM under the hood because it's fast and typesafe. But we wrap it in Alepha primitives to make it seamless.

#Defining Entities

Instead of writing SQL or complex class mappers, you define an $entity. This acts as the source of truth for both your TypeScript types and your Database table structure.

typescript
 1import { t } from "alepha"; 2import { $entity, db } from "alepha/orm"; 3  4// src/entities/User.ts 5export const userEntity = $entity({ 6  name: "users", // The table name 7  schema: t.object({ 8    // db.primaryKey() handles UUID/Integer/BigInt generation automatically 9    id: db.primaryKey(),10 11    // Standard TypeBox types12    email: t.email(),13    name: t.text(),14 15    // Automatic timestamp management16    createdAt: db.createdAt(),17    updatedAt: db.updatedAt(),18  }),19  // Simple index definition20  indexes: ["email"],21});

#Using Repositories

To interact with the database, you inject a repository for that entity.

typescript
 1import { $repository } from "alepha/orm"; 2import { userEntity } from "./entities/User"; 3  4class UserService { 5  // This creates a type-safe repository for the userEntity 6  repo = $repository(userEntity); 7  8  async findByEmail(email: string) { 9    // .findOne, .findMany, .create, .update, .delete...10    return await this.repo.findOne({11      where: {12        email: { eq: email }13      }14    });15  }16 17  async listRecent() {18    // Pagination is built-in19    return await this.repo.paginate({20      page: 0,21      size: 20,22      sort: "-createdAt" // Descending sort23    });24  }25}

#Migrations

"But how do I create the table?"

Alepha integrates with Drizzle Kit. You don't need to manually write migration files for every little change during development.

  1. Dev Mode: When you run alepha dev, we check your $entity definitions against the database. If they differ, we (safely) suggest or apply changes to your development DB.
  2. Production: You generate migration files.
bash
# Check what changed
npx alepha db:generate

# Apply changes
npx alepha db:migrate

#Advanced: Queries & Transactions

Sometimes you need raw power.

#Transactions

Use the $transaction primitive to ensure atomicity.

typescript
 1import { $transaction } from "alepha/orm"; 2  3class BillingService { 4  process = $transaction({ 5    handler: async (tx, userId: string, amount: number) => { 6      // Pass { tx } to repository methods to use the transaction scope 7      await this.userRepo.updateById(userId, { status: 'paid' }, { tx }); 8      await this.invoiceRepo.create({ userId, amount }, { tx }); 9    }10  });11}

#Raw SQL

If the repository helper methods aren't enough, you can drop down to raw SQL while keeping some type safety.

typescript
1import { sql } from "alepha/orm";2 3await this.repo.query(sql`4  SELECT * FROM users WHERE age > 185`);
On This Page
No headings found...
ready
mainTypeScript
UTF-8guides_data_database_access.md