We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

useFlask() - or how to use a React frontend for your Flask app

00:00

Formal Metadata

Title
useFlask() - or how to use a React frontend for your Flask app
Title of Series
Number of Parts
118
Author
License
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Many Flask projects nowadays still use mostly static HTML, WTForms and just a bit of AJAX. All the JavaScript is usually included straight from .js files or just minified with Python tools like webassets. But the JavaScript ecosystem has evolved a lot - one can now use tools like Babel to write modern JS even when targeting older browsers and a Webpack+Babel build is very straightforward and allows for advanced preprocessing of JavaScript code. In this talk we'll even go one step further and not only use Webpack to build the assets, but also integrate a small demo Flask app with a React frontend where the Flask app only serves the static HTML page and uses a RESTful API to communicate with the React app. We'll also look into how to keep convenient Flask features such as URL building in client-side code (using the js-flask-urls package) and of course React hooks will be used!
Keywords
Front and back endsPoint cloudGoogolCASE <Informatik>BitProjective planeMobile appFront and back endsLecture/Conference
Software developerQuery languageProjective planeWeb-DesignerIsing-ModellConfidence intervalMultiplication tableComputer animation
Software developerQuery language10 (number)Projective planeSineInformationModule (mathematics)BitCASE <Informatik>Multiplication signBlock (periodic table)CodeCartesian coordinate systemFormal languageSoftware maintenance
CodeCASE <Informatik>Formal languageValidity (statistics)File formatData dictionaryElectronic mailing listInformationGoodness of fitGUI widgetForm (programming)Client (computing)DatabaseCartesian coordinate systemLibrary (computing)Roundness (object)Group actionObservational studyIdeal (ethics)Insertion lossLine (geometry)Server (computing)Lecture/Conference
Query languageCodeExtension (kinesiology)Scripting languageWeb pageCodeQuery languagePoint (geometry)Social classIntrusion detection systemSoftware bugFiber bundleSound effectCausalityProcess (computing)Field (computer science)AreaMusical ensembleSheaf (mathematics)Functional (mathematics)Software maintenanceMultiplication signGoodness of fit
Template (C++)Server (computing)VolumenvisualisierungDependent and independent variablesWeb applicationLine (geometry)CodeQuery languageString (computer science)Arithmetic meanState of matterObject (grammar)Core dumpSystem callCrash (computing)Internet forumClient (computing)Instance (computer science)Multiplication signPhysical lawScripting languagePoint (geometry)Confidence intervalSerial portEmailLecture/ConferenceMeeting/Interview
CodeFiber bundleBuildingFront and back endsBoilerplate (text)Server (computing)Mobile appClient (computing)FlagFlash memoryComputer wormScripting languageHookingCodeDynamical systemHookingCartesian coordinate systemSoftware testingMobile appString (computer science)Free variables and bound variablesBoilerplate (text)Computer fileSource codeScripting languageBitFunctional (mathematics)Normal (geometry)Installation artProjective planeRevision controlUniform resource locatorMultiplication signSystem callType theoryDebuggerPhysical systemRight angleNeuroinformatikClient (computing)RootConfiguration spaceWeb 2.0Web browserBuildingTouchscreenPerturbation theoryDirectory serviceFile formatWritingParameter (computer programming)Focus (optics)Plug-in (computing)Inclusion mapCASE <Informatik>MultilaterationLibrary (computing)Macro (computer science)Complex (psychology)Proper mapLine (geometry)Function (mathematics)Prisoner's dilemmaMereologyGroup actionPhysical lawArithmetic meanGame controllerShape (magazine)Lattice (order)Power (physics)Closed setFiber bundleNoise (electronics)AuthorizationProcess (computing)Video gameProxy serverFlagDialectCellular automatonTask (computing)Medical imagingLevel (video gaming)Set (mathematics)Basis <Mathematik>Software developerAuditory maskingFilm editingRule of inferenceOffice suiteComputer clusterWater vaporForcing (mathematics)PlotterWeb applicationGame theoryCausalityConnected spaceWeightMusical ensembleDefault (computer science)Different (Kate Ryan album)
Server (computing)Link (knot theory)Boilerplate (text)Front and back endsUniform resource locatorNormed vector spaceMacro (computer science)CodeBuildingControl flowRevision controlForm (programming)DisintegrationCovering spaceDemosceneBuildingDemo (music)CodeMultiplication signMobile appProduct (business)Validity (statistics)Server (computing)Fiber bundleForm (programming)Macro (computer science)Arc (geometry)BitFront and back endsHookingProjective planeTraffic reportingSoftware bugClient (computing)Type theoryConnectivity (graph theory)Self-organizationUniform resource locatorLine (geometry)Web pagePoint (geometry)Plug-in (computing)Electronic mailing listField (computer science)Functional (mathematics)Boilerplate (text)Rule of inferenceState of matterSpacetimeVariable (mathematics)Template (C++)Normal (geometry)Revision controlPrice indexComputer fileEmailParameter (computer programming)CASE <Informatik>Exterior algebraSystem callIntegrated development environmentRight angleSinc functionActive contour modelWritingDependent and independent variablesMedical imagingSoftware testingMereology1 (number)Square numberSet (mathematics)Labour Party (Malta)Statement (computer science)Sound effectCuboidModal logicSoftware developerForcing (mathematics)Video gameArithmetic meanPlastikkarteProper mapAuthorizationDifferent (Kate Ryan album)TrailProxy serverElectric generatorLevel (video gaming)Chaos (cosmogony)Cellular automatonEqualiser (mathematics)Standard deviationDisk read-and-write headCore dumpHoaxComputer clusterInheritance (object-oriented programming)Structural loadConnected spaceOffice suiteSheaf (mathematics)SoftwareObject (grammar)Vapor barrierExpert systemStability theoryPower (physics)Phase transitionLattice (order)
Uniform resource locatorPointer (computer programming)Server (computing)DemosceneSlide ruleMathematicsSystem callWeightMoment (mathematics)JSON
DisintegrationFreewareInsertion lossTwitterTouch typingProjective planeTheory of relativityLibrary (computing)JSON
Connected spaceLevel (video gaming)Error messageClient (computing)MappingMetric systemBuildingLibrary (computing)LoginUniform resource locatorAuthenticationToken ringComputer configurationFront and back endsFunctional (mathematics)1 (number)CASE <Informatik>Sampling (statistics)Point (geometry)Pattern languageElement (mathematics)Extension (kinesiology)State of matterLecture/Conference
Transcript: English(auto-generated)
So, hello and welcome. Is the sound okay? It sounds a bit over saturated. So, hello and welcome to my talk. So, topic is Use Flask. Might sound familiar to people who use React Hooks,
but topic of my talk is Use Flask or how to use a React frontend for your Flask app. A bit about me. Like he already said, my name is Adrian. Online, I usually handle a Chief Master. I can find him in various places. And I am the lead developer of the Indica project at CERN. A 17-year-old project now, which actually was used to host Europe ISE in 2006.
And we still have the time table from the conference back then. Also, I am one of the maintainers of the Pallets project,
which is Flask, Verkhzoy, Kinshaw, and some more tools. So, I'm mostly hanging out there on the IOC and Discord channel, helping with the issue tracker, sometimes even writing code for it, but not as often as I would like to. But, yeah, it's just a Flask-grade project. And I'm a Python enthusiast since almost 10 years now.
I started with a small hobby project. My first thought back then was actually, ah, this language has indentation for blocks. That seems to be weird. And then I started loving it. And I'm proud to say that, well, around a year ago, I finally moved on from Check Savvy to React.
And honestly, I don't want to look back. So, a bit about the status quo. Instead, in the case of technologies used mostly in the Flask ecosystem, this information is mostly based on what I'm seeing people ask about on IRC. So, I'm sure there are some bigger projects out there
that of course do also many more customized things. But this is about what probably most people do because those are typical people asking questions as well. So, I'm going to talk a bit about the main four pieces many applications use. So, it's wtforms, jQuery, AtreX, and well, JavaScript,
which is kind of covering also jQuery. But yeah, you always have people who think JavaScript and jQuery are both different languages. Anyway, starting with wtforms. Well, it's quite nice to use actually. It's easy for simple case.
So, if you have a small application where you just want to put a form, have some validation, maybe generate the HTML for it, it's great. It's declarative. You don't have to do much. It does the job for you. So, yeah, great library. You should use it if you do a small application that's mostly static. The problem is, let's say you have something more complex,
maybe some custom widget. I mean, for example, if you're organizing a conference, you might have a widget where you select speakers, provide information about them, maybe what kind of roles they are, if they're a speaker or just an author. Well, now you are dealing with probably a list of dictionaries or some other format that can hold all this data.
So, you're submitting the data and, hmm, great. Now, there's a validation error. Maybe because the user entered something he shouldn't have entered and it wasn't caught on the client side. Well, anyway, what are they going to do now? Ideally, you don't want to lose the data, but just show an error and re-render the form.
So, you do a round trip with the value because it comes from the client, and wtforms converts to a format more suitable for Python, and then has to convert back to a value suitable for the HTML form field. Now, if validation failed, you might not have data. You can properly convert back to the format client-side code expects.
So, what are you going to do? Show the previous data you had in the database when editing? Show nothing? I think both are not great cases because, I mean, if you then don't notice it and save again, you might lose data. Of course, you can say garbage in, garbage out, but in that case, you really need good client-side validation,
which you might not always have, even though you should. Then, next one, check query. Let me ask you a question first. Do you in here still use check query nowadays or want to use check query nowadays? Okay, not too many. So, I mean, some years ago, check query was really great.
I mean, you didn't have to deal with, let's say, IE6 particularities, which I'm sure everybody got to love, but nowadays, I mean, there are not that many reasons for it. I mean, for something small, you can still easily use it, but the DOM provides the same functionality as well. And the risk of using check query is,
if it's bigger, you easily get spaghetti code because you have selectors and IDs and classes all over your code. Sometimes you might be using the same CSS class both for styling and functionality. Good luck when you remove the styling and then suddenly your scripts break. And in a bigger application, it can easily happen
that it's barely maintainable at some point because maybe you are including all JavaScript in a bundle. Then you have different pages which happen to have the same selector. Oops, suddenly the code runs on something it shouldn't run on. Yeah, not great.
Another problem with check query, of course, is there are lots of extensions. Some are very old, so you might get some subtle side effects and bugs. So, maybe not so amazing sometimes. Then, AJAX. The idea of AJAX is, of course, great, but the XML HTTP request API is absolutely horrible.
I'm sure most people here agree with it. I mean, you have to instantiate the object. You have to put a callback. You have to remember that ready state 3 or 4 means that it actually succeeded. There's no constant for it. And yeah, callback hell, of course. Then nowadays, luckily we have that,
which is supported by all major relevant browsers, which isn't bad. Can still be improved a bit, like you can't quickly build a query string from a dictionary, but it works fine. And there's stuff like Axios instead. But then, back to the server side, what are you going to respond to the AJAX request? Are you sending back large chunks of HTML? Because that might be if you just render your chinshaw template
on the server and send it back. And then one line to update is $whatever.html response data if you check query or setting inner HTML. Problem is, nowadays you might want an API. I mean, every bigger web app should have an API nowadays, because at least as a tech-savvy person myself,
I like having an API so I can do my own things, especially if tech companies nowadays like to make UI that are not so great sometimes for technical people. So it would be nice to be able to do my own stuff. Okay, let's assume, okay, we want to do an API. Are you going to duplicate your code for the API? Because you don't want to turn HTML into an API.
At the same time, you can't just return the data you passed to your render template call for the API, because that's probably an object with way too many data on it, not everything you want to actually send to a client. And it might not be JSON-serializable at all. Of course, you can use something like Marshmallow, which is nice.
You should probably use it anyway. But again, if you need HTML at some point, you probably still have some duplicate code for API and non-API cores. Then JavaScript in general. Well, what you usually see, a script tag, sometimes even inline JavaScript,
or just source URL for static pointing to some file. So it's rarely transpiled between formats, where sometimes people minify it, which is at least something, especially for libraries. And generally, the code I've seen lately looks like something written to support all browsers.
I mean, zombies can be fun. I mean, who doesn't like shooting zombies in the game? But well, this particular browser, I don't think anybody likes dealing with it. So yeah, please don't support zombie browsers if you don't get lots of money for it. And let me ask you the thing, if you still support those browsers, or if your JavaScript still contains the last statement,
why are you still writing the kind of JavaScript you could have written 10 years ago? I mean, things improved a lot. So yeah, maybe you can do things in a nicer way nowadays. So how to improve all this? So first of all, you can use a tool to build your asset bundle,
and assets in this case can be everything static basically for the client. So of course, JavaScript can also include CSS, fonts, images, since that thing can all be handled by a tool like Webpack. There are other tools as well, but in this case, we'll focus on Webpack because I think it's the best known one.
Also of course, use modern JavaScript. Like I said, probably don't need to support zombie browsers, so you can use all the nice new features, ECMAScript 6 and ECMAScript 2017 and newer standards, because like this, writing JavaScript is actually fun.
And if you do need to support some older browsers, there are tools like Babel that can transpile to, well, old JavaScript, which is not fun to write, but I mean, your tool writes it for you, and you don't really have to ever touch it, thanks to source maps, even not even by debugging. And of course, you can make the front-end reactive
using something like ReactJS. So maybe you've tried Webpack out and was like, oh God, configuring Webpack, that's quite hard, because you have to write a JavaScript file, and I don't think the typical Webpack config is less than, let's say, 30 lines of JavaScript.
Of course, you can use it without a config, but then you don't have many of the nice features. Then it's basically a glorified minifier. But let me tell you, considering Webpack is too hard, no, I don't think it is. Unless you want to do weird things with Webpack, and trust me, we did those things, so being third answered is not fun to debug
if something doesn't work properly. But no matter if it's a big complex code or something small, it is boilerplate code in the end. So for a single or two bigger projects, not too bad, but maybe you do many small web apps, basically a client version of a microservice.
Yeah, then you don't want to repeat this code all the time. And if you then want to update something, you have to do it everywhere, so yeah, not amazing. So the question is, can't we do something like from Webpack import config? I mean, we write in Python, we like to import stuff. Well, answer is almost.
There's a tool called Create React App, and to quote the documentation, Create React App is an officially supported way to create single-page React applications. It offers a modern build setup with no configuration. Sounds pretty great, I would say. So what exactly does Create React App do? So first of all, it contains the whole build config
inside its own package, so it doesn't clutter your project root directory or any of your files under version control. It does auto rebuilding and hot reloading of the front end, which is great during development, especially if you have SAP on your second screen. So you save your file, and it immediately updates.
And it even comes with its own dev server, which takes care of that. But I think that's enough just talking about the facts. Let's maybe dive into the actual code. So we start with a simple Flask application. I think whoever already used Flask in here probably knows this type of application.
It doesn't do much. It defines the Flask app. In this case, we're only registering two APIs. One simple one that gives you the current time, another one that returns a greeting, either for a given name or for an unknown person. Of course, these are both things
that could be easily done on the client side, but I needed an example, so here it is. I mean, writing useless code is fun. I think it's GitHub who actually has a useless code challenge going on right now. Okay, now we're on the client side front end. What are we going to do?
We're going to call Create React App. And luckily, it comes with this tool called NPX. Basically what it does is it downloads the package you specified to a temporary location, executes it. That's great because you don't need to install it manually somewhere first. And if you don't have NPX,
it's time to update your node version because older versions didn't have it yet. And many of the nice things nowadays don't work well with old node versions anyway. So you run NPX, Create React App, and in a folder client, so it creates the app for you, installs a bunch of packages, which usually does not take a couple of minutes
assuming you have a decent connection and computer. And it even tells you what you can do afterwards, like to start the front end. We're not going to do that right now because that's another step I have to think about because Flask is a very nice routing system. I'm sure you've seen it.
At root, you design a rule with some placeholders even, and then you can use URL4 to build the URLs. The problem is how do you do that in JavaScript? I mean, if it's fully static and you only have a few snippets, what you could do is like putting in a data attribute, which works okay for a static URL.
If it's dynamic, it won't work because then you probably start doing stuff like string concatenation, templates, rules, whatever, to put your params there, which is again pretty much fully manual. And for example, if you change the endpoint name in your code or another endpoint name, the URL,
are you going to remember to update it everywhere in your JavaScript? I mean, you might have tests for both front end and back end. And yeah, I guess you update those tests, but the tests will still pass except that now you have different URLs on the two sides. So unless you have tests covering both, the application itself might not work anymore.
So yeah, hard-coded URLs are pretty ugly, and even more so if they're dynamic. I mean, you're building a URL dynamically, are you going to remember to do URL encoding of the param in it? If it's coming from the user, you might have to. So let's install some more packages because reusing code is great.
I mean, first one, a normal Python package providing some functionality to Flask, and two node packages, and NPM is actually nice enough to update our package and so it was created by Create React app. So no need to update with a requirements text for that one. You only want to put the first one in your requirements text
or set up UI. Once we have those packages, we have to hook them up with, as build script hooks them up with Flask. So unfortunately, we still need a tiny bit of boilerplate code here, and this goes to a file called barbel-plugin-macros-rc.js. So what is barbel-plugin-macros?
This is basically a plugin for the build system that's included by default, which lets you define macros in the code transpilation step. More on that later. In this case, only thing worth mentioning is that the config now specifies that the Flask URL's plugin
has an argument URLMap, which is output from calling Flask URLMap to JSON, which is a package I installed in the previous step. And what it does is basically writing the whole URLMap from the Flask app into a JSON format containing all the details needed to build a URL.
Next step we have to do, which is partially because of a bug in Create React app, so we have to tell it to proxy requests for the API to our actual Flask application, since like I said, we have two dev servers, the one from Flask and the one for the frontend. It's a bit annoying that you have to write code for it.
I mean, you could use a one-line command in package.json, but currently this doesn't work in Firefox because it doesn't send the right header or something like that. So we have to use the boilerplate code so that we can use an environment variable to override the URL of the Flask dev server in that case.
Alternative to this proxying would be using CORS, so we simply run it on different ports and just access this with that. Or we could put engines in front of it, but I mean, it's development. Especially for smaller app, you want to keep it simple, no need to have engines there. So to summarize what we did so far,
first of all, of course, we have the Flask app which is providing API and only an API. We have the necessary boilerplate for the React frontend, most of which was generated by Create React app, so not much to commit except those two files. And we exposed URL building functionality to our JavaScript code, which we're going to use soon.
And the Create React dev server is now forwarding API requests to Flask. So let's actually start using some React. First of all, Create React app creates this app.js with a small demo page. And in this case, we only add a custom component.
So thanks to modern JavaScript, we can actually use import statements, very similar to Python, actually. So we import something called demo from file demo and instantiate it. Thanks to JSX, it's basically JavaScript where we can have XML-like snippets.
And the upper case tag is basically React component. We don't have arguments for it, so we just put it there. Then what do we put in those components? First of all, of course, we have to create the demo.js file, have some imports we will need later, but this is React-specific,
so if you start writing React, you will quickly see that, for example, you need to import React to use JSX. So what we will do related to Flask, first of all, we include the Flask URL's macro, which looks like a normal import, but trust me, it's not. And then in the next line, the 5 and 6,
we're actually using something called tag template literals and referencing the two endpoints we defined in the Flask up there. So now we have two things that are somehow related to those URLs. Both are actually callable to build the URL. But first of all, let's add some actual UI,
since right now our component is still empty. So like I promised in the abstract, we will be using React hooks. So what are we doing here? So first of all, we are defining a hook with a variable state hook that's providing a field containing,
well, something labeled time, and the setter function actually updates the state field. So this, basically, when calling set time, it re-renders the component, and time will then contain this value. What we then do is call the useEffect, another hook provided by React.
Basically, by providing the empty dependency list in line 8, we tell React to see things once and only once after the component has been mounted. So what we do in here, we simply use set, and with a generated URL to send the request and then process the JSON. And then at the end, we're building another URL, just for testing, this time with an argument.
So we could send a request to this one as well. Well, right now we don't, to keep it short. Anyway, as soon as we got the time response back, we are going to render a message, which time it is, and what the URL for the greeting would be. So I can actually show you how this would look like.
And if I reload this page, it will have the current time from the server, and the greeting URL built with the argument I put there. So now you might wonder how the hell does all of this work. Well, are you going to accept this answer? Probably not.
So let's go a bit into the code. So the first line when we're importing something ending in .macro, this tells the Babel macro plugin to actually run some code at build time. But just importantly, it doesn't actually do something yet. But as soon as we start using the macro,
in this case, it's a tag template literal. So this basically calls and sends a function provided by the macro at build time, and providing a reference to the argument you passed and its AST node. So this actually triggers the URL we're writing. What this thing is replaced with, I'll show you in a moment.
Anyway, if we call this function provided by it, it behaves basically like calling URL4 with the name equals snake keyword argument. If you're curious now, this is the code that the macro actually generates. It looks as ugly as you would expect from generated code, but luckily you never have to see this
unless you actually look in the generated code files. And it would be even uglier if there had been space to actually put all the building rules to generate the URL. But again, that's Flask internal, so no need to actually look at them manually unless you write a plugin to build URLs.
So let me show... The email I already showed you. Now an important part, you can actually contribute to this project. It's on GitHub under the MIT license in the Indic organization called js-flask-urls. Yes, I'm not great at making up funny names, so it's basically describing what it does.
How could you contribute? Well, you could really use some documentation. Right now we only have two small example apps. And yeah, I don't like writing documentation, but maybe somebody likes doing it, so you're welcome. And some guy in IRC actually told me using the Babel macros it would be possible to define TypeScript stuff.
So if you used it in TypeScript, you could get the proper typing. I'm not using TypeScript myself, but maybe somebody's interested in using TypeScript, so another thing to contribute. Or of course you can just start using it and maybe hopefully not find some bugs in it. Why should you contribute?
Well, you can brag about having contributed to a certain project. So to stay in the topic, there are some other nice stuff to show you. First of all, this thing also comes with Babel plugins. It doesn't use macros for the URL building. Instead it would give you a way
to import the URLs like this. I mean, it has the same features as the macro version, but you need to actually have access to the Webpack and Babel config, which you cannot do manually, easily, with create React app, unless you actually check the config, which makes it a bit more messy. And honestly, this is a version I wrote because initially I didn't know about the macro plugin,
so I have no idea if we'll actually keep maintaining this one or switch our own project to the macro version as well at some point. I mean, one less package to maintain. Then if you do a real application, you need some validation on the server side. Please don't use wtforms to validate your API requests.
It has web arcs. It uses Marshmallow internally. It's really great. Also very quick responding to bug reports and contributions. So a great project to use, I think. For form handling and validation on the client side, there's something called React final form. It's also very efficient, very lightweight.
Nowadays it even uses React hooks, using it for my main project as work, and it works great. And since this example was pretty short on my GitHub account, I actually have a bit more in-depth example. And this also includes some details on how you would actually generate a production bundle
and showing some different ways about how you can do things. And this demo looks like this. So yeah, if you update, you see it gets the current time from the server. Or you can go here, and you can put emojis.
So it shows me the URL it will generate, and if I call the API, I'm getting back a response, including a very pretty emoji. And yeah, that's pretty much it. So let's go back to the slides. So if you're now interested in the project
or want to get in touch, you can find me on GitHub and Twitter, and of course an IRC on Freenode, in the Poco channel, in the Indigo project channel, since this library I was talking about is kind of related. So yeah, we're using the IRC channel. Or of course you can talk to me right here.
And I even have some pretty hexagonal stickers to give away. So if you have any questions now, feel free to ask. I think we have... Thank you, Adrian.
We have a few minutes for questions, so if you want, you can... The microphone. Hi. I'm using Flask with connection. Is it possible to generate those URL mapping with connection also? I don't know connection,
except that I heard about it in a talk two days ago first. But basically what this library does, the Flask URL package really exports a function. You can pass a URL map to it, so you don't have to use a bubble metric. So as long as you can dump the URL map to JSON and somehow provide it to the URL builder function
from the Flask URL package, you can use it. The bubble thing is just syntactic sugar, so you can use it more easily without having to manually deal with exposing the URL building rules. Okay, thanks.
Hi. Thanks for the excellent talk. I just wanted to ask, there's always the option to do things clearly server-side or client-side. As far as the user authentication is concerned, assuming a very simple LDAP-based authentication, do you recommend using then Flask login
with login required on the API endpoints and then passing something, I don't know, a custom error JSON to the client or doing everything client-side? So if it's LDAP, so you have username and password, I guess? Yeah. I mean, it has to be on the server-side
because I don't think you can do LDAP login client-side. In any case, it would be very ugly. You probably don't want to expose any of the credentials to connect to LDAP on the client-side. So yeah, I would do it server-side and then return. Probably would have to use a session token for it, which is super amazing to have stateful backend with the API.
But yeah, should work fine. And if you're something SSO-based, you could do it all client-side. Personally, I like having it server-side because then you can just send some tokens there. You don't have to deal with the data from SSO. I think the pro and cons are both options. Thank you.
Sure. Question? Maybe not. Okay. Thank you, Adrian.