IHP Web Framework

repository·master·Indexed 26 days ago

https://github.com/digitallyinduced/ihp

A batteries-included, type-safe web framework built on Haskell and Nix. The ecosystem includes ihp-hsx for JSX-like views, ihp-router for WAI application routing, ihp-graphql for GraphQL support, ihp-openai for GPT streaming integration, and tools for generating DataSync TypeScript types.

Tokens
163.7K
Snippets
526
Records
770
Agent score
89%

What's inside IHP

  1. Getting Started with IHP

    master

    IHP (Integrated Haskell Platform) is a full-stack framework for rapid application development using Haskell and Nix. It provides a managed development environment where dependencies like PostgreSQL are handled via Nix, and features auto live-reloading with a virtual DOM to reflect changes in under 50ms.

    To begin building, follow the recommended learning path:

    1. Setup: Install IHP and build your first project (e.g., a blog app).
    2. Core Concepts: Learn about the Database, Routing, Controllers, Views (HSX), and Forms.
    3. Common Features: Implement Authentication, Authorization, JSON APIs, and Background Jobs.
    4. Frontend: Use Tailwind CSS, htmx, and Server-Side Components.
    5. Advanced: Explore Relationships, Typed SQL, WebSockets, and Testing.
    6. Production: Learn about Security, Deployment, and Logging.
  2. IHP Project Directory Structure

    master

    An IHP project contains several key directories and files:

    File or DirectoryPurpose
    Config/Config.hsFramework and application configuration
    Config/nix/nixpkgs-config.nixNix package manager configuration
    Application/Domain logic and database schema (Application/Schema.sql)
    Web/ControllerWeb application controllers
    Web/View/HTML template files
    Web/Types.hsCentral location for web application types
    static/Static assets (Images, CSS, JS)
    flake.nixDependency declarations (similar to package.json)
    MakefileBuild system configuration
  3. Remake IHP environment after version switch

    master

    After updating the default.nix file, you must rebuild your environment to apply the changes. Run the following commands in your terminal:

    nix-shell --run 'make -B .envrc'
    nix-shell --run 'make -B build/ihp-lib'

    Once completed, you can start your project using ./start.

  4. Migrate from AutoRoute to the explicit-routes DSL

    master

    IHP 1.6 introduces the [routes|…|] quasi-quoter for explicit route declaration. While instance AutoRoute is still fully supported, migrating to the DSL provides:

    • Visible URLs and methods at the route site.
    • Compile-time validation of typos and fields.
    • Faster dispatch (10–50×) via an app-wide trie.

    Migration Recipe

    1. Identify your controller in Web/Types.hs (e.g., PostsController).
    2. In Web/Routes.hs, replace instance AutoRoute PostsController with a [routes|...|] block.
    3. Define routes using the format: METHOD /path?queryParam ActionName.

    Query Parameter Syntax (?fieldName)

    • a (e.g., Id Post): Required. Missing/unparseable returns 404.
    • Maybe a: Optional. Absent decodes to Nothing and is omitted from pathTo if Nothing.
    • [a]: Collected from repeated keys. Emits one entry per element; empty list is omitted.

    Field Renaming

    If the URL parameter name differs from the Haskell record field name, use the { field = #captureName } syntax: GET /ShowPost?id ShowPostAction { postId = #id }

    Path Captures

    To use RESTful paths instead of query strings, use curly braces: GET /posts/{postId} ShowPostAction

    Note: Changing path shapes breaks existing deep links. Use the ?field syntax to preserve existing URL structures.

    Mixed Mode Support

    You can use both AutoRoute and [routes|...|] in the same FrontController during a gradual migration.

    -- Example Migration
    [routes|PostsController
    GET    /Posts                 PostsAction
    GET    /NewPost               NewPostAction
    POST   /CreatePost            CreatePostAction
    GET    /ShowPost?postId       ShowPostAction
    GET    /EditPost?postId       EditPostAction
    POST   /UpdatePost?postId     UpdatePostAction
    DELETE /DeletePost?postId     DeletePostAction
    |]
  5. Connect to the development database via UI

    master

    You can use GUI tools like TablePlus to connect to the development database. Use the following credentials:

    • Database Host: The absolute path to your application's build directory. Run echo pwd/build/db in your terminal to get this value.
    • Database Username: Your current system username (run whoami to find it).
    • Database Name: app.

    Alternatively, the IHP development server provides a built-in GUI-based database editor at http://localhost:8001/ShowDatabase.

    echo `pwd`/build/db
    whoami
  6. Deploy an IHP PureScript + Halogen app using Nix

    master

    To deploy an IHP app using PureScript and Halogen with Nix, follow these steps:

    1. Update default.nix: Add the following packages to the otherDeps list:
        otherDeps = p: with p; [
            cacert
            esbuild
            git
            nodejs
            purescript
            spago
        ];
    1. Update NPM dependencies: Remove the packages added to otherDeps from your packages.json file, then run npm install to update package-lock.json.
    2. Upgrade Spago: Run spago upgrade-set to ensure compatibility with the Nix-provided PureScript version.
    3. Update Makefile: Append the following target to your Makefile to handle building and bundling the PureScript app into static/halogen/index.js:
    static/halogen/index.js:
    	HOME=/tmp npm ci
    	HOME=/tmp spago build --purs-args "--output static/halogen/output" --source-maps
    	esbuild static/halogen/main.js --bundle --outfile=static/halogen/index.js --minify --sourcemap

    Note: You may need to remove local versions of PureScript and Spago to avoid conflicts with the Nix versions.

  7. Route a WebSocket Controller

    master

    To make your WebSocket controller accessible, you must mount it in Web/FrontController.hs using the webSocketApp function. This is added to the controllers list within the FrontController instance.

    import Web.Controller.HelloWorld
    
    instance FrontController WebApplication where
        controllers = 
            [ startPage StartPageAction
            -- Generator Marker
            , webSocketApp @HelloWorldController
            ]
  8. Define and use Enums

    master

    You can define custom enum types in IHP either via the Schema Designer or by adding SQL to Application/Schema.sql.

    SQL Definition

    CREATE TYPE colors AS ENUM ('blue', 'red', 'yellow');

    Haskell Usage

    Enums are accessible as Haskell types. You can use them as field types in other records and use inputValue in views to ensure compatibility with fill in controllers.

    Controller/View Example:

    -- In a controller action
    post |> fill @["body", "color"]
    
    -- In a view (HSX)
    [hsx|
    <input type="text" value="{inputValue Blue}" />
    |
  9. Define `ToJSON` instances for IHP records

    master

    IHP database records do not have ToJSON instances by default. You must define them manually to control which fields are exposed in your API. You can define these in the controller, a view file, or a shared module like Web/JsonInstances.hs.

    instance ToJSON Post where
        toJSON post = object
            [ "id" .= post.id
            , "title" .= post.title
            , "body" .= post.body
            , "createdAt" .= post.createdAt
            ]
  10. Install ihp-openai

    master

    To use ihp-openai, ensure you are running the latest master version of IHP. Add ihp-openai to your haskellDeps list in your default.nix file.

    let
        ihp = ...;
        haskellEnv = import "${ihp}/NixSupport/default.nix" {
            ihp = ihp;
            haskellDeps = p: with p; [
                cabal-install
                base
                wai
                text
                hlint
                p.ihp
    
                ihp-openai
            ];
            otherDeps = p: with p; [
                # Native dependencies, e.g. imagemagick
            ];
            projectPath = ./.;
        };
    in
        haskellEnv