v0.19.0
A new IHP release, containing bug fixes and productivity improvements to existing features π οΈ
IHP is a modern batteries-included haskell 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.
Major Changes
-
π» Apple M1 Support:
This release finally adds support for Apple M1 devices. So we all can get new macbooks now π -
π Major DataSync Improvements:
We've implemented a lot of improvements to IHP DataSync:- Limits and Offsets
- Transactons using
withTransaction
:import { withTransaction } from 'ihp-datasync'; await withTransaction(async transaction => { const team = await transaction.createRecord('teams', { title: 'New Team' }); const project = await transaction.createRecord('projects', { title: 'Project 1', teamId: team.id }); return [ team, project ]; })
- Batch Inserts, Batch Deletes, Batch Creates
const todoA = { title: 'Finish Guide', userId: '49946f4d-8a2e-4f18-a399-58e3296ecff5' }; const todoB = { title: 'Learn Haskell', userId: '49946f4d-8a2e-4f18-a399-58e3296ecff5' }; const todos = await createRecord('todos', [ todoA, todoB ]);
- Distinct on
- Optimistic Updates
- Full Text Search
- Performance Improvements
π We've also launched Thin Backend, which is IHP DataSync as a Service If you're into Frontend Development, give it a try!
-
πΆοΈ Dark Mode:
We've redesigned the dev tools and finally added Dark Mode to IHP:
There's also a couple of nice improvements in the UI in general, e.g. you can now add columns such ascreated_at
orupdated_at
with a single click:In the Data Editor you can now hover over IDs to show the referenced database record in a card:
-
π¨ Redesigned Migration Workflow:
Migrations can now be managed much better from the dev tools. E.g. you can see all migrations, see what has been run already and also manually run migrations here that haven't been executed yet:
-
π³οΈ Filebase Integration
You can now use Filebase as an alternative to S3 for storing files with IHP Storage:module Config where import IHP.Prelude import IHP.Environment import IHP.FrameworkConfig import IHP.FileStorage.Config config :: ConfigBuilder config = do option Development option (AppHostname "localhost") initFilebaseStorage "my-bucket-name"
-
π¨ Custom Middlewares:
IHP provides an "escape-hatch" from the framework with theCustomMiddleware
option.
This can be used to run any WAI middleware after IHP's middleware stack, allowing for possibilities
such as embedding a Servant or Yesod app into an IHP app, adding GZIP compression, or any other
number of possibilities. See wai-extra for examples
of WAI middleware that could be added.The following example sets up a custom middleware that infers the real IP using
X-Forwarded-For
and adds a custom header for every request.module Config where import Network.Wai.Middleware.AddHeaders (addHeaders) import Network.Wai.Middleware.RealIp (realIp) config :: ConfigBuilder config = do option $ CustomMiddleware $ addHeaders [("X-My-Header", "Custom WAI Middleware!")] . realIp
Other Changes
- Correct type signature of breadcrumbLink
- Allow user-provided WAI middlewares
- Support limit and offset in DataSync
- Allow using DataSync without a user session
- DataSync: Prepend new records if the query is sorted newest first
- Added a subscribe function to the DataSync QueryBuilder
- DataSyncController: Added flag to keep be aware if first response was received already
- Added
useQuerySingleResult
- Fixed null not encoded correctly in jsValueToDynamicValue
- Added AuthCompletedContext to DataSync
- Improved strictness in DataSync implementation to avoid memory leaks
- Only set up DataSync database triggers once for every table
- Use strict modifyIORef in AutoRefresh
- Reduced amount of queries per DataSync operation: Previously we've been using
withRLS
to wrap database operations inside a RLS context. This added an overhead of several additional database queries. Now we pack allSET ..
statements right into the query string itself, so it's send to postgres inside a single operation. - Use finally to call Websocket onClose handler to make sure the finalizer is always called
- Added basic support for Point types in DataSync
- Added a batch update operation to DataSync
- Added a batch delete operation to DataSync
- Added transactions to DataSync
- Cancel PGListener reader async when a subscription is stopped: This avoids asyncs building up over time
- Implemented expected timeout handling in websockets: The 'withPingThread' function of the websockets package is not dealing with missing pong messages at all. This means that websocket connection might never be cleaned up when the connection is not closed correctly. This change implements a manual ping and pong handling that closes a connection after not receiving a pong message within 10 seconds after sending a ping to the client.
- allow chaining filterWhere/where for AND conditions in DataSync JS API
- Fixed several event listener leaks in DataSync and reduced memory usage
- implement or, and, filterWhereNot for DataSync; add where alias
- Fixed data subscriptions not always closed in specific cases: We switched from manual resource handling to Exception.bracket with MVars to make sure that all data subscriptions are always closed when the underlying websocket connection is closed
- Allow custom configuration of Datasync limits
- DataSync: add jest, run it with ESModules, add example test, include in github workflow
- Make query builder use Table API to access the table name: This allows for easier building custom data records that use an existing table
- small change to default.nix to allow dev customization of postgres extensions
- Fixed DataSync not generating the correct sql query for null conditions: E.g. a condition like
where('field', null')
needs to add a SQL condition likeWHERE field IS NULL
instead ofWHERE field = NULL
- Fixed build of ormolu on M1 computers: Fix based on https://gist.github.com/Gabriel439/61be09ed5c223c17e5a9268323afa223
- Automatically delete all table triggers when a table is removed from the schema
- Fixed DataSync not correctly restoring after a disconnect
- Added support for 'CREATE POLICY .. FOR ..' sql statements
- DataSync: add query(..).select(..) api to only fetch certain fields
- Added tsvector based fulltext search for DataSync
- Added support for generated postgres columns
- Fixed migration generator adding a null default value to generated columns
- Fixed generated columns not normalized in migration generator causing bad migrations being generated
- Added support for GIN and GIST indexes
- Fixed getAppConfig not working when called from job worker or mailer
- Allow nonce and referrerpolicy attribute to HSX whitelist
- Fixed applyImageMagick not working on IHP Cloud
- Update Browser Location With Form Response URL
- Router: Support
[UUID]
in AutoRoute - Implement "|>>" Operator
- parse EXCLUDE constraints
- Moved IHP.HSX modules into it's own ihp-hsx package
- support ALTER TABLE .. ADD CONSTRAINT .. DEFERRABLE INITIALLY DEFERRED and all other permutations of those last parameters
- Use Inter as the font family for our tooling
- allow an optional USING btree for EXCLUDE constraints
- actually parse and compile index type for exclude constraints
- Schema Support PostgreSQL Index Column Ordering
- Added auto-setup for updated_at trigger
- Added autoPolicy behaviour from IHP backend
- DataSync: Support distinctOn
- Add routeParam Function
- Fixed ihp_user_id() is NOT NULL sql expression compiled with
(
and)
. - Fixed migration generator not correctly handling ihp_user_id function
- implemented a simple draft of how optimistic operations could look like in DataSync: The latency from the US to the IHP Backend server is just too much for me
- fixed casing issue with plpgsql in migration generator
- Fixed spaces in identifiers in CREATE INDEX statements cause syntax errors
- Rename maxFailedLoginAttemps to maxFailedLoginAttempts
- Allow custom afterLogoutRedirectPath
- Add beforeLogout
- Support "DROP FUNCTION" in migrations
- Added Sitemap Generator
- Extracted out initModelContext so it's callable from apps
- Improved database query performance if RLS is enabled: Previously when RLS was enabled it was firing multiple queries just to set up the context, before running the actual query. For DataSync we've already been using a more efficient way of just firing a single query. This commit applies the same approach to the general query interface as well
- Support sending a response from initContext: E.g. to be able to call
ensureIsUser
for the full application - Handle umlaute and non ascii chars in toSlug
- Fixed order of object keys not as expected in DataSync responses
- Allow To Toggle SSC Debug Mode
- Fixed emailField function not accepting Maybe Text fields.
- Set type="submit" when rendering a submitButton
- Throw Exceptions When Trying To Save A File
- Updated the join documentation to highlight the (:.) type constructor.
- Ignore notify_ sql functions in migrations
- Fixed textareaField does not use label class specified in CSSFramework.
- Added caching to DataSync
- Added useIsConnected() hook to keep track of DataSync connection state
Full Changelog: v0.18.0...v0.19.0
Feature Voting
Help decide what's coming next to IHP by using the Feature Voting!
Updating
β See the UPGRADE.md for upgrade instructions.
If you have any problems with updating, let us know on the IHP forum.
π§ To stay in the loop, subscribe to the IHP release emails (right at the bottom of the page). Or follow digitally induced on twitter.