Devreal

Full Stack Scala Web Application.

Event: Scale by the Bay

SBTB 2023: Pathikrit Bhowmick, Full Stack Scala Web Application.

Recording: SBTB 2023: Pathikrit Bhowmick, Full Stack Scala Web Application.

uh my name is Pat kit uh I have been doing scholar for more than 10 years uh I'm the author of the bariles library which is a popular IO library and I wrote uh the 2.13 collections uh standard Library AR de so that's my background um so the goals of this talk uh we're going to develop a toy web application um we're going to only be using Scola for CSS HTML JavaScript Ajax um we'll have types safe rpcs so this is the communication between the back end and the front end your Ajax calls would be types safe which means uh if you remove or change the signature of a backend API your front end cannot make the ax Call be compile error um I'll also show uh this kind of facilitates uh front and backend code sharing you can share your model classes your validation logic uh between the front end and back end and this is kind of handwavy U I'm not going to show a lot of like abstractions very vanilla plain uh without fancy uh monads or iio libraries so vanilla scull and it's relatively unopinionated so uh you can you can throw you can if you want to have your favorite Json calization Library you can throw the one I'm showing out and bring your and so this is um uh kind of bring your it's not it's more it's less about uh a library and more about how you can structure your code yeah so I'll be showing less than 100 lines of code okay let's get started so um I'm going to show an over engineered mortgage calculator so okay so this is my scholar Library scholar code um I'm going to just start sbtd um and yeah it starts the server okay there you go uh I mean it was rendering fine because running of the Local Host is just uh was ugly because it didn't have the bootstrap CSS Library okay anyway so this is all written in scholar um you can see when I hit calculate it calculates my mortgage let's say I have a loan of a $1 million 5% APR 30 years of mortgage and it generates my interest and principle for the next 30 * 12 360 months um you can look at the network call so it actually makes an Ajax call uh with a particular payload in this case my AP my number of years and it gets back a response uh a Json response okay all right so how do we write this so so one way to think about it is to structure such a web such an application into four components first is the framework so this is the part you can imagine you would reuse across different projects so if you're writing yet another uh full stack SCA web application you can throw it in there so this is the Library part if you will second is kind of the shared module which is the part where you want to share your validation logic your model classes between the front end and back end this is kind of obvious this is the web part this is your scholar JS and Scholar CSS um and your fourth is your server which is the back in server which uh in most cases does two things it serves your static assets your HTML and JavaScript and it serves your data or has business logic so it does computation okay um this is the build. sbts you can see I'm defining the four uh different uh subm modules for the project uh one useful thing uh I like to do is uh register a command so in this case I registered a Dev command which uh formats my code uh this Tilda is like the watch operator so it's like R live reload so when I change my code it live reloads the back end and the front end it does fast optimization JS Which comp which takes you call ajs code compiles it to JavaScript and it starts the server and waits for it to stop it's pretty useful so that's the command I ran back here okay um the libraries again I try to I like to uh organize them in these sections so there's a scholar JS section so this is related to using scholar JS it's schol CSS this is a DSL where as promised i' be writing Scola instead of CSS so this is the libraries uh the third section is the JavaScript rappers so many popular libraries like jQuery and react and bootstrap have uh scolar wrappers scolar JS wrappers so instead of directly using JavaScript you can actually use the scolar apis um third third fourth would be the API layer so this is the kind of the library layer which actually takes care of serialization distalization comes uh is in charge of communication between back end and front end and the fifth layer is the shared layer which is your validation logic here um in this case I'm using squand for validation and the other two dependencies so these are all scholar JS dependencies because these are shared between the web and the front end uh sorry web and the shared modules the back end there's only two dependencies I'm using cost and request cost as my server and request is to make the request and again this is fairly unopinionated like instead of cusk if you want play you can throw that in there instead of requests if you want some other Library you can okay and this is just uh taking the previous build. SBT and um hooking up the dependencies that I defined here all right and this again goes over the structure so as mentioned earlier uh it's four sub modules this is the framework part which is uh common across different projects this is the backend server uh the server has two parts the router which is in charge of serving static assets CSS jav CSS JavaScript HTML and your services layer which is your business logic presumably this would be talking to your database or doing computation not possible in the front end this is your shared module so in here I have one one so the shared moduel usually you want to store your data model and validation logic so you can share the same validation between back end and front end and the last is the web module which is has your um uh HTML so which looks like the this thing okay so let's talk about the front end so this is what scholes looks like so it's pretty neat Library where you get these deaths um each def takes a variable number of arguments of other defs and each of these def kind of mirror a HTML tag you can Define your custom tags but it comes with common stuff like HTML ahead body div form labels buttons things like that um it also has this colon equal to Syntax for setting a value to an attribute um and if you actually do uh so this is pure scholar code um it's actually your ID will autocomplete and do suggestions and uh if you actually invoke webpage. html. render you get back a string the string is actually the HTML um of the page so you can see looks very similar um and why are we doing this uh I'll get into that later so one tiny annoyance is a lot of uh HTML keywords like class and for and type are also scholar keywords so you have to escape them using backs so it makes the code look a little weird but uh at least you're writing scolar okay so this gets into the part what was what's the point of doing this uh so the biggest besides having a strongly typed language uh for writing HTML uh the biggest use case would be uh templating so in this case um I let's say you have a bunch of pages and they have the same head section and they all import kind of the similar uh JavaScript libraries and scripts so you can abstract that away in this case I have an abstract page class with a single abstract method the render body because that every page has a different body but everything else is the same and I kind of build it all up up together on the first line render doc which says HTML and it has a head and it has a body and it has a bunch of scripts in the end um and yeah so this is uh how I would be use writing a page a hello world page which has a push button uh which when you push it it just prints a random number so a lot of the construction of the page the common part the scripts the header section is happening in the super class in the page class um uh one thing that I'd like to me uh highlight here is this init method so there's nothing really special about it so I you can see I'm using this JJs export tag uh annotation so this annotation tells the scholar JS compiler that this should be available to invoke so the schist does not actually expose every method to you for for you to call you have to specifically whitel list either at the top level using at JS export top level or at da using JS export so how is this called so you I am actually calling it here within a script tag and name. inet uh I like to do this is as a way to inject script this is a very common pattern if you're using jQuery where you have when you load a document you attach bunch of handlers using do on ready event so here again uh when the page is loaded I'm attaching a click Handler to the button element here saying do this thing um this code actually looks like jQuery but it's actually scholar uh it's scholar code uh this is using a wrapper a scholar JS wrapper over jQuery um and it's almost valid JavaScript code if you just copy and paste this in okay um how do we do dependency management so uh instead of using Gulp or B or one of those package managers it's pretty common nowadays to actually use CDN for dependency management so in this case I'm using JS deliver and this is all the thing you need like three lines of code to do dependency management uh using Scola of frontend JavaScript libraries so I have this helpful deps and this is how I use it I'm just declaring that jQuery I want to use version 3.2.1 and I get this back uh some libraries like bootstrap have uh a CSS and a JavaScript that you have to import in and one nice way to structure this using scolar object so you can have this make sure you use the same bootstrap version for the Cs s dependency and the JavaScript dependency and how do you actually use them you can see them here you just do def JS Libs and you import the jquan bootstrap and this gets packaged into uh line 16 uh and uh in this in the def script section so you can see J Libs Plus+ list which is my custom scripts and it gets packaged into the HTML uh which gets rendered as the full HTML page okay so this is all the markup for the mortgage calculator uh which is this thing so this is all the markup that's needed for uh writing the mortgage calculator web page so this is actual code that works so this highlights one of the benefits of doing the template The Scholar route like a higher level programming language route you can see that uh I'm using this def input it abstracts away a little a common thing so which each of my input elements so in this case loan loan amount a default number a label and a text area um it's actually like four lines of HTML Scala so if and I have four inputs here so that would be 16 lines of code but I'm actually only writing four lines of code because I'm have a def that uh AB abstracts away the repeat repetitive part of the code so this is very similar to HTML templating but I'm just using vanilla scor defs so this is super useful um when writing the your markup in scolar okay uh so what happens when I click calculate this this button so again as usual I'm attaching a click Handler I'm using the jQuery wrapper I'm saying on when I click on this on the on on the click event on this element called this defa and this defa actually looks very similar to the jQuery defa it takes in an element and the event uh most of the time you don't need it because you know it happened on this thing and the event is like was was it a right click or a press down click things like that um so there's nothing interesting happened here except these two lines so what I'm doing is I am reading the my various uh values like the loan amount the APR the years out of uh the HTML elements these lines 14 through 16 look a little ugly because I'm typ casting first uh a JS element to a string and then I'm doing integer. parent that's what two. two in option does um you can write type classes read type classes to get around this you can say like given an instance of a JavaScript element can I read an integer can I read a string but it's it's okay it's a little ugly but this is what it looks like so the mag the magic happens here so I'm creating an instance of a mortgage uh CL and I'm cing calling mgage api

payments but guess what this is not actually being executed in the front end this is actually making an Ajax call um but it doesn't look like I'm making an Ajax call it looks like I'm calling a sculla def so I'll show you how how how we're doing that okay so this is the shared code so the nice thing about having everything in Scala you can share your backend and front end models so in this case I have a mortgage class and a principal class um and this is my API so I have two API let's say I have a payments API which takes in a mortgage returns a sequence of payments and and a refinance API takes in a mortgage and a new rate and tells me how much penalty I should be willing to pay to go for the new rate um but obviously I can't do that because this is just Villa Scala and uh this thing is actually making Ajax calls so we have to rewrite it using this framework. RPC I'll show that the RPC is like 40 lines of code I'll show that so instead of having uh def input output I have an RPC with input and output parameters and a path that it lives in again these are all unopinionated you can make it live at any other path so uh for this method which takes in two things a mortgage and a double and returns a double it has a doule of mortgage and double as the first type parameter and the output type as the next type parameter okay so that's let's implement this RPC class so the RPC class has two type par input and output um it has read read writer so if again the read writer is a type class that says that I can read the input and serialize and der serialize the input from Json and to Json respectively uh I'm using new pickle but if you use any other Json Library they all come with their type classes which denotes this effect so that's not interesting and it has a single apply method which takes in an input and returns a future of the output okay how should we implement it that's it that's that's all we need to do so we need to just do an Ajax dopost we're going to post it at the path that we set this API lives at this right takes in the instance of input makes it a Json sticks in the Json headers and when we get back the response we write read the output back from the text back into the output object and this is it compiles and this is all the code there is but okay what if we didn't want to do Post what if we wanted to do gets and delet leads uh that's pretty easy you just need to pass in an extra method saying what HTTP method this so earlier here uh we said this API lives here and we just assumed it was post but if you just said no no no it's not a post it's a get uh that lives here so this is all you need to do um the only thing is instead of now encoding Json encoding the input into the body you have to pass it as URL parameters because GS don't really have bodies um but that's pretty also pretty EAS again using type classes but uh for the purpose of this talk let's assume we're doing posts but it's pretty easy to extend this to gets to do your gets post puts and deletes yeah okay that's the get um yeah so this is the Json read write nothing interesting here most Scala Json serialization libraries comes with these macros which can serialize deserialize ske classes you just have to stick this implicit which tells the compiler that my models can be written to Json or read back from Json this is also not the interesting part this is the service layer so this actually runs in the back end and does the mortgage calculation obviously this could have left lived in the front end but then this stock wouldn't be that interesting so this lives in the back end uh and gets invoked via Ajax calls from the front end this is the server so this serves the static assets I'm using cost but again this talk doesn't really is opin about using cost you can use your favorite web serving Library like play in this case I'm saying the index which lives at without any path serves the render doc which uh takes my modage calculator. Scala which is my using Scola DSL to describe HTML and just sends out the string of the HTML for static files uh requesting under JavaScript I'm literally Ser serving it out of the SBT compilation directory um and I have a flag here whether I'm doing fast optimization or the full optimization because scholar JS can compile your scholar code to JavaScript in two different ways and maybe I want to switch it based on if I'm in the prod environment or am I doing Dev locally um this is the interesting part where I have where I'm routing the apis like how do I now that I have defined my apis using the RPC class how do I make them actually live as Ajax objects so there's only two things to keep in mind here there's the RPC do wire method and there's this quick squiggly arrow and I'll go over both of them okay so let's go over the wire method it's actually pretty trivial so I Define a request Handler this is a very useful object to Define it's a partial function from a request to a response which says given a request I may be able to satisfy it and respond back to you um and wire all it does it takes a variable number of such partial functions and tries the first one if not try tries the second one if not tries the third one else says I could not satisfy this request so that's all this does is one line of code so this RPC do wire is this thing so obviously that means this squiggly Arrow has to be a partial function so so this Squigly arrow is a partial function so mind you this on the left side this is an instance of the RPC class so because we defined our rpcs we defined our rpcs like this so these are instances of the RPC class so and on the right side are actually the backend implementation so we're saying this API is implemented by this back backend function um and it's all type safe because uh I defined my rpcs of particular I and O type particular input and output type and it can can only be implemented by a function of Ino so it will be a compile error if I mismatch my backend function with the uh frontend API definition and how is this and how does this work so it says oh I will be able to execute it if the request incoming request is the has the path of which this RPC was declared at so if request path matches my expected path then I'll try to Json parse the input data uh if I cannot pass it pass the then it's a bad request so that means the front end made an API call with a bad body and if I was able to parse it into an instance of I I will try to apply the function that was I told is the backend implementation and again two things can happen either this function executed successfully then I will serialize the result and send it out or the function threw an error which is really bad because your backend function then crashed so then it I would send out an um 500 error code and that's all there two is this is seven lines of code and that's it uh I have a full stack Cola uh web application I swear I did not hide any lines of code um okay so what are the use cases so the best use case for this kind of thing is for if you already have a skull application then you can stick this maybe like 50 lines of code and you don't have to have separate apis and a separate front end to do admin or admin operations so if you have like a long running service or microservices and you want to do batch uh admin operations like look at it or look at the status of it or have a nice uh panel to for internal dep purposes uh is really to throw this in there and you don't have to worry about keeping the API in sync because you'll get a compile error if you um mess if you like have a new route or you refactored your code um and you don't have to have like a separate deployment of like your front end like a HTML Javascript app another thing is you can share a lot of logic between back end and front end so you have a lot of complex validation you can have it both in your web application and in your backend and obviously you can do fun stuff uh personal projects um I don't have time so this is like a quick demonstration of 20 lines of a validation Library so one use case of this is to have shared validations so imagine you're have have like a validation for a strong password and you can put that logic in the front end and also in the back end and yeah so uh in this is a example where I have a case class address and I declare a bunch of validators saying oh the Zip must be so and so a country code must be valid and a person can have an address and the address inside the person person must also be valid and that's all there to it so and now you can I'll show you how to use the same validation uh implicit validators that I declared for a person case class and address case class um uh so this is the squiggly Arrow function from earlier and all you need to do is um this extra stick in this extra three lines of code so by default you can say I don't have a validator for my input uh but if you do uh when you Json parse the input you run it through the validation and you respond back if it fails you respond back with a bad request so that's all there is to do shared validations um again the use case is your front end may not be using the front end code to make the Ajax call someone might be using curl so you still need to validate the logic so uh this enables to have the same logic being executed twice both while making the API call and in the server side yeah so that's an an example where I'm validating my modage object in the RPC um yeah so there's also this Library Scala CSS so as promised I won't be writing any non-cola code so I have to write CSS in Scala so this is a DSL uh again you can stick it in the abstract page class so this particular lines of code compiles down to this CSS it's it's a pretty interesting Library it um you can you can do a lot of cool things with it because in one problem with CSS is variables you have a lot of variables that uh other CSS objects depend on but now that you're writing schola you can abstract all that away and have CSS variables for free um uh the last thing is as mentioned earlier this as instance of string then parent it's kind of annoying uh you can get you ideally want to write something like this or even have a type classes where you can just given bunch of input elements you can materialize an instance of mortgage um you can have a JS read type class which uh given an JavaScript a scholar JS input uh element it can materialize um strings in sent case classes so uh this is an example of it this is all the code there to it um so that makes this part of the code much easier to read um yeah so let's go over so I showed you the framework uh module so this is the module that you may want is the Library part of the code which you may want to share across different projects so the JS Libs is the part which does the CDN dependency management this is the type class to read uh JavaScript objects into scholar case classes uh this is my abstract page class this is the RPC class and this is my validation library and on the server I have the router which serves static assets and in my services section under the server I have the business logic which in this case actually implements the modage API and calculus payments my shared module has the mortgage case clause and the RPC defined and the validation logic defined um and my web is this mortgage calculator. Scala which is the Scala JS DSL of the HTML and that's all the code um you can go check this out I think this is a good uh starter project if you want to start your own uh feel free to Fork it um that's it [Applause] yeah so it's a much easier value prop when you already have a backend in that language then and especially for admin tools where like if you have a company where you have a front user facing and obviously have a front end team managing that but you have these backend Services where you don't want to bother the front end team to go implement it it's a super easy value cell to the development team be like hey like just just do this extra 100 lines of code and you have pretty cool yeah um for actually like going all the way I don't know if I would do that but definitely in my company we use our uh the internal admin pages are served by out by the server side by the scholes yeah yeah inten obviously we do use that but for this purpose of the talk I wanted to keep it the yeah laminar yeah okay one more oh just uh one of the scholar J libraries I think they released it one month ago so I haven't updated my slide I made it like right before for that so yeah I should be porting it over to scolar 3