| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-03-25 | 27.2 kB | |
| v1.5.0 source code.tar.gz | 2026-03-25 | 44.6 MB | |
| v1.5.0 source code.zip | 2026-03-25 | 45.0 MB | |
| Totals: 3 Items | 89.6 MB | 0 | |
IHP is a modern batteries-included web framework, built on top of Haskell and Nix. Blazing fast, secure, easy to refactor and the best developer experience with everything you need - from prototype to production.
v1.5.0 is the largest IHP release ever: 1,051 commits, a full database layer rewrite, major performance improvements, and 15+ modules extracted into standalone packages.
Highlights
- :zap: Hasql Migration: Entire database layer rewritten from postgresql-simple to hasql — up to 2x lower DB query latency, prepared statements, and pipeline support
- :rocket: Performance: 3x faster session middleware, 5x faster URL generation, 2x faster render pipeline, GHCi memory reduced from 4GB to 500MB
- :mag: Typed SQL: New
ihp-typed-sqlpackage — compile-time type-checked SQL with automatic JOIN nullability inference - :package: Modular Architecture: 15+ modules extracted into standalone Hackage packages (ihp-mail, ihp-datasync, ihp-schema-compiler, and more)
- :fast_forward: Pipeline Queries:
fetchPipelinedbatches multiple database queries in a single PostgreSQL round-trip - :key: Composite Primary Keys: Tables with composite PKs now generate correct types and HasField instances
- :globe_with_meridians: Custom Routes:
customRoutesandcustomPathTofor clean URLs alongside AutoRoute - :gear: GHC 9.10: Default compiler upgraded to GHC 9.10, with experimental GHC 9.12 support
:arrow_up: Upgrading? See the Upgrade Guide for step-by-step migration instructions.
Major Changes
Hasql Database Migration
The entire database access layer has been migrated from postgresql-simple to hasql, delivering up to 2x lower DB query latency for production web servers. Hasql uses prepared statements, binary protocol encoding, and supports PostgreSQL pipeline mode. The query builder API is unchanged — existing query @User |> filterWhere (#active, True) |> fetch code works exactly as before. Only applications using raw postgresql-simple imports directly need to migrate to the query builder or the new typedSql quasiquoter.
Typed SQL (ihp-typed-sql)
The new typedSql quasiquoter connects to your development database at compile time to infer column types, detect nullable columns from LEFT JOINs, and validate parameter types:
:::haskell
-- Types inferred at compile time — LEFT JOIN columns automatically become Maybe
users <- [typedSql|
SELECT users.name, orders.total
FROM users
LEFT JOIN orders ON orders.user_id = users.id
|] -- inferred: TypedQuery (Text, Maybe Double)
Compile-time errors catch type mismatches, missing columns, and parameter count issues before your code runs.
Pipeline Queries (fetchPipelined)
Batch multiple independent database queries into a single PostgreSQL round-trip using pipeline mode:
:::haskell
(allItems, activeItems, count) <- pipeline do
allItems <- query @Item |> fetchPipelined
activeItems <- query @Item |> filterWhere (#active, True) |> fetchPipelined
count <- query @Item |> fetchCountPipelined
pure (allItems, activeItems, count)
-- All three queries execute in a single network round-trip
Custom Routes
Override individual AutoRoute actions with clean URLs while keeping the rest auto-generated:
:::haskell
instance AutoRoute PostsController where
customRoutes = do
string "/posts/"
postId <- parseId
endOfInput
onlyAllowMethods [GET, HEAD]
pure ShowPostAction { postId }
customPathTo ShowPostAction { postId } = Just ("/posts/" <> tshow postId)
customPathTo _ = Nothing -- Fall back to AutoRoute for other actions
Performance
Significant performance improvements across the request lifecycle:
| Area | Improvement | PR |
|---|---|---|
| DB query latency (hasql) | up to 2x lower | #2262 |
| Session middleware | 2.9-3.1x throughput (skip AES when session unused) | #2328 |
URL generation (pathTo) |
5.3x faster | #2335 |
| Render pipeline | 2x faster (INLINE fix) | #2333 |
| GHCi dev server memory | 4GB → 500MB | #2543 |
| HSX compilation | Static subtrees precomputed at compile time | #2248 |
The session middleware optimizations are published as standalone Hackage packages: wai-session-maybe and wai-session-clientsession-deferred.
Full Changelog
Breaking Changes
- Removed
RequestContext, replaced with WAI request vault and?respondparameter (#2215) - Added
?request :: Requestimplicit parameter alongside?context(#2218) - Moved
ActionTypeto WAI request vault (#2225) - Removed legacy
addStylefunction — use<style>tags in HSX instead - Switched
toSlugto use thesluggerpackage (#2153) - Migrated Bootstrap CSS classes from v4 to v5 conventions (
ml-*→ms-*,sr-only→visually-hidden, etc.) (#2242) - Migrated Bootstrap data attributes (
data-toggle→data-bs-toggle, etc.) - Added
-Werror=incomplete-patternsacross all packages (#2260) - Deprecated Makefile targets — use
nix buildinstead (#2170) - Replaced
ApplicationContextwith WAI request vault for AutoRefresh (#2149) - Used
OsPathinstead ofFilePathacross all packages (#2246) requestBodyJSONnow returnsIO Aeson.Valueinstead ofAeson.Value; update call sites to bind it indo-notation (#2396)- Migrated from
postgresql-simpletohasqlfor all database access — applications usingDatabase.PostgreSQL.Simpledirectly need to migrate to IHP's query builder ortypedSql touchedFieldschanged from[Text]toIntegerbitmask for better performance (#2473)CSSFrameworkDefaultinstance removed — useunstyledinstead ofdef; newstyledLabelClassfield added to the recordrequestBodyJSONnow returns HTTP 400 for malformed JSON instead of crashing- Session packages replaced:
wai-session→wai-session-maybe(Network.Wai.Session→Network.Wai.Session.Maybe),wai-session-clientsession→wai-session-clientsession-deferred(Network.Wai.Session.ClientSession→Network.Wai.Session.ClientSession.Deferred) (#2582)
New Features
customRoutesandcustomPathTofor overriding individual AutoRoute actions (#2236)- Static route shortcut via
IHP.Staticmodule (#2237) filterWhereGreaterThan,filterWhereLessThan,filterWhereAtLeast,filterWhereAtMostcomparison filter functions (#2178)filterWhereCaseInsensitiveJoinedTable(#2166)- Improved code generators with type-aware scaffolding (#2240)
compileRelationSupportflag to optionally disable relation type machinery for faster compilation (#2255)- Persistent socket for seamless app restarts during development (#2210)
- DataSync: append-only deltas for streaming text updates (#2265)
- DataSync:
columnsparameter forqueryfunction - SSC: proper error handling with
SSCErrortype, reconnection support, production-ready documentation (#2224) Fixtures.sqlis now optional (#2235)nix flake check --impuresupport for IHP projects (#2146)autocapitalizeattribute support in HSX (#2176)EnvVarReaderinstance forSMTPEncryption(#2214)- NixOS:
sessionSecretFileoption andappKeygenservice - Migrations made optional in deployment configuration
- Typed SQL (
ihp-typed-sqlpackage) — type-safe SQL queries with automatic decoder inference and JOIN nullability detection (#2304) fetchPipelinedfor batching multiple database queries in a single round-trip using PostgreSQL pipeline mode (#2459)- Composite primary key support — tables with composite PKs now generate correct types and HasField instances (#992, #2553)
- PostgreSQL table inheritance (INHERITS) support in schema designer (#2505)
- SECURITY DEFINER support for SQL functions (#2504)
- Multiple trigger events support (e.g. INSERT OR UPDATE) (#2508)
- Integration test support with automatic temporary PostgreSQL database (#2510)
withIHPApppublic API for testing (#2314)runDevScriptfor easy GHCi script execution (#2539)devHaskellPackagesoption for dev-only Haskell dependencies (#2529)- Docker worker images for background job runners (#2541)
- IDE logs viewer with devenv service tabs (#2499)
- App selector in Generate Controller preview from schema designer (#2509)
- Deselectable actions in controller generator (#2511)
- HSX wildcard pattern support (#1852, #2506)
- Type applications in HSX splices (#2321)
- Hoogle documentation enabled by default (#2512)
- Allow overriding nixpkgs config (#2495)
- Separate
nixpkgs-nixosflake input for pinning NixOS deployment nixpkgs independently from Haskell package nixpkgs (#2519)
Performance
- Precompute static HSX subtrees at compile time (#2248)
ByteString.BuilderintoSQL'to reduce allocations (#2244)- ByteString parsing for route params — no Text roundtrip (#2241)
Textinstead ofStringinpathTo(#2226)- Serve
/static/requests without middleware (#2237) - Skip request body parsing for GET/HEAD/DELETE/OPTIONS requests
- Eliminate double conversion in
respondHtml - Split
Generated.ActualTypesinto per-table modules for parallel compilation (#2254) - Eliminate
callCabal2nixIFD for faster Nix evaluation (#2251, #2253) - Replace ClassyPrelude with lightweight specific imports (57% faster ControllerSupport compilation) (#2241)
- Split
IHP.ModelSupport,IHP.FrameworkConfig,IHP.HaskellSupport,IHP.CSSFrameworkinto sub-modules for faster compilation - Refactor
QueryBuilderinto sub-modules for faster compilation - Add
IHP.IDE.Preludefor faster IDE controller compilation - Optimize
IHP.Breadcrumb.Typescompilation by removing heavy imports - Disable profiling and haddock for faster local builds (#2206)
- Disable Haddock for generated models package
- Use
pg_isreadyfor faster Postgres startup polling (#2208) - Improve HSX parser performance and code generation
- Hoist
actionPrefixTextcomputation out of per-constructor loop - Break serial compilation dependencies for better Nix parallelism (#2252)
- Build jobs and web server concurrently in
nix build - Optimize AutoRoute URL generation (
pathTo) by 2.3x (#2335) - HashMap-based dispatch for AutoRoute routing
- Optimize per-request latency: flash messages + Accept header (#2329)
- Lazy session middleware: skip decrypt/encrypt when session is unused (#2328, #2582) — published as
wai-session-maybeandwai-session-clientsession-deferredon Hackage (2.9-3.1x throughput improvement for routes that don't access the session) - INLINE pragmas on render hot-path functions (#2333)
- Drop INLINE pragmas from fetch/query compilation chain to reduce Core bloat (#2522)
- Use record update syntax for SetField/UpdateField codegen (#2476)
- Replace Snippet with direct Hasql.Statement compilation (#2435)
- Reduce GHCi dev server memory from ~4GB to ~500-800MB (#2543)
- Mark
actionPrefixTextNOINLINE to reduce code bloat in views (#2516) - Tune GC settings for dev server to reduce request latency spikes (#2323)
Package Extractions
The following modules have been extracted into standalone packages:
| Module | New Package |
|---|---|
IHP.Mail |
ihp-mail |
IHP.SchemaCompiler |
ihp-schema-compiler |
IHP.IDE.SchemaDesigner.Parser |
ihp-postgres-parser |
IHP.Controller.Param (request params) |
wai-request-params |
IHP.FlashMessages.* |
wai-flash-messages |
IHP.Assets |
wai-asset-path |
IHP.FileStorage.Preprocessor.ImageMagick |
ihp-imagemagick |
IHP.Job.Dashboard.* |
ihp-job-dashboard |
IHP.DataSync.* |
ihp-datasync |
IHP.Welcome.Controller |
ihp-welcome |
IHP.ControllerContext |
ihp-context |
IHP.PageHead.* |
ihp-pagehead |
IHP.Log.* |
ihp-log |
IHP.Modal.* |
ihp-modal |
IHP.PGListener |
ihp-pglistener |
All extracted modules are still re-exported from ihp for backwards compatibility.
Frontend Dependency Upgrades
- jQuery 3.6.0 → 4.0.0 (with 3.7.1 as intermediate step)
- Bootstrap 5.2.1 → 5.3.8
- Select2 4.0.13 → 4.1.0-rc.0
- Old jQuery versions kept in
ihp-staticfor backwards compatibility - Lodash (DataSync) 4.17.21 → 4.17.23
Bug Fixes
- Fix flash messages by using current request in
renderHtml(#2232) - Fix
<<loop>>error caused by lazy evaluation of TMap afterputContext - Fix context freezing prematurely in render function
- Fix HTML5 required validation for date and datetime fields (#2182)
- Fix
ConversionFailederror for tables with generated columns (#2179) - Fix WebSocket
onPingnot called (#2138) - Fix DevServer stdout/stderr buffering (#2137)
- Fix
run-scriptnot working / multiple loads of the application (#2141) - Fix deploy script migrate service check
- Fix incomplete pattern matches in MigrationGenerator for PostgreSQL 17 compatibility
- Fix missing
NoRenderCommentNodepattern innodeOuterHtml - Fix type equality operator warning by re-exporting
(~)from Prelude - Fix
hsDataDirpicking doc directory on x86_64-linux - Fix
make dbfailing - Fix AutoRefresh and DataSync breaking after
make dbrecreates the database (#2295) - Fix crash in ToolServer
- Fix missing
UrlInputpattern in Bootstrap CSS framework - Use
modelContextMiddlewareto populate request vault in test helpers (#2174) - Drop
Default Boolinstance for data-default >= 0.8 - Use
Data.Textqualified inIHP.NameSupportto fix build with text-2.1.2 - Fix
uriToStringinstead ofshowfor URI serialization - Fix duplicate Cabal modules from nested Generated subdirectories
- Fix AutoRefresh losing layout after vault migration (#2337)
- Fix AutoRefresh losing query parameters during re-render (#2401)
- Fix stale AutoRefresh response comparisons (#2546)
- AutoRefresh gracefully degrades without PGListener (#2465)
- Fix DataSync append optimization double-applying (#2544)
- Fix DataSync trigger installation deadlock (#2468)
- Fix HasqlDecodeColumn Int using int8 instead of int4 (#2493)
- Fix
filterWherewithNothinggenerating invalidIS $Ninstead ofIS NULL - Fix RowDecoder generating nullable decoder for PRIMARY KEY columns (#2540)
- Fix controller generator producing invalid Id fields when table doesn't exist (#1179, #2559)
- Fix duplicate HasField "id" when table has composite PK and id column (#989, #2557)
- Fix foreign key non-PK column type (#2558)
- Fix modal close button not working (#2561)
- Fix DataSync trigger installation causing AccessExclusiveLock on every server restart (#2580)
- Fix DataSubscription crash during WebSocket reconnect (#2576)
- Fix Flatpickr shifting datetime values by user's timezone offset on every edit (#2570)
- Fix Hoogle not loading in Safari due to CSP headers on localhost (#2567)
- Fix Cabal 3.12+ aborting configure due to duplicate GHC2021 in default-extensions (#2572)
- Fix IDE Data Editor foreign key dropdown flickering (#2489)
- Fix IDE toolbar help popover not opening (#2494)
- Fix devenv up Ctrl+C leaving orphan processes (#2527, #2548)
- Fix devenv up Ctrl+C not stopping postgres (#2485)
- Fix lazy IO crash in FileWatcher filterGitIgnored (#2330)
- Suppress
-Wambiguous-fieldswarnings in generated types (#2487) - Render hasql PostgreSQL errors with structured detail in dev mode (#2399)
- Fix job worker silently exiting on transient fetchNextJob error (#2453)
DataSync JavaScript Fixes
- Fix
slice()→splice()so optimistic record IDs are actually removed (#2263) - Guard
splice()againstindexOfreturning -1 - Handle null/undefined value in
whereTextSearchStartsWith - Fix UUID fallback variant bits by parsing hex digit properly
- Use strict equality for
OpEqual/OpNotEqualinrecordMatchesQuery - Await recursive
close()so callers wait for actual cleanup - Initialize headers in
fetchAuthenticatedif missing - Add error handling to
useCount'ssendMessagecall - Retry reconnection after failure instead of giving up
- Log error when reconnect fails instead of silently swallowing it
- Omit empty
changeSet/appendSetfromDidUpdateJSON - Fix memory leaks when connection is overloaded
Developer Experience
- Improved HSX parser error messages with suggestions (#2238)
- Full middleware stack in test requests (#2228)
- Updated documentation for Bootstrap 5 (#2193)
- Updated deployment guide to match modular config structure (#2257)
- Simplified installation guide for Determinate Nix
redirectnow uses 303 See Other for auth redirects (#2199)- Set line buffering for stdout and stderr in DevServer
- Use default GHC from nixpkgs instead of pinned version
- i18n documentation (#2503)
- Pagination guide (#2551)
- Explain side-effect actions need forms, not links (#2552)
- Docker migration-before-start pattern documentation (#2555)
- Comprehensive documentation improvements for beginners: security, flash messages, JSON API, production checklist, and 10+ other guides (#2577)
- Passkeys (WebAuthn) authentication guide (#2574)
Job Queue
- Job worker redesign for reliability and performance (#2327)
- Dev-only job poller trigger self-heal (#2470)
- Decouple job queue from ModelContext by passing HasqlPool explicitly
- IHP.Job refactored into focused submodules (#2471)
DevEnv Updates
- devenv v1.8.2 → v1.10 → v1.11.2 → v2.0.2 → v2.0.6 (#2475, #2568)
- Switch devenv to process-compose process manager (#2566)
- Use devenv postgres instead of IHP's built-in postgres
- Add binary cache to flake
- Improve caching of nix builds