All Projects → choonkeat → elm-webapp

choonkeat / elm-webapp

Licence: MIT license
A setup for writing http based, client-server app in elm, inspired wholly by lamdera.com

Programming Languages

elm
856 projects
javascript
184084 projects - #8 most used programming language
Makefile
30231 projects

Projects that are alternatives of or similar to elm-webapp

Prisma Ecommerce
💰A graphql e-commerce real-world fullstack example (backoffice included)
Stars: ✭ 231 (+344.23%)
Mutual labels:  fullstack
nextjs-dapp-starter-ts
A fullstack monorepo template to develop ethereum dapps
Stars: ✭ 228 (+338.46%)
Mutual labels:  fullstack
showcase
A Full Stack Journey with Micro Services and Micro Front Ends. Using dapr, kubernetes, react module federation and web assembly,
Stars: ✭ 45 (-13.46%)
Mutual labels:  fullstack
Frontend Developer Roadmap
📘 Front-end developer roadmap in 2021. This repository aims to collect the most important concepts of front-end.
Stars: ✭ 233 (+348.08%)
Mutual labels:  fullstack
fullstack-apollo-subscription-example
A minimal Apollo Server 2 with Apollo Client 2 with Subscriptions application.
Stars: ✭ 72 (+38.46%)
Mutual labels:  fullstack
fullstack-typescript
A demo project of a full stack typescript application
Stars: ✭ 28 (-46.15%)
Mutual labels:  fullstack
Stator
Stator, your go-to template for the perfect stack. 😍🙏
Stars: ✭ 217 (+317.31%)
Mutual labels:  fullstack
easypastes
Use Easy Pastes to create, store, share code snippets by simply pasting them with syntax highlight.
Stars: ✭ 34 (-34.62%)
Mutual labels:  fullstack
rsp
A server-state reactive Java web framework for building real-time user interfaces and UI components.
Stars: ✭ 35 (-32.69%)
Mutual labels:  fullstack
inCyberPunk2022
👾 https://cybersocial.herokuapp.com/   C̵̡̡͓̪̺̲̺̳̭̱̩͖͔̽̈́͜y̵̢̺̮̥̠̲̼̫͗b̴̲͇̟̭̹͆͒̈́̒͋̃̌̇̈̆̚͠͠ȅ̷̡̢̩̺̏r̴̢̛̹̲̜͙͉̩̩̣͉̺͂̀́̈́̇͛͋̊̉̈́̇P̵̡͊̚ų̵̙̣͓̤̼̭̤̥̯̻̯̒ͅň̸̛̯͕̦̦͓̙̋͐̈́́̉ͅḱ̷̡̪͚͉̟̘̳̯̳͉̈́͐͂̇̾͑̕̕͝ͅ
Stars: ✭ 28 (-46.15%)
Mutual labels:  fullstack
Moonzoon
Rust Fullstack Framework
Stars: ✭ 244 (+369.23%)
Mutual labels:  fullstack
Knowledge-Base
record every requirement and solution here
Stars: ✭ 31 (-40.38%)
Mutual labels:  fullstack
vuenode-fullstack
Vue.Js & Node fullstack Single Page Application with Relational databases as backend using Sequelize. Live Demo:
Stars: ✭ 42 (-19.23%)
Mutual labels:  fullstack
Opentouryo
”Open棟梁”は、長年の.NETアプリケーション開発実績にて蓄積したノウハウに基づき開発した.NET用アプリケーション フレームワークです。 (”OpenTouryo” , is an application framework for .NET which was developed using the accumulated know-how with a long track record in .NET application development.)
Stars: ✭ 233 (+348.08%)
Mutual labels:  fullstack
web-onefx-boilerplate
Full-stack React Framework for building web and backend
Stars: ✭ 34 (-34.62%)
Mutual labels:  fullstack
Fullstack Challenges
Open source's challenges of full-stack jobs to test your skills
Stars: ✭ 227 (+336.54%)
Mutual labels:  fullstack
react-soft-ui-dashboard
React Dashboard - Soft UI Dashboard | AppSeed
Stars: ✭ 171 (+228.85%)
Mutual labels:  fullstack
Trybe-School
All activities while studying at Trybe fullstack software development school. Contains: projects, exercises, course summaries. Brazil, 2020-2021.
Stars: ✭ 73 (+40.38%)
Mutual labels:  fullstack
shopyo
shopyo.readthedocs.org
Stars: ✭ 66 (+26.92%)
Mutual labels:  fullstack
create-fullstack-app
Create Fullstack TypeScript application with ease.
Stars: ✭ 29 (-44.23%)
Mutual labels:  fullstack

Elm-Webapp

A setup for writing http based, client-server app in elm, inspired wholly by Lamdera

1. Message passing

Client and Server communicate with each other using regular Elm custom type values.

elm-webapp will encode and decode these values to transmit them over HTTP. Websocket is possible but the code there is less robust.

Though elm-webapp does NOT manage the versioning & migration of MsgFromClient and MsgFromServer, the initial generated type definition does come with ClientServerVersionMismatch value which is leveraged to know that the client/server is out of sync and present a "Please reload this browser page" message to the end user.

2. Bring Your Own Data Persistence

elm-webapp does NOT persist the "model" of the server (aka serverState), unlike Lamdera. While you can still write code that update the model of Server, note that the values are only held in memory and is lost when the Server process exits. Doing so is still worthwhile during development though, enabling quick iteration of the app without messing with db & schema

You CAN have Server.elm instead

  1. make regular HTTP requests to query and mutate persisted data on DynamoDB (e.g. the-sett/elm-aws-core, choonkeat/elm-aws)
  2. make regular GraphQL HTTP requests to Hasura to query and mutate persisted data in a PostgreSQL database (e.g. graphql-to-elm, dillonkearns/elm-graphql)

Getting started

npx elm-webapp element hello-app

This will create a skeleton file directory structure

hello-app
├── Makefile
├── index.js
└── src
    ├── Client.elm
    ├── Server.elm
    ├── Protocol.elm
    └── Protocol
        └── Auto.elm

1 directory, 5 files

The above command generates a barebones Client of Browser.element.

npx elm-webapp

To generate a Browser.document, Browser.application or even a full fledged CRUD applications, run the cli without arguments npx elm-webapp:

USAGE:

    elm-webapp <type> <target_directory>

TYPE:

    While the generated "src/Server.elm" is the same, you can choose
    what kind of "src/Client.elm" to generate:

        application             generates a standard "Browser.application"

        document                generates a standard "Browser.document"

        element                 generates a standard "Browser.element"

        application-element     generates a standard "Browser.element" with
                                routing capabilities like "Browser.application"
                                but more compatible with browser extensions

    This generates a different "src/Server.elm" that comes with "CRUD"
    operations with an in-memory server state: Data is preserved on the
    Server only while the Server process is running.

        crud <TypeName>         patch the <target_directory> with the ability
                                to list, create, edit, and destroy "TypeName"
                                records

EXAMPLES:

    elm-webapp application helloworld

    elm-webapp document helloworld

    elm-webapp element helloworld

    elm-webapp application-element helloworld

    elm-webapp crud Post blog

Advanced overview

While app developers only need to work inside the cyan boxes on the extreme left and right, here's a rough overview of how the pieces are put together end-to-end:

src/Client.elm

In this file, we see

webapp =
    Webapp.Client.element
        { element =
            { init = init
            , view = view
            , update = update
            , subscriptions = subscriptions
            }

☝️ This record is where we provide our standard Browser.element, Browser.document, or Browser.application

        , ports =
            { websocketConnected = \_ -> Sub.none -- websocketConnected
            , websocketIn = \_ -> Sub.none -- websocketIn
            }

☝️ Here's where you can connect a WebSocket port implementation to communicate with src/Server.elm. Uncomment to enable.

By default, elm-webapp is wired up to communicate with src/Server.elm through regular http POST /api/elm-webapp

        , protocol =
            { updateFromServer = updateFromServer
            , clientMsgEncoder = Protocol.Auto.encodeProtocolMsgFromClient
            , serverMsgDecoder =
                Json.Decode.oneOf
                    [ Protocol.Auto.decodeProtocolMsgFromServer
                    , Json.Decode.map Protocol.ClientServerVersionMismatch Json.Decode.value
                    ]
            , errorDecoder = Json.Decode.string
            , httpEndpoint = Protocol.httpEndpoint
            }
        }

☝️ This section wires up the necessary functions to coordinate with src/Server.elm

updateFromServer

updateFromServer : MsgFromServer -> Model -> ( Model, Cmd Msg )

is the entry point where we handle MsgFromServer values from src/Server.elm. We usually do a case ... of statement inside, much like how we write our standard update function

main

main =
    webapp.element

that gives us our main function for the client.

sendToServer

sendToServer : Protocol.MsgFromClient -> Cmd Msg
sendToServer =
    webapp.sendToServer >> Task.attempt OnMsgFromServer

sends MsgFromClient values to our server whereby the server must respond with a MsgFromServer that we've wired to handle in updateFromServer (see above). This happens over http post by default, and over websockets if enabled (see above)

This is how we achieve a seamless and type-safe way for Client-Server communication.

src/Server.elm

serves our Client frontend app by default, and can respond to values from Client.sendToServer or regular http requests.

main : Program Flags ServerState RequestContext Msg String MsgFromServer
main =
    Webapp.Server.worker
        { worker =
            { init = init
            , update = update
            , subscriptions = subscriptions
            }

☝️ This record is where we provide our standard Platform.worker

        , ports =
            { writeResponse = writeResponse
            , onHttpRequest = onHttpRequest
            , onWebsocketEvent = \_ -> Sub.none -- onWebsocketEvent
            , writeWebsocketMessage = \_ _ _ -> Cmd.none -- writeWebsocketMessage
            }

☝️ Here's where we've connected our httpserver with Elm ports. You can connect a WebSocket server Elm port too; uncomment to enable.

        , protocol =
            { routeDecoder = routeDecoder
            , updateFromRoute = updateFromRoute
            , updateFromClient = updateFromClient
            , serverMsgEncoder = Protocol.Auto.encodeProtocolMsgFromServer
            , clientMsgDecoder = Protocol.Auto.decodeProtocolMsgFromClient
            , headerDecoder = headerDecoder
            , errorEncoder = Json.Encode.string
            , httpEndpoint = Protocol.httpEndpoint
            }
        }

☝️ This section wires up the necessary functions to coordinate with src/Client.elm

updateFromClient

updateFromClient : RequestContext -> Time.Posix -> MsgFromClient -> ServerState -> ( ServerState, Task String MsgFromServer )

is called whenever the Client sends a value over with its sendToServer. We usually do a case ... of statement inside, much like how we write our standard update function

updateFromRoute

updateFromRoute : ( Method, RequestContext, Maybe Route ) -> Time.Posix -> Request -> ServerState -> ( ServerState, Cmd Msg )

is the catch-all handler for http request; called whenever Server has to handle a http request that isn't handled by updateFromClient. e.g. oauth redirect path.

Note that ServerState is simply Model you see in standard Elm apps; named differently.

headerDecoder

headerDecoder : ServerState -> Json.Decode.Decoder RequestContext

is applied to http request headers and gives us a more meaningfully categorised RequestContext . e.g. we can decode the Authorization header and determine if the JWT value gives us a valid LoggedInUser Email or an AnonymousUser

This difference can be put into good use when we handle updateFromClient or updateFromRoute

Other files

  • index.js boots up our src/Server.elm
    • by default, node.js runs http.createServer and let Elm handles http request and write responses via Elm ports
    • if env LAMBDA is set, lambda.js will instead setup a callback so we can handle http request inside AWS Lambda behind an API Gateway.
    • other possible integrations are cloudflare-workers.js or even deno-deploy.js
    • PRs are extremely welcome to improve the robustness of these integrations 🙇‍♂️
  • src/Protocol.elm holds the types shared between Server and Client.
    • encoders & decoders are auto-generated in src/Protocol/Auto.elm ; also see gotchas regarding imported types
    • we're using elm-auto-encoder-decoder in elm-webapp only for convenience; you can switch it out for your own encoders & decoders. BUT if you continue using elm-auto-encoder-decoder, don't use them anywhere else (e.g. as encoder to save in db, exposed as part of your external api, etc...). Main reason being that the serialized format could change future releases of elm-auto-encoder-decoder and thus MUST NOT be relied on.

How do I...

License

Copyright © 2021 Chew Choon Keat

Distributed under the MIT license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].