{"id":21606551,"url":"https://github.com/y-taka-23/miso-tutorial-app","last_synced_at":"2025-07-23T17:06:46.895Z","repository":{"id":94987050,"uuid":"124488381","full_name":"y-taka-23/miso-tutorial-app","owner":"y-taka-23","description":"An example GHCJS + Miso single page application. 🍜","archived":false,"fork":false,"pushed_at":"2018-06-22T20:57:52.000Z","size":115,"stargazers_count":47,"open_issues_count":1,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-11T04:15:30.379Z","etag":null,"topics":["frontend","ghcjs","haskell","javascript","miso","spa","ssr","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/y-taka-23.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-09T04:38:38.000Z","updated_at":"2023-09-30T13:03:27.000Z","dependencies_parsed_at":"2023-06-11T09:45:20.636Z","dependency_job_id":null,"html_url":"https://github.com/y-taka-23/miso-tutorial-app","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/y-taka-23/miso-tutorial-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-taka-23%2Fmiso-tutorial-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-taka-23%2Fmiso-tutorial-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-taka-23%2Fmiso-tutorial-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-taka-23%2Fmiso-tutorial-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/y-taka-23","download_url":"https://codeload.github.com/y-taka-23/miso-tutorial-app/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/y-taka-23%2Fmiso-tutorial-app/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266717709,"owners_count":23973384,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["frontend","ghcjs","haskell","javascript","miso","spa","ssr","tutorial"],"created_at":"2024-11-24T20:22:15.681Z","updated_at":"2025-07-23T17:06:46.868Z","avatar_url":"https://github.com/y-taka-23.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"Example SPA in Miso\n===================\n\n![Docker Automated build](https://img.shields.io/docker/automated/ytaka23/miso-tutorial-app.svg)\n![Docker Build Status](https://img.shields.io/docker/build/ytaka23/miso-tutorial-app.svg)\n\nAn example GHCJS + Miso single page application.\n\n![screenshot](screenshot.png)\n\n\nRunning the Application\n-----------------------\n\n### Docker\n\nThe application is packed as a Docker image, so you can launch it with a single command.\n\n```console\ndocker run -d -p 4000:4000 ytaka23/miso-tutorial-app:latest\n```\n\nAccess [http://localhost:4000](http://localhost:4000).\n\n### Source\n\nYou can also build the application from source code. Note that, if it's your first experience of GHCJS, it takes a long time to set up the compiler.\n\n```console\ngit clone https://github.com/y-taka-23/miso-tutorial-app.git\ncd miso-tutorial-app\n./run.sh\n```\n\nThe script prepares compilation environments according to each `stack.yaml`, builds both of the client/server side stuffs and starts the web server. Access [http://localhost:4000](http://localhost:4000).\n\n\nHighlights\n----------\n\nThis project is ported from [Elm implementation](https://github.com/sporto/elm-tutorial-app). Miso framework aims to provide \"The Elm Architecture\" for GHCJS, thus its architecture looks very close to the original Elm application. Nevertheless, the Miso implementation has several interesting differences.\n\n### Isomorphism\n\nIsomorphism, also known as server side rendering (SSR,) is one of the most remarkable features of Miso framework. Implementing both of the client and server sides in the same language, that is Haskell, Miso can reuse most of the application and optimize the cost of renering.\n\nAs with The Elm Architecture, an isomorphic Miso application consists of the following parts:\n\n* __[Model]()__ stores states of the application.\n* __[Action]()__ triggers a state transition with a payload (Msg in Elm.)\n* __[Update](https://github.com/y-taka-23/miso-tutorial-app/blob/master/client/Update.hs)__ makes a transit from the current state to the next, according to the incoming action.\n* __[Subscription](https://github.com/y-taka-23/miso-tutorial-app/blob/master/client/Main.hs#L17)__ catches mainly user's input.\n* __[Effect](https://github.com/y-taka-23/miso-tutorial-app/blob/master/client/Effect.hs)__ outputs anything to the outside world (Cmd in Elm.)\n* __[View]()__ converts the model to an HTML response.\n* __[Routing](https://github.com/y-taka-23/miso-tutorial-app/blob/master/shared/Routing.hs)__ determines which page should be rendered.\n\nIn Miso isomorphism, the models, actions and views are \"shared\" between the client/server sides.\nWhen a browser accesses the web server for the first time, the server returns an HTML file built from the initial model and the view logic. Next, when the user changes the current URL (by a subscription) or accesses external APIs (by an effect,) JavaScript re-renders the page by the shared view and routing logic in the client side only.\n\n| Client       | Shared  | Server           |\n|:-------------|:--------|:-----------------|\n| Update       | Model   | (Servant Server) |\n| Subscription | Action  |                  |\n| Effect       | View    |                  |\n|              | Routing |                  |\n\n### Path-based Routing by Servant API\n\nOn the top of Haskell's powerful type system, Miso can denote its routing as types, in the similar way with Servant. The following defines three routes : `/`, `/players` and `/players/:id` as a single page application.\n\n```haskell\ntype Route =\n         TopRoute\n    :\u003c|\u003e ListRoute\n    :\u003c|\u003e EditRoute\n\ntype TopRoute = View Action\n\ntype ListRoute = \"players\" :\u003e View Action\n\ntype EditRoute = \"players\" :\u003e Capture \"ident\" PlayerId :\u003e View Action\n```\n\nBesides, for the server side, Miso converts and extends the client side routes to define the APIs. In the following snippet there are four APIs:\n\n* `JsonApi` returns JSON files for XHR by Miso effects; `/api/players` and `/api/players/:id`.\n* `IsomorphicApi`returns HTML files generated by SSR. \n* `StaticApi` returns static resources including JavaScript.\n* `NotFoundApi` returns the 404 error page.\n\n```haskell\ntype Api = JsonApi :\u003c|\u003e IsomorphicApi :\u003c|\u003e StaticApi :\u003c|\u003e NotFoundApi\n\ntype JsonApi =\n         \"api\" :\u003e \"players\" :\u003e Get '[JSON] [Player]\n    :\u003c|\u003e \"api\" :\u003e \"players\" :\u003e Capture \"id\" PlayerId\n            :\u003e ReqBody '[JSON] Player :\u003e Put '[JSON] NoContent\n\ntype IsomorphicApi = ToServerRoutes Route HtmlPage Action\n\ntype StaticApi = \"static\" :\u003e Raw\n\ntype NotFoundApi = Raw\n```\n\n### Effects in IO Monads\n\nIn The Elm Architecture, the `update` function has a type:\n\n```elm\nupdate : Msg -\u003e Model -\u003e (Model, Cmd Msg)\n```\n\nOn the other hand in Miso:\n\n```haskell\nupdateModel :: Action -\u003e Model -\u003e Effect Action Model\n```\n\nThe `Effect` type is a sort of aliases, to handle `IO` actions asynchronously, implemented as:\n\n```haskell\ndata Effect action model = Effect model [Sub action]\ntype Sub action = Sink action -\u003e IO ()\ntype Sink action = action -\u003e IO ()\n```\n\nIt means that Miso can do arbitrary `IO` actions as effects. Moreover it provides several constructors to simply denote update with/without effects:\n\n```haskell\n(\u003c#) :: model -\u003e IO action -\u003e Effect action model\nnoEff :: model -\u003e Effect action model\n```\n\nLet's back to the example. Here `fetchPlayers` is just an `IO` action to fetch a player list via external connection. `updateModel` passes the result to `SetPlayers`, then again `updateModel` sets the player list without effects.\n\n```haskell\nfetchPlayers :: IO (Either String [Player])\nupdatePlayer :: Either String Player -\u003e Model -\u003e Model\n\nupdateModel FetchPlayers m = m \u003c# do\n    SetPlayers \u003c$\u003e fetchPlayers\nupdateModel (SetPlayer eP) m = noEff $ updatePlayer eP m\n```\n\nFor more detail, see [the implementation](https://github.com/dmjio/miso/blob/master/ghcjs-src/Miso/Effect.hs).\n\n\nReferences\n----------\n\n* [GHCJS](https://github.com/ghcjs/ghcjs)\n* [Miso Framework](https://haskell-miso.org/)\n* [Elm Tutorial](https://www.gitbook.com/book/sporto/elm-tutorial/details)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-taka-23%2Fmiso-tutorial-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fy-taka-23%2Fmiso-tutorial-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy-taka-23%2Fmiso-tutorial-app/lists"}