v0.11.0 (Beta 13.06.2021)
A new IHP release with some new features (spoiler alert: finally table joins) and bug fixes. Around 150 commits have been merged since the last release 23 days ago 🚀
Major Changes
-
Joins:
This has been requested for quite some time already. Finally @hllizi solved this: You can now do joins with the IHP query builder 🎉tomPosts <- query @Post |> innerJoin @User (#authorId, #id) |> filterWhereJoinedTable @User (#name, "Tom") |> fetch
You can also join multiple tables:
query @Posts |> innerJoin @Tagging (#id, #postId) |> innerJoinThirdTable @Tag @Tagging (#id, #tagId) |> filterWhereJoinedTable @Tag (#tagText, "haskell") |> fetch
Type safety is maintained by adding all joined types to a type-level list and checking that the table has been joined where necessary.
E.g. uses offilterWhereJoinedTable @Model
will only compile if@Model
had been joined to the input before. -
Server-Side Components
We've mixed the ideas of react.js with HSX. IHP Server-Side Components provide a toolkit for building interactive client-side functionality without needing to write too much javascript.Here's how a Counter with a
Plus One
button looks like:module Web.Component.Counter where import IHP.ViewPrelude import IHP.ServerSideComponent.Types import IHP.ServerSideComponent.ControllerFunctions -- The state object data Counter = Counter { value :: !Int } -- The set of actions data CounterController = IncrementCounterAction deriving (Eq, Show, Data) $(deriveSSC ''CounterController) -- The render function and action handlers instance Component Counter CounterController where initialState = Counter { value = 0 } render Counter { value } = [hsx| Current: {value} <br /> <button onclick="callServerAction('IncrementCounterAction')">Plus One</button> |] action state IncrementCounterAction = do state |> incrementField #value |> pure instance SetField "value" Counter Int where setField value' counter = counter { value = value' }
Here's a demo of using Server-Side Components for a interactive data table:
Learn more about Server-Side Components in the documentation.
-
New IHP.FileStorage Module: For storing files in AWS S3 and other Cloud Storage Services
A common task when building web applications is to save and manage uploaded files like custom logos, profile pictures or
.csv
files provided by the user. IHP now provides a simple file storage system to upload files to Amazon S3 or any S3 compatible cloud service.Here's how the new API looks, once configured:
action UpdateCompanyAction { companyId } = do company <- fetch companyId company |> fill @'["name"] |> uploadToStorage #logoUrl >>= ifValid \case Left company -> render EditView { .. } Right company -> do company <- company |> updateRecord redirectTo EditCompanyAction { .. }
Some of the cool features:
- For pictures, use the ImageMagick preprocessor to resize and convert the pictures
- Generate signed download URLs for private files
- You can use the
static/
directory instead of uploading to S3 in development
-
Case Insensitive Operations:
You can now usefilterWhereCaseInsensitive
to query for strings in a case insensitive way:userByEmail :: Text -> IO (Maybe User) userByEmail email = do user <- query @User |> filterWhereCaseInsensitive (#email, email) |> fetchOneOrNothing -- Query: `SELECT * FROM users WHERE LOWER(email) = <email>` pure user
There's also a new
validateIsUniqueCaseInsensitive
for checking uniqueness in a case insensitive way:action CreateUserAction = do let user = newRecord @User user |> fill @'["email"] |> validateIsUniqueCaseInsensitive #email >>= ifValid \case Left user -> render NewView { .. } Right user -> do createRecord user redirectTo UsersAction
Important if you use IHP's Login: IHP's built-in sessions controller used for the built-in login now uses case-insensitive lookup for the email addresses at login. This will improve user experience for users that create their account with
[email protected]
and then try to log in using[email protected]
.
Other Changes
- Introduce redirectBackWithFallbackPath and redirectBackWithFallbackUrl
- Add
isTrue
andisFalse
Validation Constraints - Added basic support for CREATE FUNCTION sql statements in Schema.sql
- Added support for CREATE TRIGGER sql statements in Schema.sql
- Hide functions and unknown statements from the schema designer objects selector
- Use nonmoving gc in dev server
- Fixed migrate command not outputing the sql statements.
- Added support for more advanced postgres expression in check constraints
- Fixed context menu in schema designer not visible on small screen when having too many tables
- Added withCustomErrorMessageIO to customize validateIsUnique Error Message
- Added Delete All Rows Menu Option to the Data Editor
- Renamed
make ghci
tomake console
- Explicit type cast for UPDATE statements for array types
- Use applicationName When Generating Action And Mail
- Added FromJSON instance for Id's
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. Or follow digitally induced on twitter.