Fission makes it easy to build and host web apps using only frontend and local development tools with no effort put into servers or deployment workflows.
In this guide, we will use the Fission CLI to publish the elm-pages-starter
to the web.
Install Fission. Please follow the steps in the Installation guide to install and set up Fission on your machine before proceeding.
Clone the elm-pages-starter
to your local development environment.
Install and build the app.
Once the elm-pages-starter
is built, we are ready to publish it with Fission.
Initialize the project as a Fission app with fission app register
. You will be prompted for a build directory.
Accept dist
as your build directory, and fission
will initialize your app.
Fission has created a fission.yaml
file with your app configuration.
The url
is where the app will be on the web after it has been published, build
tells the fission which directory to publish, and ignore
is a list of files and directories to ignore. See the Fission YAML guide for more details.
Run fission app publish
to publish the webpage.
Copy the URL into your browser and you will see the elm-pages-starter
live on the web!
It may take a few minutes for DNS to propagate. If you don't see the app live at the URL, come back and try again in a bit. You can also take the URL from the CLI output and view the app athttps://ipfs.runfission.com/ipfs/{URL}
right away.
You can continuously update the hosted version of the app by running fission app publish --watch
. Fission will watch the files in dist
and publish when changes occur.
Where is my app? An app published with Fission runs locally and on the web on top of the InterPlanetary File System (IPFS). When you run fission up
you are saving your app to IPFS on your machine and syncing it with a greater file system distributed across the web. It's like a part of your file system merges with the whole and your app becomes available to everyone with a browser!
We have put the elm-pages-starter
out on the web without setting up a DevOps environment or going through a complex deployment workflow!
At this point your mind might be swimming with a thousand questions. Here are a couple of resources that explain how this all works:
WebNative: How to put a full stack directly in the browser talk at Speakeasy JS
In the next section, we will add Fission auth to the elm-pages-starter
.
In this guide, we will add Fission auth to the elm-pages-starter
. We will add a login button to the navbar that redirects users to the Fission auth lobby. After a user authenticates in the lobby, we will log them in and display their Fission username.
All of the code in this guide is available in the repository on the auth
branch.
We are updating this example to use the new package! The latest code on the auth
branch includes these changes. While we are updating the guide, please refer to the code linked above which has been tagged as webnative-auth
.
Before authentication, our header has a Fission login button.
After authentication, we show a username next to the button.
Our login button will be added to Layout.elm
, but we will start by adding our authentication logic and state in Main.elm
.
Let's start by adding a username
to our model.
Note that we always start without a username. We will check for an authenticated user after initialization.
Add a SubmittedLogin
message for when a user clicks the login button and a GotAuth
message to respond to authentication changes.
Add an outgoing login
port and an incoming onFissionAuth
port.
Add a subscription to onFissionAuth
.
We expect JSON that should look something like { username: "fission-username" }
. After decoding the value, we call GotAuth
with a Just username
on success or Nothing
on failure.
⚠️ Nothing on failure? In a real application, we would want to add error reporting and handling. We silently fail here to keep our example simple.
Our model
, update
, and subscriptions
are ready to go! Now let's add the login button to the navbar.
In Main.elm
, pass the SubmittedLogin
message and model.username
to Layout.view
In Layout.elm
, modify view
to accept the new parameter and call it fissionAuth
.
Pass fissionAuth
through header
to a new fissionAuthButton
displayed alongside the Elm docs and GitHub links.
We call SubmittedLogin
when a user clicks the fissionAuthButton
and display their username
if they are logged in.
All of our Elm code is written and we only need a small bit of JavaScript to authenticate users with Fission.
Our desired authentication flow looks like this:
A user clicks on the Fission button
They authenticate in the Fission auth lobby
They are redirected back to our page in an authenticated state
The next time they visit our site, they should already be authenticated
Let's start by installing the webantive
package from npm.
Import it in index.js
.
After our Elm app is initialized, we can bring webnative
together with our login
and onFissionAuth
ports to authenticate users.
Initializing webnative
gives us access to state
which has a state.username
and a state.scenario
. The state.scenario
tells us if the user is authenticated or not.
If a user just returned from the Fission auth lobby, state.scenario
will be webnative.Scenario.AuthSucceeded
. If they authenticated on a previous visit, state.scenario
will be webnative.Scenario.Continuation
. In both cases, we have a state.username
to pass through the onFissionAuth
port.
The login
port calls on webnative
to redirect the user to the Fission auth lobby. When the user returns from the lobby, we will initialize and check authentication status all over again.
That's it! User identity is held by your users in browser storage and you won't need a backend to manage it. A user that has authenticated with Fission also has a webnative
file system where your app can store their data. In the next section, we will show how to use it.
Here are a couple of resources on Fission auth for a deeper dive into how this all works:
We will use the Fission package to authenticate users. Our Elm app will request a login over an outgoing port and subscribe to authentication changes over an incoming port.
Users own their data. All Fission services are designed on the principle that users should own their data. When users own their identity, developers no longer need to maintain user accounts and authentication becomes simple to implement. See in our guide for more information on identity.
In this guide, we will show how to use Fission webnative storage to save user data. Our goal will be to add annotations to blog posts in the elm-pages-starter
.
All of the code in this guide is available in the fission-elm-pages-starter repository on the storage
branch.
When a user has signed in with Fission, an annotation button is displayed at the bottom right corner of the page.
Clicking the button opens a text input box where the user can write their notes.
If a user has not signed in, they do not see the annotation button or text input box.
Annotations are saved to the user's webnative filesystem and will be there when they return or use the app on another device.
We will start off by writing an annotation module. Annotation.elm
is built around an Annotation
type and has a way to create, encode, and view annotations.
An Annotation
can be editable or not. If an annotation is editable, it has a title
and notes
.
We have two ways of creating an annotation. A NotEditing
annotation can be created directly. An Editable
annotation must be created from a Value
that will come over a port.
Our code that interacts with the webnative filesystem will use blog post titles as keys to lookup and save annotations.
Add encode
for storing annotations and encodeTitle
for loading them.
Lastly, we need a way to view annotations. When a user is editing an annotation, we show their notes or the blog post title as a placeholder. When an annotation is not being edited, we show a button with a pencil icon.
Add a view
function with a case for Editable
and NotEditing
annotations.
Now that our annotation module is ready, we can add annotations to the elm-pages-starter
.
We want the user to see the annotation button when they navigate to a page with a blog post. When they click on the annotation button, we load the annotation from their webnative filesystem or an empty annotation if they are starting a new annotation. We replace the button with the notes in a text input box.
We save the annotation to webnative with each keystroke the user makes in the text input box.
Import the annotation module in Main.elm
.
Add an annotation
to the Model
.
We only display one annotation at a time, which means we can replace annotation
when we navigate to a new page or load an annotation.
The annotation starts off as NotEditing
in init
.
Our update
will handle four messages that initialize, load, or store annotations. OnPageChange
sets the annotation to NotEditing
when a user navigates to a new page. LoadAnnotation
calls over a port to load an annotation. UpdateAnnotation
updates the displayed annotation in the input box and stores the annotation. GotAnnotatation
subscribes to annotations that come in over a port after a load.
Our main
function needs a small change to configure the OnPageChange
message. Set the onPageChange
field in the record passed to Pages.Platform.init
to Just OnPageChange
.
Add loadAnnotation
, onFissionAnnotation
, and storeAnnotation
ports.
Add a subscription to the onFissionAnnotation
port.
We create an annotation from the Value
received over onFissionAnnotation
and use GotAnnotation
to update our model with it.
Pass the annotation and two messages to tell it how to load and update annotations to Layout.view
.
In Layout.elm
, the view
displays the annotation at the bottom right corner and in front of the rest of the page. We check if the user is logged in and viewing a blog post. If both are true, we show them the annotation.
Our app is ready for annotations and the last step is adding webnative storage.
Each Fission user has a filesystem that is stored locally and distributed across the web. An app that uses Fission storage asks the user for permission to use their filesystem -- similar to how a native mobile app asks for permission. After the app has been granted permission, it can store user data to their local filesystem and publish it to the distributed filesystem.
Let's start by installing the webnative
package from npm.
Import it in index.js
.
We initialize webnative
with a list of permissions stating what our app would like to use. In our case, we only need to request permission to use the storage associated with our app.
Shared storage: The webnative filesystem also has public and private shared storage that can be accessed across apps. See the webnative paths documentation for more details.
Declare an fs
variable that will be used to access the user's filesystem. Add fissionInit
with a request for permissions
to use app storage by app name and your Fission username.
Replace <username>
with your Fission username.
Next, we initialize webnative
. In the AuthSucceeded
and Continuation
cases, the user has authenticated and granted permission to use the filesystem. We can now set up the filesystem and a way to load and store annotations.
Annotations are stored in app storage in an annotations
subdirectory as JSON. Each annotation is stored as a file using the blog post title and a .json
suffix. For example, an annotation on the blog post "Hello Galaxy 🌠" would be stored on the path annotations/Hello Galaxy 🌠.json
. Paths are case sensitive, spaces are fine, and emoji are welcomed!
Paths are built using fs.appPath
which takes an array of strings that are parts of a path separated by forward slashes.
The numbered references in the code go as follows:
Alias the filesystem from state. We alias state.fs
as fs
to put it onto the global scope.
Create the app directory if it does not exist. A new user will not have a directory for our app. Check if the directory exists and make one if not with fs.mkdir
. Each time we make a change to the local filesystem, we fs.publish
to synchronize with the distributed filesystem.
Load an annotation or send an empty one. When the Elm app sends a message over the loadAnnotation
port, we make check if a file for the annotation exists. If it does, we fs.read
and send it over the onFissionAuth
port into the Elm app. If not, we send an empty annotation value.
Store and publish an annotation to the filesystem. When the Elm app sends a message over the storeAnnotation
port, webnative
saves the annotation to the filesystem with fs.write
. The current implementation uses a transaction queue to handle writes that come in quick succession. See the transaction queue implementation to examine how this works now. An upcoming version of webnative
will handle writes in parallel, and the transaction queue will not be needed.
Everything is in place and users can write their notes about blog posts! Annotations persist across visits to the blog and are available on any device where the user has logged in with Fission.
We haven't covered shared storage in this guide, but you can do much more when user data is not restricted to a single app. With shared private storage, you could write an app that shows users their annotations from multiple apps across the web. With shared public storage, you could convert annotations into a publicly visible comments!
For more information on how Fission storage works, take a look at: