måndag 13 september 2021

ClojureScript. Amplified.

"… we're going to build a web app with storage, authentication and everything …"

But how?

My favorite building blocks 🤩

I have tried out some tools that I find very useful. As you may have guessed, the code is developed with ClojureScript. A language I appreciate more each day. Developing an app, without the Interactive REPL? That would be taking a huge step backwards.

I have found that Material-UI works really well in the development of the app I'm working on. The components library cover many usage scenarios, it is well documented and there's a lot of code examples to learn from.

Recently, I added Storybook and really like the idea of developing user interfaces according to Component Driven Design. It reminds me of how I experiment with code using Test Driven Development. Just like TDD, I think tools like Storybook and Devcards changes the way we develop, to the better. Start small & build quality in.

Let's begin with a building block from my favorites list: AWS Amplify.

A basic setup for AWS Amplify & ClojureScript

The official AWS Amplify documentation is all about JavaScript. In this post, and in this example code repo, you will find a ClojureScript setup that I think will get you up & running quickly. First, set up AWS Amplify by following the first parts of the Official getting started guide. You will be creating an AWS account and installing a CLI to initialize your setup.

If want more details, have a look at Hey Webpack, Hey ClojureScript. It's a post describing a ClojureScript & Amplify specific setup that is used in the example repo.

Start coding, but where? 🤔

A pitfall might be where to place the auto generated client code from Amplify. I usually put files like the aws-exports.js at the same level as the root namespace.

In the example code repo, the ClojureScript source code lives in the src/main folder. That's also where I've added the aws-exports.js file.

Configure Amplify in the init function of your app.

Login & Logout with ClojureScript & the Amplify CLI

Adding authentication can easily be done by using the Amplify CLI.

From the docs:

"... The Amplify CLI supports configuring many different Authentication and Authorization workflows, including simple and advanced configurations of the login options, triggering Lambda functions during different lifecycle events, and administrative actions which you can optionally expose to your applications ..."

Use the Auth features in Amplify UI

There's a very useful Higher-Order React Component (aka HOC) in the AWS Amplify UI Library, that will render login and signup views.

I've chosen this less-lines-of-code approach by using the withAuthenticator HOC. To customize things like signup fields or styling, there is also a fully customizable Authorization component available in the Amplify UI library. Or you can use your own components.

The views entry point.
The passed in component is wrapped in the withAuthenticator HOC.

If you already have browsed the code in my example repo, you may have noticed the Reagent reactify-component and as-element functions. These functions are needed when passing components between ClojureScript and JavaScript.

Read more about React Components, elements and instances and how the interop is done with Reagent.

Create, read, update and delete

So far, we have used built in features that will render UI views and handle all auth communication with a backend service. To store, read and update data, we can use the Amplify API feature. Use the CLI to create a backend:

I have chosen GraphQL in my example code, but there's also a REST API option. When using GraphQL, all you need to do is to define data models. Amplify will create the backend infrastructure according to your models. In a matter of minutes, there are AWS AppSync endpoints & DynamoDB tables created for your app.

GraphQL

Here's a GraphQL model. An authenticated user should be able to add data to the profile, like a summary or an "About me" text. The user will be able to create, delete and update the data in the profile. Other authenticated users can read the data when they visit the user's profile.

The schema files belongs to the backend, and lives in the amplify folder of your repo.

async ClojureScript

The Amplify Client Library handles the communication with the backend services. All calls are asynchronous. In ClojureScript, you can use core.async. I haven't yet dig into the core.async stuff, so I'm using Promises here. I think it's a quite straight forward approach.

The data going through the wire is JSON. To make the developer experience better, I think it might be a good idea to add conversion functions to-and-from Clojure data structures. Like this one, converting user settings JSON to Clojure data:

Read more about the ^js type hints in ClojureScript.

Finally: Ship it! 🚢

Here's an excerpt from an amplify.yml that defines the build steps. If you add a definition file in your repo, AWS Amplify will use it in the CI/CD process.

This is the amplify.yml that I use in my example code repo.

There's more details at the official AWS docs about build settings and about amazon-linux-install.

Add UI building blocks to the amplified app

Now we have our amplified ClojureScript app. Let's add UI building blocks from Material-UI, a library that is very popular in the JavaScript & React ecosystem. Here, I'm using it via a ClojureScript library called reagent-material-ui. The library makes it possible to write components using Clojure data structures. You can read more about Material-UI and ClojureScript in my previous post Material Design in a Functional World.

Totally unnecessary css styling used here, but why not?

Testing the UI building blocks with ✨ Storybook ✨

I find Storybook very useful when experimenting with components and user interfaces in isolation. Being able to focus on one specific thing. There's many different aspects of developing an app, and it is sometimes difficult to handle all of them at the same time.

Storybook is a place for trying out things like css, styling, different viewports and UI events. You won't try out the AWS Amplify code here. The Interactive REPL is a better tool for that.

You'll find more about Storybook in Component Driven ClojureScript with Storybook.

Finally, check out my GitHub repo for ideas on how to create apps with the building blocks AWS Amplify, Material-UI, Storybook and ClojureScript.

That's a combination I'd like to call ✨ClojureScript. Amplified. ✨



Top photo by Drew Patrick Miller on Unsplash

torsdag 2 september 2021

Material Design in a Functional World

"... Material is a design system created by Google to help teams build high-quality digital experiences for Android, iOS, Flutter, and the web ..."
from material.io

ClojureScript & React is A 💕 Story

With Clojure, you can write functional & minimalistic code. With ClojureScript and Reagent, you can write functional & minimalistic React components. By adding re-frame to it, we also have a powerful way of handling state changes and triggering events.

The code you write in ClojureScript is instantly compiled to JavaScript React components that runs in your browser. The entire JavaScript ecosystem is available with this kind of setup.

"... React components for faster and easier web development. ..."
from material-ui.com

Material-UI

Material-UI is a very popular React framework. And we can use it with ClojureScript. But the JavaScript interop can sometimes be bit of a hassle. Fortunately, there is a great ClojureScript library called reagent-material-ui that simplifies this a lot.

One thing to notice is that the Material-UI components use React Hooks heavily to inject styling and theming into components. This is something to think about when using Reagent. Basically, just use the functional component syntax [:f> my-component] and you’re all good.

Have a look at the reagent-material-ui docs about common pitfalls in the React/Reagent interop and a fully working example in this GitHub repo.

Storybook: a playground for components

Here’s me playing around with the Card component, using ClojureScript and Storybook. I describe the Storybook integration in detail in this post.

The source code is available at GitHub.

No copy-paste?

I guess it can be a bit overwhelming for a Clojure developer to look at example code from the Material-UI site, with JavaScript/JSX syntax that is quite verbose (you’ll be even more overwhelmed by the TypeScript examples).

A JavaScript developer would probably copy-paste the example code and make some adjustments. We can’t do that. But it is worth the effort writing the code from scratch when discovering that the result is way more simplistic than the original. The amount of code written is usually half the size of the original JavaScript code. Yeah, that’s Clojure!

ClojureScript to the left, JavaScript to the right. Less is more.

Check out my GitHub repo with examples on how you can implement Material-UI components in ClojureScript, with hooks and styles - while having fun writing functional code.



Top photo by Thierry Lemaitre on Unsplash

tisdag 31 augusti 2021

Component Driven ClojureScript with Storybook

Component Driven?

Start small. Develop components in isolation. Don’t forget to pause & reflect on the code you’ve written. Can it be split into several small components?

✨ When you’re ready, combine your components to create features.

✨ Add the features to a view.

✨ Add the view to your app.

You can read more about Component Driven User Interfaces here.

Storybook

I’ve just begun to use Storybook and currently learning about it. What I especially like so far, is that the tool encourages you to write UI components that are independent … and functional?

This makes it possible to develop user interfaces in isolation. Just pass data and actions as parameters into your components. Keep it simple. This way of working reminds me a lot of Polylith, an architecture I have used in backend focused Clojure projects and that I enjoy a lot.

ClojureScript & Storybook?

You will find a fully working example in this repo. I have added Storybook to a ClojureScript web app, using shadow-cljs, with additional npm scripts located in package.json.

Update: a new version of shadow-cljs supports ns-regexp for the npm-module target. No need to manually add new stories!

The code in my GitHub repo is a continuation of the work I’ve described in an earlier post, about combining shadow-cljs with Webpack and AWS Amplify.

The Clojure Interactive REPL and Storybook working well together.

No JavaScript quirks?

Well, yeah some. The JavaScript ecosystem is evolving fast, tools are updated with breaking changes and sometimes that causes compatibility issues. In this case it is Storybook and Webpack 5. But I have solved it with a few extra rows of configuration. Don’t worry.

An example of a Storybook story written in ClojureScript

Have a look at the fully working example at GitHub.

Many thanks to Shaolang Ai, that wrote this blog post about shadow-cljs and Storybook that I’ve learned a lot from.



Top photo by Etienne Girardet on Unsplash

söndag 29 augusti 2021

Hey Webpack, Hey ClojureScript

✅ ClojureScript is great.

✅ AWS Amplify is great.

🤔 Why are there errors in my browser console?

ClojureScript. Amplified.

Some time ago, I decided to give AWS Amplify a try for a new project. With Amplify, you can quickly create and deploy web apps and AWS backend services by using your command line.

Even though the Amplify code examples and docs are all about JavaScript, I wasn't too keen on abandoning my favorite REPL powered language: Clojure. 🤩

So, I decided to combine two great things: AWS Amplify & ClojureScript!

Amplifed, yes. But with 🔔.

I have learned a lot from this blog post by Robert J Berger about how to setup AWS Amplify with ClojureScript. New versions of the AWS Amplify libraries are released regurarly. The current Major version include breaking changes that probably will cause ClojureScript web apps to raise errors and stop working.

Until recently, I have had to rollback updates and use earlier versions of the Amplify libraries. Nothing wrong with that. But my inner Alarm Bells have been ringing constantly since noticing the word legacy in the official Amplify docs for those library versions (the aws-amplify-react library and the Amplify Auth feature in particular).

The newer Amplify libraries rely on Webpack specific features under the hood. The example setup described in the official Amplify docs include using react-scripts, that (if I'm not mistaking) will package code with Webpack. That kind of setup is not supported by shadow-cljs, a tool that is very common in ClojureScript projects.

Breaking things

In addition to being stuck using legacy code, the app will break each time I add a third party library from npm. I have learned that it is caused by not having a fixed version for amplify libraries defined in my package.json file. A quick fix is to set all AWS Amplify libraries to a fixed version and use that one forever. A version that is becoming more legacy for each day. 🔔🔔🔔

Fix the broken things

The main issue with AWS Amplify libraries, ClojureScript and shadow-cljs is described in this GitHub thread. There is also a suggested solution hinted by Thomas Heller, the creator of shadow-cljs.

A solution: shadow-cljs & Webpack working together

I have created an example app using AWS Amplify and ClojureScript in this repo. Here, shadow-cljs will only create a target JavaScript file containing requires to all of the third party JavaScript dependencies.

Webpack will use that target to create a JavaScript bundle. In a continuous deploy environment, this can easily be automated by executing two scripts instead of one (the shadow-cljs build and webpack).

This solution makes it possible to use the very latest versions of the AWS Amplify libraries, without any css quirks or being forced to use legacy code. Hey Webpack, Hey ClojureScript!

Nowadays, my inner alarm bells are nice & quiet.

Photo by Kamil Pietrzak on Unsplash

söndag 18 april 2021

Confessions of an Open Source Contributor

I am one of the contributors to an Open Source project called zookeeper. It's an Apache ZooKeeper client made for Node.js. You can find the package on npm and the source code at GitHub.

What’s ZooKeeper?

"Apache ZooKeeper ... providing configuration information, naming, synchronization and group services over large clusters in distributed systems ..."

In short, Apache ZooKeeper is made of two parts: a service, and clients that communicate with it. Included in the packaging from Apache, there is a client made for the Java platform and also a C based client. You will most likely find one for your favorite language out there as a third party Open Source project.

Why joining Open Source?

A couple of years ago, I was part of a team in an organisation that use Apache ZooKeeper heavily. The team maintains a Node.js based app that communicates with a ZooKeeper service.

One day, the app began to raise strange warnings and unexpected errors. We looked into it and found out that the reason was probably caused by the client no longer being compatible with the API of the service.

There wasn't much happening in the client's repository:
unanswered questions from users, issues not taken care of and pull requests not being reviewed.

The project was abandoned.

So, we used a different client that seemed fairly updated and stable. The strange behavior in our app was now gone. Great! It worked on our machines and in production. All good!


🙋"Hey, team. I can't install your app on my Windows machine."

Sometimes other teams need to run our app locally for testing and such things. It turned out that our new client didn't support Windows at all.

Wait a minute, isn't Node.js supported in all major platforms? Well, yes. But this one is building a Native Node.js AddOn using bash scripts. Windows don't have that.

🤦‍♂️

Even though it wasn't my fault only, all I could think of is that it was my fault. I have ruined it for the teams at this workplace.

What else to do than try fix it on my spare time? So, I grabbed the source code to try to understand what's going on behind the scenes.

😭



And that's how my Open Source journey began: triggered by guilt and feeling ashamed.

Coding in the Dark

Late nights and weekends were spent trying to figure out how to solve this huge problem, including C/C++ code, CMake, bash and node-gyp.

One day, I figured it out. I had succeeded in making it work on Mac OS X, Linux and on Windows! The next day - a sunny morning in beautiful Stockholm - I went to the office smiling. Exhausted by the lack of sleep, but happy.

😎


Do I have to fix everything? 😟

While struggling with the source code, I realized there were more things that should be fixed. By then, I also realized that this Open Source project suffered from similar issues as the one we had replaced earlier: there wasn't really that much activity in the repo.

This is probably a common pattern: you work with something on a daily basis and contribute to Open Source projects. The years go by, you drift way from the project, simply because you now work with other things unrelated to the project.

How to maintain an Open Source project

Instead of grabbing the next task to spend late nights and weekends with, I decided to learn more about how to maintain Open Source projects.

I found a very useful guideline at GitHub, and I began to add notes about all kinds of things I thought should be done. Other Open Source projects were using labels in a way that I liked, so I added labels like help-wanted and good-first-issue to most of the tasks.

I think labels can help people getting an overview of the tasks and to understand the degree of difficulty.

Every time a question comes up, or someone is filing a bug report, I have replied to most of them within a day or two. I think this is important. The person reporting will know there is someone that has actually received and read the message.

As a result, really nice things have happened. The project has received Pull Requests from people located all over the world. Solving a broad variation of things from typos in the docs, to rewrites of the source code to modern JavaScript.

What's happening today?

Today, I think the project is stable and working well. I don't work nights or weekends. Even though I have moved on and no longer work with ZooKeeper as before, the feedback from users is my main inspiration to keep on working. Also, the amount of users of the client looks pretty good.

It inspires me to do the best I can with the project.



Photo by Justin Lim on Unsplash