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
37 changes: 37 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: "Test & Deploy to GitHub Pages"
on:
pull_request:
push:
branches: ['main']
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
- run: nix build
site:
runs-on: ubuntu-latest
needs: tests
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
- name: Build site
run: nix build .#site
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./result
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
12 changes: 0 additions & 12 deletions .github/workflows/test.yml

This file was deleted.

6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ src/gen/functions.json
src/gen/tables.json

.aider*
.envrc.secrets
.envrc.secrets

site/public/api
.next
site/out
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 ryanrasti
Copyright (c) 2025 Ryan Rasti

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
143 changes: 143 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Typegres

[![CI](https://github.com/ryanrasti/typegres/actions/workflows/main.yml/badge.svg)](https://github.com/ryanrasti/typegres/actions/workflows/main.yml)

> PostgreSQL, expressed in TypeScript

Typegres is a TypeScript library that brings the full power of PostgreSQL to TypeScript with complete type safety. Import the full power of Postgres as a TypeScript library.

## 🚧 Under Construction

This project is in active development. Documentation and features are still being built.

## What is Typegres?

While traditional ORMs and query builders abstract over multiple SQL dialects, Typegres goes all-in on PostgreSQL to provide the most powerful and type-safe experience possible. In a single import, you can access the full power of Postgres with complete TypeScript type safety.

## Key Features

- **All-in PostgreSQL**: Spend your time learning PostgreSQL, not a new abstraction
- **Type-Safe by Design**: Every PostgreSQL type and function with TypeScript safety
- **Iterate Fast**: Minimize context switching and get immediate feedback for your queries
- **Write Real SQL**: Use every PostgreSQL feature without limitations
- **Full Type Safety**: Catch errors at compile time, not runtime
- **No Magic**: Your queries compile to exactly the SQL you'd expect
- **Great DX**: Autocompletion, inline documentation, and type checking

## How It Works

Typegres automatically generates TypeScript types and function signatures from your PostgreSQL database schema. Every PostgreSQL type becomes a TypeScript class, and every PostgreSQL function gets proper type annotations.

```typescript
import { Text, Int4, Bool } from 'typegres'
import { values } from 'typegres/query'

// Create typed data with full PostgreSQL type safety
const users = values(
{ name: Text.new("Alice"), age: Int4.new(25), active: Bool.new(true) },
{ name: Text.new("Bob"), age: Int4.new(30), active: Bool.new(false) }
)

// Build type-safe queries that compile to efficient SQL
const result = await users
.select(u => ({
greeting: u.name.textcat(Text.new(" is ")).textcat(u.age.int4Out()),
isAdult: u.age.int4Gt(Int4.new(18))
}))
.where(u => u.active.boolEq(Bool.new(true)))
.execute()

// TypeScript knows the exact return type:
// result: Array<{ greeting: string; isAdult: boolean }>
```

## What Makes Typegres Different

### Complete PostgreSQL Fidelity
- Every PostgreSQL type, function, and operator is available
- Supports advanced features like window functions, CTEs, arrays, JSON operations
- No limitations - if PostgreSQL supports it, Typegres supports it

### Advanced Type System
- Sophisticated nullability tracking at the type level
- Support for PostgreSQL's complex type relationships (arrays, ranges, composite types)
- Automatic type inference for query results

### Code Generation Approach
- Automatically generates TypeScript definitions from your live PostgreSQL database
- Types always match your actual database capabilities
- Regenerate types when your schema changes

### Expression-Oriented Design
- Queries are built as composable expression trees
- Supports complex query logic through functional composition
- Built on top of Kysely for reliable SQL generation

## Development

### Prerequisites

- Node.js 20+
- PostgreSQL
- Nix (optional, for reproducible development environment)

### Getting Started

With Nix:
```bash
nix develop
```

Without Nix:
```bash
npm install
```

### Building

```bash
# Generate types from PostgreSQL
npm run codegen

# Build the library
npm run build

# Run tests
npm run test

# Type check
npm run typecheck

# Lint
npm run lint
```

### Nix Builds

```bash
# Build the main library
nix build .#typegres

# Build the documentation site
nix build .#site

# Run the test suite
nix build .#test
```

## Project Structure

- `src/` - Main library source code
- `src/gen/` - Auto-generated PostgreSQL types and functions
- `site/` - Documentation website and interactive playground
- `migrations/` - Database migrations for development

## Documentation

- [Quick Start Guide](./site/pages/docs/quickstart/+Page.tsx) (coming soon)
- [API Reference](./site) (coming soon)
- [Interactive Playground](./site/pages/playground/+Page.tsx)

## License

MIT © Ryan Rasti
114 changes: 90 additions & 24 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,21 @@
config.allowUnfree = true;
};
node = pkgs.nodejs_20;
in
{
devShells.default = pkgs.mkShell {
buildInputs = [ node pkgs.postgresql pkgs.jq pkgs.claude-code pkgs.nixfmt];
};
formatter = pkgs.nixpkgs-fmt;
packages.default =
buildModulesSite = pkgs.runCommand "node_modules" { } ''
mkdir $out; cd $out
cp ${./site/package.json} package.json
${pkgs.node2nix}/bin/node2nix -d -l ${./site/package-lock.json}
'';
generatedSite = (import buildModulesSite { pkgs = pkgs; nodejs = node; }).nodeDependencies.overrideAttrs (oldAttrs: {
# There's an issue with napi-postinstall that isn't marked executable when the first patchShebangs is run.
preRebuild = ''
echo "Patching shebangs in node_modules/napi-postinstall/lib" >&2
chmod +x node_modules/napi-postinstall/lib/cli.js
patchShebangs node_modules/napi-postinstall/lib/cli.js
echo "Patching shebangs done" >&2
'';
});
bootstrapped =
let
buildModules = pkgs.runCommand "node_modules" { } ''
mkdir $out; cd $out
Expand All @@ -27,34 +35,92 @@
'';
generated = import buildModules { pkgs = pkgs; nodejs = node; };

foo = pkgs.stdenv.mkDerivation {
name = "test";
buildInputs = [ pkgs.nodejs pkgs.postgresql pkgs.jq ];
src = pkgs.nix-gitignore.gitignoreSourcePure [ ./.gitignore ] ./.;
buildPhase = ''
bash ./start_postgres.sh
cp -rT --no-preserve=ownership ${generated.nodeDependencies}/lib/node_modules ./node_modules
chmod -R u+rw ./node_modules
export PATH=$PWD/node_modules/.bin:$PATH

mkdir $out
ln -s $out ./node_modules/.vitest
in
pkgs.stdenv.mkDerivation {
name = "bootstrapped.tar";
buildInputs = [ pkgs.nodejs pkgs.postgresql pkgs.jq ];
src = pkgs.nix-gitignore.gitignoreSourcePure [ ./.gitignore ] ./.;
buildPhase = ''
bash ./start_postgres.sh

psql -U postgres -h localhost -p 1234 < <(echo "create database test")
cp -rT --no-preserve=ownership ${generated.nodeDependencies}/lib/node_modules ./node_modules
chmod -R u+rw ./node_modules

bash ./src/gen/gen.sh
cp -rT --no-preserve=ownership ${generatedSite}/lib/node_modules ./site/node_modules
chmod -R u+rw ./site/node_modules

npm run typecheck
npm run lint
psql -U postgres -h localhost -p 1234 < <(echo "create database test")

export PATH=$PWD/node_modules/.bin:$PATH
bash ./src/gen/gen.sh

#ls -lrath ./node_modules/.bin >&2
#exit 1
'';

installPhase = ''
cur=$(pwd)
cd /tmp
mv $cur /tmp/source
tar -cf $out ./source
'';
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = [ node pkgs.postgresql pkgs.jq pkgs.claude-code pkgs.nixfmt ];
};
formatter = pkgs.nixpkgs-fmt;

packages = rec {
typegres =

pkgs.stdenv.mkDerivation {
name = "typegres";
buildInputs = [ pkgs.nodejs pkgs.postgresql pkgs.jq ];
src = bootstrapped;
buildPhase = ''
export PATH=$PWD/node_modules/.bin:$PATH
npm run build

npm run typecheck
npm run lint

bash ./start_postgres.sh
kysely --no-outdated-check migrate:up -e test

npm run test
'';

installPhase = ''
mkdir -p $out
cp -r ./dist $out/lib
cp package.json $out/lib/package.json
cp README.md $out/lib/README.md
cp LICENSE $out/lib/LICENSE
'';
};
in
foo;

site =
pkgs.stdenv.mkDerivation {
name = "typegres-site";
buildInputs = [ pkgs.nodejs ];
src = bootstrapped;
buildPhase = ''
cd ./site
export PATH=$PWD/node_modules/.bin:$PATH

npm run build
'';
installPhase = ''
mv ./out $out
'';
};

default = typegres;

};
}
);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"lint": "eslint",
"prepare": "true",
"format": "prettier --write \"**/*.{ts,js,json}\"",
"codegen": "./src/gen/gen.sh && ./src/gen/table-gen.sh",
"codegen": "bash ./src/gen/gen.sh && kysely --no-outdated-check migrate:up -e test && bash ./src/gen/table-gen.sh",
"clean": "(rm ./src/gen/functions.ts; rm -r ./src/gen/types; rm ./src/gen/tables.json; rm ./src/gen/tables.ts) || true",
"prepublishOnly": "npm run clean && npm run codegen && npm run typecheck && npm run lint && npm test && npm run build"
},
Expand Down
3 changes: 3 additions & 0 deletions site/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["next/core-web-vitals"]
}
2 changes: 0 additions & 2 deletions site/.gitignore

This file was deleted.

File renamed without changes.
Loading