Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions .github/actions/fly-deploy/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@ inputs:
fly_api_token:
description: "Fly.io API token"
required: true
sentry_api_auth_token:
description: "Sentry API auth token"
required: false
sentry_api_project:
description: "Sentry API project"
required: false
sentry_org:
description: "Sentry organization"
required: false
github_sha:
description: "Git commit SHA for release tracking"
required: true

runs:
using: "composite"
Expand All @@ -33,9 +27,6 @@ runs:
--remote-only \
--dockerfile ${{ inputs.dockerfile }} \
--config ${{ inputs.fly_config }} \
--build-arg GITHUB_SHA=${{ github.sha }} \
--build-arg SENTRY_API_AUTH_TOKEN=${{ inputs.sentry_api_auth_token }} \
--build-arg SENTRY_API_PROJECT=${{ inputs.sentry_api_project }} \
--build-arg SENTRY_ORG=${{ inputs.sentry_org }}
--build-arg GITHUB_SHA=${{ inputs.github_sha }}
env:
FLY_API_TOKEN: ${{ inputs.fly_api_token }}
32 changes: 29 additions & 3 deletions .github/workflows/deploy-server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,37 @@ jobs:
concurrency: deploy-group
steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.5"

- uses: pnpm/action-setup@v4
with:
version: 9

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build with source maps
working-directory: apps/api
run: bun build --sourcemap --outdir dist src/index.ts

- name: Create Sentry release and upload source maps
working-directory: apps/api
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_API_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_API_PROJECT }}
run: |
npx @sentry/cli releases new ${{ github.sha }}
npx @sentry/cli releases set-commits ${{ github.sha }} --auto
npx @sentry/cli sourcemaps upload --release ${{ github.sha }} ./dist
npx @sentry/cli releases finalize ${{ github.sha }}

- uses: ./.github/actions/fly-deploy
with:
dockerfile: apps/api/Dockerfile
fly_config: apps/api/fly.toml
fly_api_token: ${{ secrets.FLY_BAHAR_API_TOKEN }}
sentry_api_auth_token: ${{ secrets.SENTRY_API_AUTH_TOKEN }}
sentry_api_project: ${{ secrets.SENTRY_API_PROJECT }}
sentry_org: ${{ secrets.SENTRY_ORG }}
github_sha: ${{ github.sha }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ coverage
# vite
vite.config.ts.timestamp-*

# bun api binaries
server
migrate

# next.js
.next/
out/
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Bahar is an Arabic language learning application with these key features:

### Backend

- Node.js API with Express and tRPC for type-safe APIs
- Bun runtime with Elysia framework
- Type-safe API client: Eden Treaty
- Authentication: Better Auth with email OTP and OAuth support
- Logging: Pino for structured logging, Sentry for error tracking

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ React Native app with Expo:

#### API (`apps/api`)

Node.js backend:
Bun backend:

- **Framework**: Express, tRPC
- **Runtime**: Bun
- **Framework**: Elysia
- **Database**: Turso (central + per-user SQLite databases)
- **ORM**: Drizzle
- **Auth**: Better Auth (email OTP, OAuth)
Expand Down
89 changes: 42 additions & 47 deletions apps/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-slim as base
ARG BUN_VERSION=1.3.5
FROM oven/bun:${BUN_VERSION}-slim AS base

LABEL fly_launch_runtime="Node.js"
LABEL fly_launch_runtime="Bun"

ARG PORT

ARG SENTRY_API_AUTH_TOKEN
ARG SENTRY_API_PROJECT
ARG SENTRY_ORG
ARG GITHUB_SHA

ENV PNPM_HOME="/pnpm"
Expand All @@ -17,68 +12,68 @@ WORKDIR /app

FROM base AS prune

RUN npm install -g pnpm
RUN pnpm add -g turbo
RUN bun add -g pnpm turbo

COPY . .

RUN turbo prune api --docker

FROM base AS development

# Add certificates so we can call Sentry's API
RUN apt-get update && apt-get install -y ca-certificates
FROM base AS build

RUN npm install -g pnpm
RUN pnpm add -g turbo
RUN bun add -g pnpm turbo

# First install the dependencies (as they change less often)
COPY .gitignore .gitignore
COPY --from=prune /app/out/json/ .
COPY --from=prune /app/out/pnpm-lock.yaml ./pnpm-lock.yaml

RUN pnpm install --frozen-lockfile
RUN pnpm config set store-dir /pnpm/store
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile

# Build the project
# Copy full source
COPY --from=prune /app/out/full/ .

# Environment variables needed for Sentry esbuild plugin
# which will create a new release and upload sourcemaps
ENV SENTRY_API_AUTH_TOKEN=$SENTRY_API_AUTH_TOKEN
ENV SENTRY_API_PROJECT=$SENTRY_API_PROJECT
ENV SENTRY_ORG=$SENTRY_ORG
ENV GITHUB_SHA=$GITHUB_SHA
WORKDIR /app/apps/api

RUN pnpm turbo run build --filter=api
ENV NODE_ENV=production

FROM base AS production
# Compile server binary
RUN bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--external @libsql/linux-x64-gnu \
--outfile server \
src/index.ts

RUN npm install -g pnpm
RUN pnpm add -g turbo
# Compile migrate binary
RUN bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--external @libsql/linux-x64-gnu \
--outfile migrate \
src/db/migrate.ts

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
FROM debian:12-slim

COPY .gitignore .gitignore
COPY --from=prune /app/out/json/ .
COPY --from=prune /app/out/pnpm-lock.yaml ./pnpm-lock.yaml

RUN pnpm install --prod --frozen-lockfile

COPY --from=development /app/apps/api/dist /app/apps/api/dist
COPY --from=development /app/apps/api/entrypoint.sh ./
WORKDIR /app

COPY --from=prune /app/turbo.json /app/turbo.json
# Copy compiled binaries
COPY --from=build /app/apps/api/server server
COPY --from=build /app/apps/api/migrate migrate

# Needed for migrations
COPY --from=prune /app/apps/api/drizzle /app/apps/api/drizzle
COPY --from=prune /app/apps/api/instrument.mjs /app/apps/api/instrument.mjs
COPY --from=prune /app/apps/api/src/*.json /app/apps/api/dist/
# Copy native libsql module (required at runtime)
COPY --from=build /app/node_modules/@libsql/linux-x64-gnu ./node_modules/@libsql/linux-x64-gnu

RUN chmod +x /app/entrypoint.sh
# Copy drizzle migrations folder (needed at runtime)
COPY --from=build /app/apps/api/drizzle drizzle

WORKDIR /app/apps/api
ENV NODE_ENV=production
ARG GITHUB_SHA
ENV GITHUB_SHA=$GITHUB_SHA

EXPOSE $PORT
EXPOSE 3000

ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["./server"]
49 changes: 43 additions & 6 deletions apps/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ The backend API for Bahar - an Arabic language learning platform.

## Tech Stack

- **Framework**: Express with tRPC for type-safe APIs
- **Runtime**: Bun
- **Framework**: Elysia
- **Type-safe Client**: Eden Treaty
- **Database**: Turso (LibSQL) - central database + per-user databases
- **ORM**: Drizzle ORM
- **Auth**: Better Auth (email OTP, OAuth)
Expand Down Expand Up @@ -41,7 +43,7 @@ When adding migrations, SQL statements should be separated by newlines.

### Prerequisites

- Node.js >= 18
- Bun >= 1.3
- pnpm
- Turso CLI (for local development)

Expand Down Expand Up @@ -145,18 +147,53 @@ npx tsx --env-file=.env scripts/<script-name>.ts

## Building

### Binary (Local)

Build standalone Bun binaries:

```bash
pnpm run build
# Build server binary
cd apps/api
pnpm run build:binary

# Build migrate binary
bun build --compile --minify-whitespace --minify-syntax \
--external @libsql/linux-x64-gnu \
--outfile migrate src/db/migrate.ts
```

The built output is in `/dist`.
Run the binaries (must have `node_modules/@libsql/linux-x64-gnu` available):

## Running in Production
```bash
# Run migrations
./migrate

# Start server
./server
```

**Note:** The `@libsql/linux-x64-gnu` native module must be in `node_modules/` - it's marked as external because Bun can't bundle native modules.

### Docker

Build the Docker image:

```bash
docker build -t bahar-api -f apps/api/Dockerfile .
```

Run the container:

```bash
pnpm start
docker run --network host -p 3000:3000 --env-file apps/api/.env bahar-api
```

**Important:** When using `--env-file`, values should NOT have quotes around them:

## Running in Production

The Docker container runs the compiled `server` binary. Migrations run automatically via Fly.io's `release_command` before deployment.

## Environment Variables

Required environment variables:
Expand Down
29 changes: 0 additions & 29 deletions apps/api/build.mjs

This file was deleted.

7 changes: 0 additions & 7 deletions apps/api/entrypoint.sh

This file was deleted.

3 changes: 3 additions & 0 deletions apps/api/fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ primary_region = 'yyz'

[build]

[deploy]
release_command = "./migrate"

[http_service]
internal_port = 3000
force_https = true
Expand Down
22 changes: 0 additions & 22 deletions apps/api/instrument.mjs

This file was deleted.

Loading