[HN Gopher] How to store your app's entire state in the url
___________________________________________________________________
 
How to store your app's entire state in the url
 
Author : escot
Score  : 314 points
Date   : 2023-01-09 16:57 UTC (6 hours ago)
 
web link (www.scottantipa.com)
w3m dump (www.scottantipa.com)
 
| jdlyga wrote:
| You've just invented video game passwords! This reminds me of
| transferring over my save data from Golden Sun to Golden Sun: The
| Lost Age by typing in a 260 character password.
 
| gernb wrote:
| Lots of sites do this. Lately for me there's
| 
| https://www.typescriptlang.org/play
| 
| I'm mixed on it. It means it's mostly useless for having your own
| list of creations because you have to manually store the URLs
| (PITA) and you need to store them somewhere accessible if you
| want to repo the UX of the site actually storing them where you
| can more easily access them from anywhere.
 
| jonny_eh wrote:
| Note that this could easily break or get messy as you make
| changes to your app. Consider either some kind of versioning
| indicator, or be very careful to only add fields, never changing
| them.
 
| benatkin wrote:
| This has taken off in the Vue community w/ https://sfc.vuejs.org/
| and https://uno.antfu.me/play/ and they use the fragment so it
| doesn't get sent to the HTTP server
 
| tonto wrote:
| I always like this when the url is always copyable, but in our
| app, the state can get quite large. We started with base64
| encoded gzip of json.stringified app state but just gets quite
| long. Arguably it could be reduced a little bit, but would still
| be hundreds-thousands of chars routinely, so we switched to using
| a little URL shortening service that we wrote on aws
| lambda+dynamodb
 
| binarymax wrote:
| Shameless plug for bashfill - a fun little drawing program for
| making art for bash stdout:
| https://max.io/bash.html?zip=eDE1eTF4MjRlMXgzeTF4MzB5MXg1ZTF...
| 
| ...made in 2013 :) Here's the js that runs it:
| https://max.io/bash.js
 
| cwt137 wrote:
| A few years ago, I worked on an app where some of the state was
| kept in the session and some was on the url as GET params. We ran
| into an issue where some browsers would only accept urls that
| were less than 1024 chars long. Is that still an issue today?
 
  | aplummer wrote:
  | The limit disappeared after ie9, however can and should are
  | different things here
 
  | werdnapk wrote:
  | Yes. I had a bug report last year to deal with this due to the
  | url being cut off in the browser with Ajax GET requests. I
  | converted to a POST request which has no length limitations.
 
| EGreg wrote:
| It's actually difficult to do this in the UX.
| 
| Suppose that you have a hierarchy that was opened. Do you want to
| store all the hierarchy breadcrumbs in case you hit refresh, and
| reload all of them?
| 
| Furthermore, what about URLs to initiate an action from a
| particular context? If you refresh what should it show? It should
| show a dialog / lightbox above the page, showing the form /
| interface for filling out before taking this action.
 
  | ddyevf635372 wrote:
  | Routes are the fundamental building blocks of a webpage, so the
  | hierarchy will be represented by the routes in the url. Proper
  | frontend framework, like Ember.js, built on top of routes and
  | building a UX focused web app is much easier with them.
  | 
  | https://guides.emberjs.com/release/routing/
 
    | EGreg wrote:
    | Your link and the explanation there is not enough. It only
    | shows the current resource you are looking at. As I said
    | above - you should also consider representing the breadcrumbs
    | of how you got there in the hierarchy (you could have taken
    | multiple paths) and also on top of that use URLs to represent
    | actions that can be taken in dialogs !
    | 
    | Here is a question for you: what happens when you have a url
    | for a New Issue in github? By itself. It renders a form for a
    | new issue full-page. But now what if I want it as an overlay
    | over a hierarchical state that U navigated to? I should have
    | a longer URL and it wouldnt be full screen anymore.
 
| mperham wrote:
| I recall this technique being used in 1999. It doesn't work well
| with caching proxies which key off the URL, using a Cookie header
| is cleaner and simpler.
 
  | easrng wrote:
  | Caching proxies which key off the URL are pretty much useless
  | now because they don't work with HTTPS.
 
| temporallobe wrote:
| Out of necessity for a very similar issue, I actually wrote a
| small Javascript utility for this that does all this
| automatically and uses optional symmetric encryption. Would it be
| against the rules to post it here?
 
| tlavoie wrote:
| As usual when I see this sort of thing, I have questions around
| the security needs of the application. Storing all of this
| information on the client means that the client has to be trusted
| to not mess with all of this state. If it's important, it needs
| to be stored and checked server-side.
 
  | deckard1 wrote:
  | yep. A coworker introduced a CSRF doing this. I had to point
  | out that you can't just take raw input from a URL and throw it
  | back on the page. Even after I pointed it out and gave a proof-
  | of-concept they still didn't get it.
 
    | tlavoie wrote:
    | Right, that's usually when I go into story-telling mode, to
    | paint a picture of exactly the sorts of things I'd be doing
    | with the problem as an attacker. Technical vulnerability
    | descriptions provide useful information, but people often
    | need an idea of what it really means, to them.
 
  | somehnguy wrote:
  | Validating data from the client should always be done on the
  | server in that context, regardless of if state is being saved
  | client side or not. I think this is just an extension of what
  | you said though.
  | 
  | I can think of many apps that don't need server side data
  | storage or security that would benefit from this type of state
  | saving however. For example, this app that I was using today -
  | https://wheelofnames.com/ . Right now to save I need to create
  | an account, presumably to tie my account to the saved state on
  | the server side. That's pretty heavy/intrusive when all I want
  | to do is keep the names list and preferences populated for my
  | next visit.
 
    | tlavoie wrote:
    | Sure, there are reasonable use cases. It's worth pointing out
    | where those use cases are not reasonable though, because
    | those devs that don't know better will be inclined to treat
    | obfuscated inputs as if they're trustworthy.
 
| saghm wrote:
| I imagine I'm not the first one to think of this, but a little
| while back I wrote a toy pastebin-like webpage that stored the
| code being rendered directly in the URL (with base64 and gzip
| compression to make it a bit smaller, although it still was quite
| large by usual standards). My thinking was that rather than
| having a centralized app for this publicly available, making a
| small page that could be statically hosted would make it easy for
| people or organizations to host their own page with whatever
| security they need (e.g. behind a firewall or VPN) and then the
| links could be shared privately on Slack or whatever without a
| need to add functionality for determining who should have access
| or how long to keep the code snippet around and instead just
| piggyback off of the privacy of whatever channel/DM they sent it
| across and how long the Slack instance is configured to retain
| history.
| 
| When I first came up with this idea, I was going to write some
| sort of server backend rather than just doing everything in the
| frontend, but once I started actually working on it, I realized
| that it wouldn't really add any value and it would make it a bit
| more annoying for people to self-host. Since then, I've wondered
| a bit how viable "static pages with state in the URL that can be
| easily self-hosted" would be as an alternative to a desktop GUI
| or Electron app for more technical users. One of the nicest parts
| of this approach to me is that I was able to make in only a few
| hours one evening despite knowing only the basics of webdev from
| maybe 8-10 years ago and next to nothing about programming GUIs
| in general. This makes me think that it wouldn't be too hard for
| people to fork and modify it to their own liking (e.g. changing
| the hard-coded style settings for rendering the code snippets),
| so there could be potential for an ecosystem to grow organically
| around something like this.
| 
| In case anyone is curious to take a look:
| https://gitlab.com/saghm/pastml/
 
  | Ndymium wrote:
  | Great minds think alike? I just posted about my URL-using
  | pastebin: https://news.ycombinator.com/item?id=34315577
 
    | dzaima wrote:
    | There are quite a few of these apparently.
    | https://topaz.github.io/paste/ (lzma) from the creator of
    | Advent Of Code, and mine: https://dzaima.github.io/paste/
    | (pako)
 
| lol768 wrote:
| Am I alone in thinking it's ridiculous for an editor action to
| push/pop state in browser history to allow undo/redo? Surely this
| leads to substantial browser history pollution?
 
  | vasco wrote:
  | You are not wrong. I cannot think of many interactions with an
  | editor where if the user presses the back button they want to
  | undo the last micro action they did, instead of going to the
  | previous page / major UI interaction. Even just trying the demo
  | after adding 3 blocks I had to press back like 5 times to exit
  | the page back to where I was before.
 
  | kccqzy wrote:
  | Absolutely agreed. Given how HN can be when complaining about
  | back button breakage (so common that it had to be explicitly
  | banned), I'm surprised so few people are talking about the
  | implications here.
 
| getToTheChopin wrote:
| It's pretty common to do this using the URI fragment in a link.
| 
| For example: https://themeasureofaplan.com/market-
| timing/#initialContribu...
| 
| This shows the performance of a market timing strategy on the
| S&P500, where you invest $20,000 upfront and $1,000 per month
| thereafter, while only buying when the market is 25%+ away from
| the all-time high price (analysis from 1993 to 2023).
| 
| These user input variables are all directly encoded in the URL,
| after the # symbol.
 
  | iLoveOncall wrote:
  | This is a terrible example though, there is no reason why those
  | shouldn't be normal query parameters.
 
    | getToTheChopin wrote:
    | I don't see why normal query parameters (using ? symbol)
    | would be better than putting the key/value pairs after a #
    | symbol.
    | 
    | They both achieve the purposes of allowing custom URLs that
    | bring the user to a specific state of the app.
 
  | nimzoLarsen wrote:
  | Interesting. Just to make sure I understand -- that url
  | contains all of the IDs and user inputs, and then you parse out
  | of the key/value pairs and feed those inputs into the app?
 
    | getToTheChopin wrote:
    | Yep. See here for an explanation:
    | https://stackoverflow.com/questions/12520124/parse-url-
    | fragm...
    | 
    | Edit: better link to explain the concept
 
| dndn1 wrote:
| I built a quick/wip payroll validator [0] that can read state
| (i.e. payroll details) from the URL.
| 
| The purpose in this case was to generate a QR code, the idea is
| this can be included in physical payslips. Then the numbers
| behind the calculations (tax deductions) can be validated, broken
| down, and understood. I'm developing more tools to these ends,
| via my overarching project calculang, a language for calculations
| [1].
| 
| I also have a loan/repayment validator [2] but haven't added this
| QR code feature yet.
| 
| Bank letters e.g. "Interest rates are rising and now we want 100e
| more per month, every month" could use a QR code to an
| independent validator or to see the workings behind the 100
| calculation.
| 
| Not using this in the real world now and there are security
| considerations to keep in mind, but reading state from a URL
| facilitates the usecase: QR codes that link physical numbers to
| their calculation and model.
| 
| Implementation of payroll calculator is an Observable notebook
| and thankfully it neatly supports all my strict requirements as
| demo of this.
| 
| [0] https://observablehq.com/@declann/payroll-playground-
| ireland...
| 
| +Feature tweet:
| https://twitter.com/calculang/status/1608183731533107206
| 
| [1] https://github.com/calculang/calculang
| 
| [2] https://observablehq.com/@declann/loan-validator-dev
 
| cpa wrote:
| This is a handy trick that I've used on multiple occasions.
| However, it may not work on some corporate networks that filter
| URLs with more than a certain number of characters (I've
| encountered limits of 2048 and 4096 characters).
| 
| The rationale being that long URLs are often suspicious and could
| potentially be used for SQL injection or path traversal attacks.
| Whether or not this is a good heuristic is left as an exercise to
| the reader.
 
| Ndymium wrote:
| Last November I had covid and during that time I wrote a little
| pastebin that runs in the browser, compresses the paste data with
| Brotli, and puts it in the URL. It has line numbers, file naming,
| and syntax highlighting so it's feature complete for my own use.
| Here's a demo, but beware, the URLs are loooong:
| https://nicd.gitlab.io/t/#NhfW?Qm3F?6-&22c&CQZXuP+Aej5OXzXk7...
| 
| I find it interesting to sometimes play around with the pasted
| data just to see how it compresses. Sometimes adding a character
| or two may cut several characters from the URL length!
 
| holdenk wrote:
| I did something similar using Racket's (nee PLT scheme) web
| programming module (
| https://www2.ccs.neu.edu/racket/pubs/hosc07-sk-mf.pdf ) back with
| an app that got on slashdot --
| https://github.com/holdenk/web2.0collage
| 
| The theory was wonderful (yay! stateless load balancer etc.) but
| in practice browsers at the time were less than happy with trying
| to store that much state in the URL.
 
| jwoglom wrote:
| Like some other commenters mentioned, this is a cool idea,
| however by storing JSON data inside base64, the length of the URL
| blows up very quickly as you start storing more state.
| 
| While technically URLs have no formal length limit, various
| sources suggest that URLs with more than around 2,048 characters
| start causing issues in browsers, and around 8,196 start causing
| issues in CDNs (https://stackoverflow.com/a/417184). You can work
| around this partially by not storing this state information in
| the query string (path/to?) and instead using a hash
| string (path/to#), and then extract that data in
| JavaScript such that it's not sent to the server at all.
| 
| IMO, the canonical way to solve this problem is using protocol
| buffers, which you can then serialize into a much smaller number
| of bytes, which is then base64 encoded. For example, as mentioned
| in another comment in this thread
| (https://news.ycombinator.com/item?id=34314578), Yahoo Finance
| has a 1,616 character-long URL with 1,572 characters of
| base64-encoded JSON state:
| 
| > {"interval":"week","periodicity":1,"timeUnit":null,"candleWidth
| ":4.3486590038314175,"flipped":false,"volumeUnderlay":true,"adj":
| true,"crosshair":true,"chartType":"line","extended":false,"market
| Sessions":{},"aggregationType":"ohlc","chartScale":"linear","stud
| ies":{" vol undr ":{"type":"vol undr","inputs":{"id":" vol undr
| ","display":" vol undr "},"outputs":{"Up Volume":"#00b061","Down 
| Volume":"#ff333a"},"panel":"chart","parameters":{"widthFactor":0.
| 45,"chartName":"chart"}}},"panels":{"chart":{"percent":1,"display
| ":"F","chartName":"chart","index":0,"yAxis":{"name":"chart","posi
| tion":null},"yaxisLHS":[],"yaxisRHS":["chart"," vol undr "]}},"se
| tSpan":{"multiplier":5,"base":"year","periodicity":{"period":1,"i
| nterval":"week"}},"lineWidth":2,"stripedBackground":true,"events"
| :true,"color":"#0081f2","stripedBackgroud":true,"eventMap":{"corp
| orate":{"divs":true,"splits":true},"sigDev":{}},"customRange":nul
| l,"symbols":[{"symbol":"F","symbolObject":{"symbol":"F","quoteTyp
| e":"EQUITY","exchangeTimeZone":"America/New_York"},"periodicity":
| 1,"interval":"week","timeUnit":null,"setSpan":{"multiplier":5,"ba
| se":"year","periodicity":{"period":1,"interval":"week"}}}]}
| 
| The un-base64'd JSON dictionary is 1,162 characters long, 569 of
| which is composed of just the key names (48 percent!), and most
| of the remaining fields likely contain their default value. If
| this was instead encoded in protobuf, the key names wouldn't need
| to be included, since fields are referenced by their field ID,
| and default field values would take up little-to-no space since
| when the default value is present it isn't encoded into the
| structure. I presume that, if done efficiently, you could likely
| make an equivalent representation in protobuf that is 1/4th or
| even 1/8th the size. You also get, for better or for worse, what
| is essentially obfuscation for free (yes, you can read out the
| packed format, but without an equivalent protobuf struct
| definition it's still hard to tell what field is which, and the
| entire operation becomes much more labor-intensive).
 
  | secondcoming wrote:
  | Excellent, I was waiting for the protobuf suggestion.
 
| ladberg wrote:
| I tried to do that at first with https://textshader.com because
| the whole site is client-side and I didn't want to bother with a
| backend. It didn't really work because the state is unbounded in
| size, so instead I just chuck it on GitHub gists and then point
| to the gists. It means I don't have to store any data myself and
| there's zero recurring costs outside of the domain name... but if
| the site ever got popular I'd probably have to figure something
| else out.
 
  | didericis wrote:
  | I dream of a world where there's some kind of ipfs like thing
  | for storing state in some sort of distributed commons, all
  | while maintaining user privacy/allowing for state to expire if
  | untouched, and monetized in a balanced way to incentivize
  | hosting without discouraging consumer adoption too
  | much/trending towards gouging.
  | 
  | We're so close/all the pieces needed seem to exist, but getting
  | something like that off the ground is super difficult.
 
| IYasha wrote:
| I'm not into web "programming", but do people really have to
| abndon binary data storage these days? I think compressing more
| informaiton produces still more information. While JSON is by no
| means as heavy as, for example, XML, it's far heavier than just
| array of int graph[x][y];
 
  | Zamicol wrote:
  | At work we have two api endpoints. One for JSON and the other
  | for binary.
 
  | recursive wrote:
  | To put it into a url, you'll still need to serialize to text,
  | even if the underlying data is binary.
 
  | misnome wrote:
  | A colleague has been tasked with writing a remote image viewer.
  | It's slower than before (running on the same machine) "because
  | it's client-server".
  | 
  | Naturally, the 16 Megapixel images are sent as an array of json
  | floats...
 
    | secondcoming wrote:
    | Unless it's still at a proof-of-concept stage, that person is
    | out of their depth.
 
      | misnome wrote:
      | Yes, yes they are. Unfortunately this is dysfunctional
      | academia, so as long as the people paying the bills (who
      | know nothing about building software) like them, they
      | remain at this depth.
      | 
      | Extra fun fact: Two years into the project, they hadn't
      | heard of REST. Then rejected it, because, I quote, "The S
      | means stateless and [they] need the server to store state"
 
| threeseed wrote:
| This technique goes back to the late 90s where Apple WebObjects
| (owned by NeXT at the time) pioneered this approach in their web
| framework.
| 
| https://en.wikipedia.org/wiki/WebObjects
| 
| Was quite revolutionary at the time and it also included the
| first (or one of the very earliest at least) SQL ORMs.
 
| hardwaregeek wrote:
| I love doing this for playgrounds! It's so nice to be able to
| share a link with the code embedded. Especially if you're giving
| someone a bug report and they need a minimal reproduction.
 
  | nerdponx wrote:
  | I appreciate this feature a lot on sites like https://tio.run
  | and https://mermaid.live/. However the URLs also get very long
  | and I often want to run them through a shortener for e.g.
  | posting to IRC. But there are worse problems to have!
 
| rickreynoldssf wrote:
| Your SEO guy is going to throttle you if you even consider
| thinking about doing this.
 
| eatonphil wrote:
| While this is great for being able to send anyone a URL to a
| stateful page without needing a backend database, keep in mind
| browsers have URL length limits and they vary by browser.
| Compressing as mentioned in the article can help but still you
| want to know when you're going to go over and figure out a plan
| for dealing with that.
 
  | vbezhenar wrote:
  | Chrome limits URLs to a maximum length of 2MB
  | 
  | It's more likely that messaging app will have stricter limit
  | than Chrome (and other browsers at this point do not matter).
  | 
  | Though I'd suggest to use some kind of server storage if you
  | need to transfer more than few kilobytes of data.
 
    | eatonphil wrote:
    | Great point too. At least you can count on being able to send
    | over email.
    | 
    | But yeah, the length will get long and even if the browser
    | can handle it do you really want to be passing around that
    | much text at once?
    | 
    | I'm also curious what various OS clipboard size limits are.
 
| swyx wrote:
| This is quickly becoming a standard in apps and it really
| shouldnt be handrolled since its such a common requirement and
| easy to get wrong (between serializing/deserializing/unsetting
| states). In Svelte it is now as easy as using a store:
| https://github.com/paoloricciuti/sveltekit-search-params
| 
| in general i've been forming a thesis [0] that there is a webapp
| hierarchy of state that goes something like:
| 
| 1. component state (ephemeral, lasts component lifecycle)
| 
| 2. temp app state (in-memory, lasts the session)
| 
| 3. durable app state (persistent, whether localstorage or
| indexeddb or whatever)
| 
| 4. sharable app state (url)
| 
| 5. individual user data (private)
| 
| 6. team user data (shared)
| 
| 7. global data (public)
| 
| and CRUD for all these states should be as easy to step down/up
| as much as possible with minimal api changes as possible
| (probably a hard boundary between 4/5 for authz). this makes
| feature development go a lot faster as you lower the cost of
| figuring out the right level of abstraction for a feature
| 
| 0: relatedly, see The 5 Types of React Application State, another
| post that did well on HN
| https://twitter.com/swyx/status/1351028248759726088
| 
| btw my personal site makes fun use of it -> any 404 page takes
| the 404'ed slug and offers it as a URL param that fills out the
| search bar for you to find the link that was broken, see
| https://www.swyx.io/learn%20in%20public
 
  | WaffleIronMaker wrote:
  | Does anyone know of solutions for this in React?
 
    | austinpena wrote:
    | Tanstack is working on a router that purports to solve a lot
    | of this.
    | 
    | https://tanstack.com/router/v1
 
  | enos_feedler wrote:
  | So you think of a new feature "X". That feature has a bunch of
  | state it needs to manage. Each piece of state has
  | _requirements_ over where it fits within this hierarchy based
  | on how this state relates to the feature being developed. I
  | don't understand where the cost lowering is? I know instantly
  | where the state should live when I design the feature.
 
    | swyx wrote:
    | requirements evolve over time. and they tend to go up in
    | terms of persistence/auth/sharability needs. it would be nice
    | to make that easy; and conversely when they arent needed
    | anymore it'd be nice to take the state persistence level down
    | with just a few character changes rather than switching state
    | systems entirely
 
  | mhink wrote:
  | Note on 2: I haven't seen this get a lot of use (or maybe I
  | just haven't noticed it), but the Storage API also provides a
  | `sessionStorage` object [1] with some interesting properties.
  | Not only are values persisted across same-tab refreshes, but if
  | you duplicate the tab, the session values are copied over as
  | well (but they don't remain linked like `localStorage`). An
  | interesting use case for this, IMO, is for tracking dynamic
  | layout values (like if you have resizable panels, scroll areas,
  | etc). If you keep this kind of thing in sessionStorage, you can
  | refresh or duplicate the working tab and that ephemeral state
  | will carry over, but since it's copied (not shared) the tabs
  | won't step on each other.
  | 
  | 1: https://developer.mozilla.org/en-
  | US/docs/Web/API/Window/sess...
 
    | skrebbel wrote:
    | Unfortunately sessionStorage is not copied over when you
    | ctrl+click a link, and that's arguably the most common way to
    | "duplicate" a tab. Something about security iirc but i dont
    | grok it.
    | 
    | Still an underappreciated browser feature though, esp for
    | class server-side rendered apps.
 
      | mhink wrote:
      | I'm actually kinda surprised that's the case for links to
      | the same origin. I suppose you could probably still get
      | this working by using `window.opener` along with
      | `postMessage` to request data from the opener, although
      | you'd have to be very conscientious about checking the
      | origin and verifying that the requested data is actually ok
      | to share.
 
    | swyx wrote:
    | i was thinking 2 is more like Redux, btw :)
    | 
    | ive never honestly used sessionStorage as it's not intuitive
    | to me (if i want it clientside i should probably track it
    | serverside, and at that point server is my source of truth)
 
    | s4i wrote:
    | Yup. Session storage is particularly useful for remembering
    | the initial URL of the user when they leave your app for SSO.
 
| phkahler wrote:
| I did this for an Othello game back in the 90's. The state was
| encoded in the URL along with the players move. It seemed weird
| to encode a slightly different URL for every move the player
| might click, but that also meant squares that were not legal
| moves didn't get a URL. And that meant the mouse pointer would
| change when you hovered over a valid square! Another trick was to
| return the updated board state, but also include an automatic
| reload after a few seconds with the computers next move already
| determined. This made it look like "thinking" was going on when
| in fact it was already decided when the board updated on players
| move ;-) This was all in a CGI script. As others have pointed
| out, it also supported "undo" via the back button. Fun.
 
| divbzero wrote:
| App state in URL can be a good idea, but if possible I prefer
| readable path/query parameters instead of unreadable base64
| encoding.
| 
| As one comparison, this is Google Finance encoding stock chart
| parameters:
| https://www.google.com/finance/quote/F:NYSE?window=5Y
| 
| Versus Yahoo! Finance doing the same:                 https://fin
| ance.yahoo.com/quote/F/chart?p=F#eyJpbnRlcnZhbCI6IndlZWsiLCJwZXJp
| b2RpY2l0eSI6MSwidGltZVVuaXQiOm51bGwsImNhbmRsZVdpZHRoIjo0LjM0ODY1O
| TAwMzgzMTQxNzUsImZsaXBwZWQiOmZhbHNlLCJ2b2x1bWVVbmRlcmxheSI6dHJ1ZS
| wiYWRqIjp0cnVlLCJjcm9zc2hhaXIiOnRydWUsImNoYXJ0VHlwZSI6ImxpbmUiLCJ
| leHRlbmRlZCI6ZmFsc2UsIm1hcmtldFNlc3Npb25zIjp7fSwiYWdncmVnYXRpb25U
| eXBlIjoib2hsYyIsImNoYXJ0U2NhbGUiOiJsaW5lYXIiLCJzdHVkaWVzIjp7IuKAj
| HZvbCB1bmRy4oCMIjp7InR5cGUiOiJ2b2wgdW5kciIsImlucHV0cyI6eyJpZCI6Iu
| KAjHZvbCB1bmRy4oCMIiwiZGlzcGxheSI6IuKAjHZvbCB1bmRy4oCMIn0sIm91dHB
| 1dHMiOnsiVXAgVm9sdW1lIjoiIzAwYjA2MSIsIkRvd24gVm9sdW1lIjoiI2ZmMzMz
| YSJ9LCJwYW5lbCI6ImNoYXJ0IiwicGFyYW1ldGVycyI6eyJ3aWR0aEZhY3RvciI6M
| C40NSwiY2hhcnROYW1lIjoiY2hhcnQifX19LCJwYW5lbHMiOnsiY2hhcnQiOnsicG
| VyY2VudCI6MSwiZGlzcGxheSI6IkYiLCJjaGFydE5hbWUiOiJjaGFydCIsImluZGV
| 4IjowLCJ5QXhpcyI6eyJuYW1lIjoiY2hhcnQiLCJwb3NpdGlvbiI6bnVsbH0sInlh
| eGlzTEhTIjpbXSwieWF4aXNSSFMiOlsiY2hhcnQiLCLigIx2b2wgdW5kcuKAjCJdf
| X0sInNldFNwYW4iOnsibXVsdGlwbGllciI6NSwiYmFzZSI6InllYXIiLCJwZXJpb2
| RpY2l0eSI6eyJwZXJpb2QiOjEsImludGVydmFsIjoid2VlayJ9fSwibGluZVdpZHR
| oIjoyLCJzdHJpcGVkQmFja2dyb3VuZCI6dHJ1ZSwiZXZlbnRzIjp0cnVlLCJjb2xv
| ciI6IiMwMDgxZjIiLCJzdHJpcGVkQmFja2dyb3VkIjp0cnVlLCJldmVudE1hcCI6e
| yJjb3Jwb3JhdGUiOnsiZGl2cyI6dHJ1ZSwic3BsaXRzIjp0cnVlfSwic2lnRGV2Ij
| p7fX0sImN1c3RvbVJhbmdlIjpudWxsLCJzeW1ib2xzIjpbeyJzeW1ib2wiOiJGIiw
| ic3ltYm9sT2JqZWN0Ijp7InN5bWJvbCI6IkYiLCJxdW90ZVR5cGUiOiJFUVVJVFki
| LCJleGNoYW5nZVRpbWVab25lIjoiQW1lcmljYS9OZXdfWW9yayJ9LCJwZXJpb2RpY
| 2l0eSI6MSwiaW50ZXJ2YWwiOiJ3ZWVrIiwidGltZVVuaXQiOm51bGwsInNldFNwYW
| 4iOnsibXVsdGlwbGllciI6NSwiYmFzZSI6InllYXIiLCJwZXJpb2RpY2l0eSI6eyJ
| wZXJpb2QiOjEsImludGVydmFsIjoid2VlayJ9fX1dfQ--
| 
| In both examples, the only custom parameter is setting the time
| window to 5 years.
 
  | BurningFrog wrote:
  | The possible advantage of the encoding is that it that it can
  | be encrypted and make parameter hacking impossible.
 
    | PetahNZ wrote:
    | You don't need to encode it to do that though. You can just
    | sign the URL
 
    | A4ET8a8uTh0 wrote:
    | Harder. Not impossible. Harder. I don't want to make it sound
    | like I am being disagreeable just for the sake of being
    | disagreeable. If there is one thing the past decade has
    | shown, it is that hacking is just a matter of time and
    | whether a determined person is willing to direct resources at
    | it.
 
      | rsync wrote:
      | I see you, and raise you:
      | 
      | What if they encrypted the parameters _with a one-time pad_
      | ?
 
        | A4ET8a8uTh0 wrote:
        | Hmm. Most likely ( if not only ) way to counter that
        | would be social hacking ( because I assume the pad is
        | generated automatically from some source ), which seems
        | like the best way to obtain it. Then again.. I am not an
        | expert in this field ( but we do sometimes use parameters
        | from link for some projects ). Is it refreshed after some
        | time passes ( if so, maybe there is an easier way to just
        | observe what changes )?
 
    | hot_gril wrote:
    | Would have to do the signing server-side, predicated on the
    | server checking the entire state to ensure validity. Such
    | effort being put into preventing parameter-hacking would
    | suggest there are serious vulnerabilities in that website.
    | The URL is meant to be modified by the client.
 
      | hot_gril wrote:
      | > The URL is meant to be modified by the client.
      | 
      | Clarification, the _human user_ probably won 't want to
      | modify the URL, but the client will.
 
    | rattlesnakedave wrote:
    | Yeah, why not just base64 encode, shove that into a db with a
    | shorter key? I'm pretty positive this is how tiktok works for
    | sharing videos.
 
  | [deleted]
 
  | spullara wrote:
  | Here is it decoded:
  | 
  | {"interval":"week","periodicity":1,"timeUnit":null,"candleWidth
  | ":4.3486590038314175,"flipped":false,"volumeUnderlay":true,"adj
  | ":true,"crosshair":true,"chartType":"line","extended":false,"ma
  | rketSessions":{},"aggregationType":"ohlc","chartScale":"linear"
  | ,"studies":{" vol undr ":{"type":"vol undr","inputs":{"id":"
  | vol undr ","display":" vol undr "},"outputs":{"Up
  | Volume":"#00b061","Down Volume":"#ff333a"},"panel":"chart","par
  | ameters":{"widthFactor":0.45,"chartName":"chart"}}},"panels":{"
  | chart":{"percent":1,"display":"F","chartName":"chart","index":0
  | ,"yAxis":{"name":"chart","position":null},"yaxisLHS":[],"yaxisR
  | HS":["chart"," vol undr "]}},"setSpan":{"multiplier":5,"base":"
  | year","periodicity":{"period":1,"interval":"week"}},"lineWidth"
  | :2,"stripedBackground":true,"events":true,"color":"#0081f2","st
  | ripedBackgroud":true,"eventMap":{"corporate":{"divs":true,"spli
  | ts":true},"sigDev":{}},"customRange":null,"symbols":[{"symbol":
  | "F","symbolObject":{"symbol":"F","quoteType":"EQUITY","exchange
  | TimeZone":"America/New_York"},"periodicity":1,"interval":"week"
  | ,"timeUnit":null,"setSpan":{"multiplier":5,"base":"year","perio
  | dicity":{"period":1,"interval":"week"}}}]}
  | 
  | I bet if they were more aggressive in using defaults it would
  | be a lot smaller.
 
    | a_c wrote:
    | OT, we used to joke about LISP source code contains mostly
    | brackets. Nowadays we literally store, transform and pass
    | around willy nillily
 
    | _fat_santa wrote:
    | I suspect it's probably a global library that handles this.
    | So refactoring it in one place would be difficult. If I had
    | to guess their implementation looks something like this.
    | 
    | SomeUrlService.pushState(JSON.stringify({ foo: 'bar' }));
    | 
    | SomeUrlService.navigate('/some/url/here')
    | 
    | Likely, that "SomeUrlService" is also tacking on plenty of
    | other shit to the URL that is passed along. I don't work for
    | Yahoo and never have, so this is all pure speculation.
 
      | spullara wrote:
      | Having worked there years and years ago, I doubt it. All
      | those parameters look like chart parameters to me, probably
      | local to Y! Finance.
 
  | [deleted]
 
  | hot_gril wrote:
  | I decoded that, gzipped --best, then re-encoded and got
  | something that's shorter at least
  | 
  | H4sIAAAAAAACA7VU3W7TMBS+5ykmcxuNdOlGyd3QmECCIdYWNKYKnSanqVfHNrb
  | TNqoq7QF4Sp6EYyfpaMYtdz7H5/vOz3fsHePSoVmDYCnbIK5YxDQarnKecVezdB
  | Axx0ucSu5YKishIpaBzAV+47lbsnR4mgxHF+dv4jgZJYPh4PV5xBaCa405SxcgL
  | EZsrUTlKXI0AojTmYq8kD90x8woa5fAzcGxBOMmtUaqSnCJVBVuHRLDE2sJZoVu
  | jNZyJS1Ld3viLAqDBTjytGi1FBlrCccZiI4RDHmtq3KOHst+P/6iMk8qmRs6eo9
  | rCDovhXOpKxeiOZXRh0Qs51aHBvtXVJmqXAee6pOvYSIU+DKO5/HFgMBXaiP/8i
  | 8WSZKAR2qQ6NUJLXh5wECJJFog23gZriFzioYXnw7P215vIPA0oP2+4wmYxkkHU
  | jpD6YLKT8Vfs39x+PZz3FKSiNWXWx6Y5HGEVpb72TebQjlroMCP78csvZ+11m2w
  | DpDepGa+UkuyUrU+QVkJx7XgSN1Ra3OwPl/dyHe0qLvWDM30l9qzetXbpT3z0ht
  | OO/oWslVhFKXvVg/XNBF7WEQl/GC9TqPB4ow9Ax7jPoEOA1ZGKwMOvZHz9YGORs
  | w7ct8oL65w7VeXjKyyTpW3IAvsXpqty7nymt3v2nMrT2N8nj9gFoQ8uvxZKYft+
  | r/7Mv0wuQvPhyZO1BN6zN+V9HeXJQ0sg1c3uPlxp8wqLFvv7T//Hfq/wf8Qaz/b
  | v/gDbvc6N5oEAAA=
 
    | dyno12345 wrote:
    | you could come up with a better compression scheme given that
    | the dict keys and many of the values are probably common
    | across everyone even if they don't repeat multiple times for
    | a given user. a lot of flexibility would be lost though, and
    | you would need to always update the scheme in a backwards-
    | compatible way.
 
      | mikepurvis wrote:
      | Once you've already given up on human-readability, it
      | probably doesn't much matter how long/gross the result is.
      | Rather than spending effort and adding complexity, just
      | provide a built in URL shortener.
 
        | hot_gril wrote:
        | I agree, same reason people use json in the first place
        | despite the inefficiency, it's easier.
 
      | hot_gril wrote:
      | Could start with a smaller encoding. Use single-length
      | keys, or better yet use protocol buffers or even ASN1.
      | That's similar to taking advantage of common key names.
 
      | waynesonfire wrote:
      | would it be feasible to include the compression dictionary
      | along w/ the data? And, maybe just send it once on the
      | initial request and store it on the client?
 
  | michaelmior wrote:
  | In your example, absolutely the first URL is better. But if as
  | in the case of the OP, you're trying to encode an entire
  | flowchart, I would argue it's a bit of a pointless exercise.
  | Maybe you could come up with something reasonably readable, but
  | I don't think anyone would care. It really depends on what kind
  | of state you need to store.
 
  | thewisenerd wrote:
  | ah, azure does the same for generating the "share" URL for
  | their cost dashboards.
  | 
  | it additionally gzip's the data to compress it futher, with..
  | some savings..
  | 
  | in the above example you've shared, gzip would've,
  | orig=1177       base64=1572       gzip(orig)|base64=768
 
  | orasis wrote:
  | Another reason to not base64 encode is that many URLs with
  | base64 strings will break in iMessage do to strings randomly
  | matching various keywords that iMessage looks for. I think
  | 'usd' is one such substring to look out for.
 
    | secondcoming wrote:
    | Surely that's something that Apple should fix, rather than
    | everyone else working around it?
 
    | slivanes wrote:
    | base64 does not have the problem here, iMessage has.
    | Frustrating.
 
      | jimbokun wrote:
      | Agreed, iMessage should have URL detection that supersedes
      | any other string parsing.
 
      | nanidin wrote:
      | Curious if English is your mother tongue, and if so, which
      | locale?
      | 
      | As a native speaker of en-US, I would write this as "base64
      | does not have the problem here, iMessage does."
 
        | elteto wrote:
        | Right before I was forked my parent did "export LC_ALL=C"
        | and here I am.
 
        | slivanes wrote:
        | Originally en-AU. I was trying to match the "have" with a
        | "has". My sentence has a truncated " the problem" in my
        | head.
        | 
        | Your version reads more correct, thanks for pointing it
        | out.
 
  | hot_gril wrote:
  | I think the other params are custom too. There's probably other
  | state not being stored in that URL. If users want to send
  | charts to each other and have them show up the same, this
  | actually seems like a reasonable way to do it.
 
  | voidmain0001 wrote:
  | Pass that super duper long Yahoo URL to Tinyurl and shrink it
  | down to https://tinyurl.com/bdfepwar
  | 
  | It seems like storing state in the URL could benefit from URL
  | shortening techniques so long as the secret sauce logic is both
  | client and server side.
 
    | stnmtn wrote:
    | This loses all the benefits of storing all state in the url,
    | which is that it's inspectable by the client. Using a URL
    | shortener still makes all the data stored somewhere in the
    | server
 
      | voidmain0001 wrote:
      | Okay that's not exactly what I had in mind. My suggestion
      | was to implement the same hash both server and client side
      | so that the query string is small and manageable. Don't
      | worry about the state not being fully legible because you
      | will implement the hash decode on the client too. The code
      | will be there for you to peruse.
 
  | anshumankmr wrote:
  | >
  | https://finance.yahoo.com/quote/F/chart?p=F#eyJpbnRlcnZhbCI6...
  | 
  | Dear god... this link was real. I thought it was like a joke or
  | something
 
    | PUSH_AX wrote:
    | Genuinely curious to hear why you are so shocked. Long, ugly,
    | user unfriendly urls are really prevalent. Arguments can be
    | made even query params are pretty ugly.. Are urls expected to
    | be a good UX these days? What's the big deal?
 
      | hunter2_ wrote:
      | I don't think people care so much about a messy address bar
      | as a messy chat/email message after pasting a URL.
      | Shorteners and vanity URLs exist, but that's more friction
      | than just copying from the address bar as-is.
      | 
      | This can be overcome with html, markdown, and other rich
      | text formats that let you specify the visible link text
      | (missing from many chat apps*), but that's also friction to
      | compose compared to automatically linking from just a URL.
      | 
      | *Slack's implementation is awesome: type the link text,
      | highlight it, and paste a URL.
 
        | owl57 wrote:
        | It's not even only about messiness.
        | "https://news.ycombinator.com/item?id=34315441" isn't
        | some horrible long base64, but it's not a good UX either:
        | when you paste it in a messenger, or in your notes, by
        | default the reader can't tell what's it about without
        | clicking.
        | 
        |  _> *Slack 's implementation is awesome: type the link
        | text, highlight it, and paste a URL._
        | 
        | Yes! Matches my vision of hypertext perfectly: I usually
        | type a message in normal human language and then paste
        | links all over it for the reader who needs to dig deeper.
 
        | TheBrokenRail wrote:
        | > by default the reader can't tell what's it about
        | without clicking.
        | 
        | Most chat apps like Discord will automatically include an
        | embed generated from a URL's metadata when present in a
        | message.
 
      | munk-a wrote:
      | Just because a thing is prevalent doesn't mean it's good. I
      | have a hard time believing all those parameters are
      | completely necessary for the amount of data being
      | displayed. Often times it feels like people just
      | base64encode(page.state) and call it a day.
      | 
      | I think it's advantageous to keep the complexity and
      | specificity of information in URLs to a minimum outside of
      | what's needed to retrieve the data set as it can make
      | backwards compatibility easier - it is valuable (for long
      | lasting tools) to have URLs that users can favorite and
      | share for their common searches.
 
        | PUSH_AX wrote:
        | > Just because a thing is prevalent doesn't mean it's
        | good.
        | 
        | I agree with this sentiment. I'm not sure I've ever seen
        | a url referred to as "good", I was just a bit confused as
        | to why someone would baulk at a URL. In my opinion
        | everything after the host portion of the url is to serve
        | the apps needs, so it knows where to go or what to do,
        | it's not _really_ an interface for the user as obviously
        | there are better ways to present that.
 
        | MereInterest wrote:
        | I'd say everything after the host is part of the UX, and
        | should serve the user needs. For example:
        | 
        | * Should be book-markable.
        | 
        | * If searching with a query, should have a clear query
        | parameter for ease of browser integration.
        | 
        | * If the URL contains slash dividers that can be read as
        | categories (e.g.
        | storefront.com/department/category/product), then
        | deleting everything after a given slash should go to that
        | category's page.
        | 
        | * The link should be short enough to be sent over a
        | plain-text conversation without becoming a wall of text.
        | 
        | * The link should be short enough that it cannot hide a
        | malicious subdomain (e.g.
        | Google.com.flights.malicio.us/more/elements).
 
        | hinkley wrote:
        | * I should be able to guess if you're sending me a link
        | about composting or a Rick Astley video
        | 
        | Opaque slugs are problematic in a low-trust world.
        | Phishing attempts are easier when the parameters are
        | indecipherable. I'm not clicking on that shit in a text
        | message or an email. I've seen what happens to people
        | when they do.
 
        | whatusername wrote:
        | No - but they have been referred to as "cool" before.
        | Gotta love the early 90's.
        | 
        | https://www.w3.org/Provider/Style/URI
 
    | xwdv wrote:
    | There are no jokes on HN.
 
      | anotheraccount9 wrote:
      | A horse walks into a bar.
      | 
      | Several of the patrons quickly get up and leave, realizing
      | the potential danger in the situation.
 
      | BlueNorth wrote:
      | There are different languages. The jokes are symptomatic of
      | the differences. I'm not Indian but English is laughable
      | for many good reasons. Without damage...
 
      | scott_s wrote:
      | There are. We just have a high bar. I wrote this, but
      | others have also pointed to it:
      | https://news.ycombinator.com/item?id=7609289
 
      | 2ICofafireteam wrote:
      | I did once and was told off for adding noise to the
      | discussion by someone who didn't see the irony of their
      | complaint also adding to the noise.
 
        | xwdv wrote:
        | For many HN, telling someone off for making a joke isn't
        | noise but rather music to their ears.
 
        | macintux wrote:
        | If a one-time correction has its desired impact, the
        | small addition to the noise reduces future noise.
 
        | ant6n wrote:
        | This is some sort of meta joke, right?
 
        | throw10920 wrote:
        | It's not, and it's pretty clearly true.
        | 
        | The idea that "complaints about people violating the HN
        | norms is somehow comparable to violating the norms" is
        | trivially wrong if one thinks about it for more than a
        | few seconds.
 
        | munk-a wrote:
        | Why are you assuming they work in FAANG?
 
        | gerdesj wrote:
        | There's no M in FAANG ...
 
        | anotheraccount9 wrote:
        | Two chemists walk into a bar. The first says "I'll take a
        | glass of H2O." The second says "I'll take a glass of H2O
        | too."
 
        | valvar wrote:
        | Oh no...
 
    | [deleted]
 
| barbazoo wrote:
| > Another great benefit is that these urls can be embedded. That
| means the user can put their graph on any ewb page that supports
| embedding. I see people typically do this with wikis like Notion,
| which means you can share with a team
| 
| This is so valuable that it can't be overstated. Being able to
| store the raw data in a link WITH the visual representation,
| you'll never struggle trying to find out who has the latest
| version.
 
| ecshafer wrote:
| What is old is new again.
| 
| Back before websites were "Apps", you could often do exactly this
| (Including Authentication!) right from the url.
 
  | Zamicol wrote:
  | Before cookies, I remember in middle school (ebay?) using the
  | URLs for sessions.
  | 
  | If they forgot to include the session token on any link on the
  | page, you'd be logged out and have to authenticate again, or
  | you could navigate backwards back to a sessioned address in
  | your history. Once I figured that out, I copy and pasted my
  | session id so if they omitted the session on a link I wouldn't
  | have to log in again.
 
| ceritium wrote:
| I did the same for https://markdowntable.jose.gr
| 
| As some comments suggest, I will move the data from the query
| string to the URI fragment to avoid the length limitation.
 
| zX41ZdbW wrote:
| I'm successfully* using it in a few personal projects:
| 
| ClickHouse Playground:
| https://play.clickhouse.com/play?user=play#U0VMRUNUIDE=
| ClickHouse Dashboards:
| https://sql.clickhouse.com/dashboard#eyJob3N0IjoiaHR0cHM6Ly9...
| 
| The second example shows you why it is not an entirely good idea.
| The second link cannot be posted in Slack and Google Meet, so I
| have to use an URL shortener...
| 
| Luckily I have it in my disposal: https://pastila.nl/ Example:
| https://pastila.nl/?02a0d469/8f49f7e4406bd4fbc0e7b142c470dfb...
 
| devmunchies wrote:
| Seems like it could be better to store the state in a database
| and create a unique, short ID that goes in the URL, especially if
| your doing server rendering (like SSR react or HTMX, or
| rails/Django)
| 
| > I also didn't want to store anything on the backend (at least
| for the free tier).
| 
| You can make the state unique in the DB by a user cookie ID and
| then upsert the state which will overwrite older state with the
| latest state. Then you only have 1 record per free user. Remove
| record that haven't been modified in 30 days and you don't have
| much overhead. Just off the cuff thinking.
 
  | jonny_eh wrote:
  | > You can make the state unique in the DB by a user cookie ID
  | 
  | https://gdpr-info.eu/
 
  | ErrantX wrote:
  | I am genuinely intrigued as to why that's better?
  | 
  | Especially as in the second part of your comment you restrict
  | shareability/usability significantly from what the author wants
  | to achieve (IE I can't as a free user bash out a diagram and
  | share it, then later do another and share with a different
  | person).
  | 
  | In fact the solution is so elegant when you think on it; free
  | account volume is incredibly scalable, zero storage cost, offer
  | immediate value but also a really simple upsell to pro accounts
  | (save to the DB so you don't have to save the URls).
 
    | devmunchies wrote:
    | > Especially as in the second part of your comment you
    | restrict shareability/usability significantly
    | 
    | no it doesn't. If my URL is `/thing#a1234` and I share it
    | with you it will load my state for you. If you make a change,
    | it's easy to push a new hash to the url that belongs to you.
    | 
    | > I am genuinely intrigued as to why that's better?
    | 
    | I didn't say it was better. We engineers are supposed to
    | discuss solutions. I ended my last comment with "Just off the
    | cuff thinking".
 
      | kamikaz1k wrote:
      | not my fight, but you literally wrote:
      | 
      | > Seems like it could be better
      | 
      | Maybe you didn't mean it that way, but it does come across
      | that way. Just a data point for you; I got no beef...
 
| tresilience wrote:
| This brings to mind the concept of continuation found in Seaside
| Smalltalk.
| 
| https://www.seaside.st
 
| MutableLambda wrote:
| But it was done already in widely known in narrow circles
| PlantUML years ago?
 
| mguerville wrote:
| Isn't this also how old school games (I've seen it as late as the
| NES I think) did allow you to save progress, you'd get a short
| string as your "save" and could just enter it to restart the game
| in the same state later. I love that it offers essentially
| unlimited "saves"
 
  | jonny_eh wrote:
  | Great YouTube channel about reverse engineering old game
  | password systems:
  | https://www.youtube.com/playlist?list=PLzLzYGEbdY5nEFQsxzFan...
 
  | colejohnson66 wrote:
  | The downside of a lot of those "password" save systems was that
  | you lost your lives, and sometimes more. The password sometimes
  | just wasn't long enough to store enough information. For
  | example, Driver[0] (at least on the Game Boy) is an icon-based
  | one with eight(?) symbols per field and four fields. That's
  | only 12 bits.
  | 
  | [0]: https://en.wikipedia.org/wiki/Driver_(video_game)
 
  | hot_gril wrote:
  | And you can use your save on another system.
 
  | [deleted]
 
| amadeuspagel wrote:
| How much of a concern is the length of the URL? It doesn't matter
| for bookmarks, but what about sharing? Character limits, or even
| just the unwieldiness of long URLs in a textarea?
 
| EGreg wrote:
| On a related note ...
| 
| If you don't want to store anything on the server, why serve the
| static JS and HTML? You can embed them in the URL also.
| 
| In that cass the URL just becomes a local file.
| 
| You should store the file locally, along with all the data it
| embeds.
| 
| Or you can store the files on a CDN at the edge. The problem with
| the latter is that people can abuse the storage.
| 
| Really, the Web needs to be overhauled to have a topology more
| like a DHT, where you only have peering agreements with a few
| peers, and they cache the content temporarily when it is
| requested a lot. So no centralized servers at all.
 
| redleader55 wrote:
| A better idea is to use the URI fragment (the string after the #
| sign). It has the advantage of not having a size limit, and not
| needing to be sent all to the backend during the request.
| `window.location.hash` is your friend.
 
  | escot wrote:
  | This is a great point, but also depends on what browsers you
  | want to support. In my experience some browsers a) will
  | truncate the url when you try to copy it from the browser bar
  | (safari), and b) will not support any url including the hash
  | past a certain cut off (like the SO comment states).
 
    | escot wrote:
    | I just updated knotend to use the hashmark, see my comment
    | above!
 
  | rcme wrote:
  | Sometimes you might want the state on the backend, e.g. to
  | generate open-graph metadata tags.
 
    | culi wrote:
    | well you don't have to store _ALL_ of your data this way.
    | Also you can always just choose to send it to the backend
    | still with a POST
 
  | escot wrote:
  | I just updated knotend so that it now uses the hashmark. I
  | think this is a great idea, and it just loses the ability to
  | get the urls server side, which in my case is totally fine. I
  | also updated the blog post and acknowledged your comment, so
  | thank you!
 
  | Zamicol wrote:
  | I wrote a library based on that very idea. It supports fragment
  | query, just like normal queries.
  | 
  | https://github.com/Cyphrme/URLFormJS
  | 
  | I use it in a lot of places, like:
  | https://convert.zamicol.com/#?inAlph=Text&in=Hello%20world!&...
  | 
  | https://cyphr.me/ed25519_applet/ed.html#?msg=Hello%20World!&...
  | 
  | https://cyphr.me/coze#?verify&input={%22pay%22:{%22root%22:%...
 
  | thedougd wrote:
  | https://app.diagrams.net/ has an excellent example of this.
  | File > Export As > URL...
 
  | kishinmanglani wrote:
  | That's really interesting. Do you have any idea how much data
  | (i.e. max amount of json) you can store using something like
  | that?
 
    | bckygldstn wrote:
    | It seems to vary greatly by browser. Based on this SO answer
    | [1] MS edge only supported 2000 characters in 2017, while
    | Chrome currently handles 40 million characters (of
    | compressed, encoded json).
    | 
    | [1] https://stackoverflow.com/a/44532746
 
      | G3rn0ti wrote:
      | Weirdly, with IEs it was exactly 2083 characters (1) not
      | some base 2 number and MS never increased this number over
      | all these years. This upper limit even included fragments.
      | We tried to do something similar as described in the
      | article and were surprised to learn about IEs limitation.
      | In the end, we stored states on a JS object instead using
      | their hash sum as keys and put that inside the fragment.
      | Then fragment based navigation worked like charm across
      | browsers.
      | 
      | (1) https://support.microsoft.com/en-us/topic/maximum-url-
      | length...
 
        | refulgentis wrote:
        | I don't quite understand and it's on me, trust me :)
        | 
        | My reading is you were worried about length of encoding
        | one state, so you moved to encapsulating states in a
        | dictionary with keys of hash of State and objects of
        | State
        | 
        | And this led to a decrease in size of the URL?
        | 
        | My guess at my misunderstanding: you kept the state
        | dictionary server-side or at least some other storage
        | method than the URL, and just used the URL to identify
        | which state to use from the dictionary. I e. The bit you
        | add to the URL is just the hash of the state, or
        | dictionary key
 
        | G3rn0ti wrote:
        | Yes, in the final solution we just stored a hash sum
        | inside the URL fragment (I think it was an MD5 sum) and
        | the actual state inside a JS object in main memory. With
        | a page reload you lost all states which was fine for us
        | but you could use session storage to avoid that.
 
  | opportune wrote:
  | Be careful with this. This is only supposed to be used on the
  | client side. Many HTTP server implementations (and
  | infrastructure you run on) will not let you use the URI
  | fragment even if it does hit the wire, or things like caching
  | will break - you almost certainly want use query parameters for
  | anything backend related
 
    | madeofpalk wrote:
    | URI fragments being client side are entirely their point.
    | _Clients_ should never send them to the server.
 
  | TheRealPomax wrote:
  | If you have so many parameters that you need a URL that's over
  | 2048 characters, you should probably be reminded that
  | localStorage exists.
 
    | kelnos wrote:
    | Using local storage means that you can't share a flowchart
    | with someone else (or embed one in another page) at all, let
    | alone easily.
 
      | TheRealPomax wrote:
      | If you need to show a flowchart that doesn't fit in a URL,
      | the idea that you _should_ be able to do this with a URL is
      | kinda bonkers. The solution there would be www.example.com
      | /?data=url_for_the_flowchart_definition, because what
      | you're talking about isn't page state, it's a secondary
      | resource.
 
  | dheera wrote:
  | > It has the advantage of not having a size limit
  | 
  | Still though I suppose you could probably gzip it before base64
  | encoding it for some additional optimization.
  | 
  | Extremely long URLs have other UX issues, e.g. sending them on
  | chat apps and having them eat up multiple scroll pages in one
  | URL, like:
  | 
  | "Check out this event:
  | 
  | http://..... ... ... (500 scroll-screen-lengths of just URL)
  | ... ...
  | 
  | Here's another event on the same day:
  | 
  | http://..... ... ... (500 scroll-screen-lengths of just URL)
  | ... ...
  | 
  | Can you let me know what you think and which one you'd like to
  | go to?"
 
    | kelnos wrote:
    | The article mentions that compression is already being used.
 
    | jefftk wrote:
    | I don't most chat apps truncate long URLs?
 
  | imhoguy wrote:
  | Mega.co.nz uses that to hide the decryption key for almost
  | decade. You share links but Mega never knows your key because
  | fragment hash is never sent to server or in referrer. Encrypted
  | files are stored on Mega servers but the decryption occurs in
  | JS on the client side, files are decrypted into temporary JS
  | Blob objects in browser then you save them aka "download"
  | locally... a lot of clever tech there.
  | https://help.mega.io/security/data-protection/end-to-end-enc...
 
  | Izkata wrote:
  | Everything old is new again!
  | 
  | A decade-plus ago, this was common in single-page apps. It was
  | the only way to do it before the history API existed, and
  | enough people were doing it that Google was pushing a fragment-
  | url thing [0] that allowed server-side rendering. You'd make
  | such links in a certain way, then the crawler would hope a
  | simple modification of the URL could be rendered by the server
  | and load that.
  | 
  | (Per a comment on that source, it's not been a thing since
  | 2015, since the google crawler can now handle javascript)
  | 
  | [0] https://stackoverflow.com/questions/6001433/does-google-
  | igno...
 
| dang wrote:
| Related ongoing thread:
| 
|  _Show HN: URL Snake_ -
| https://news.ycombinator.com/item?id=34288577 - Jan 2023 (30
| comments)
 
| dools wrote:
| I've been using this hosted on my own server since it was first
| posted in 2013:
| 
| https://news.ycombinator.com/item?id=5696127
 
| franciscop wrote:
| In React you normally create local ephemeral state like this:
| const [myState, setMyState] = useState(null);
| 
| I've created at least 3 libraries that follow that same pattern,
| but instead of ephemeral the state is saved somewhere:
| // ?firstname=         const [firstName, setFirstName] =
| useQuery('firstname');              // localStorage         const
| [name, setName] = useStorage('name');              // global
| state (that can be connected to other places)         const
| [user, setUser] = useStore('user');
 
| escot wrote:
| Hey all, I made this post to show a technique that I'm using in
| my keyboard-centric flowchart editor [0]. I really love urls, and
| the power that they hold, and would love to see more apps
| implement little hacks like this.
| 
| Also, another shout out to mermaidjs and flowchart.fun for also
| implementing similar url-based sharing.
| 
| [0] https://www.knotend.com
 
  | tonerow wrote:
  | Love the app!
 
    | escot wrote:
    | Thanks!
 
  | tlarkworthy wrote:
  | There is a size limit though that quickly gets exhausted if you
  | are storing text (2000 chars)
 
    | tonerow wrote:
    | (maker of flowchart.fun here) This is technically true but
    | it's a little more nuanced. The 2048 character limit comes
    | from Internet Explorer. There's a great answer on SO
    | https://stackoverflow.com/a/417184/903980
    | 
    | Still on FF I opted to use LZ Compression (pretty sure many
    | other sites do this as well) to get the number of characters
    | down
 
      | tlarkworthy wrote:
      | I have done this myself and various tools e.g. slack,
      | Twitter will truncate long urls. Eventually it will break
      | somewhere, not just in the browser. If your tool has a hard
      | limit on information content it's ok, but that does not
      | seem to be the case here.
 
    | escot wrote:
    | You're right, its a limitation. Knotend also supports
    | upload/download of a .knotend file to get around this.
 
    | __MatrixMan__ wrote:
    | Store the content in ipfs and just put the hash in the URL?
    | 
    | I haven't used https://github.com/ipfs/js-ipfs in this
    | capacity but I'm under the impression that that's moving bits
    | around like that is more or less its purpose.
    | 
    | Although I suppose this puts a burden on the URL-creator to
    | pin the content until the URL-clicker doesn't need it
    | anymore, which is not how URLs are supposed to work.
 
    | rmetzler wrote:
    | This is the right answer here. The solution works until it
    | doesn't. Browser URLs are limited in real life. So when the
    | state grows it will break sooner or later. Of course it
    | depends on the usecase.
 
| michaelteter wrote:
| As long as your users are reasonably tech-saavy this is ok. But
| long URLs can be problematic when sent in email for many possible
| reasons, such as the email client wrapping the URL and then
| creating a link out of just part of the original URL. The user
| sometimes doesn't realize this and gets unpredictable results
| when trying to use the URL they "saved" in the email.
| 
| Perhaps instead, assuming the user is online, hash the json state
| data, then send the json+hash to the server and update the URL to
| URL?hash. Then the user has a much shorter URL, and the server
| can lookup the json state from the provided hash to resume the
| app at that state. As a bonus you then have versions of the data
| over time (either posted automatically in the background or on-
| demand when the user clicks Save).
 
| [deleted]
 
| zelphirkalt wrote:
| Racket does this in its web framework and docs.
 
| AndrewStephens wrote:
| This is pretty common and has a bunch of advantages, like the
| fact you can link to and bookmark a particular state.
| 
| Also, if you are careful you get undo and redo for free with the
| browser's back button doing all the work for you.
| 
| The disadvantages are that your representation of internal state
| becomes part of the interface - if you ever change your app you
| need to deal with versioning the state so your new version can
| transparently handle the old state format.
| 
| If your app has a server component that acts on this state, be
| super careful about acting on it and treat it as you would any
| other input under user control.
| 
| If you app is completely client side, consider storing the state
| in the #fragment section of the URL. This never gets sent to the
| server. An example from my own site [0] - see how the fragment
| part of the URL changes as you select different topics.
| 
| There are also limits on just how much you can cram into a URL
| but with care you can shove a lot of state.
| 
| [0] https://sheep.horse/tagcloud.html#computing
 
  | typingerror wrote:
  | > _If your app has a server component that acts on this state,
  | be super careful about acting on it and treat it as you would
  | any other input under user control._
  | 
  | I would recommend signing it if it's generated by the server
  | component, and checking the signature when the server component
  | is provided this signed state.
  | 
  | For example to do this in Node is quite straightforward.
  | 
  | Key generation:                   const crypto =
  | require('crypto');
  | crypto.generateKeyPair('ed25519', (e, pubkey, privkey) => {
  | // save pubkey and privkey somewhere             // ...
  | }
  | 
  | Signing:                   const data =
  | Buffer.from(JSON.stringify(state));                  const
  | signature = crypto.sign(null, data, privkey);
  | const signeddata = `${data.toString('base64')}.${signature.toSt
  | ring('base64')}`.replace(/=/g,'');
  | 
  | Verification:                   const parts =
  | signeddata.split('.');                  const data =
  | Buffer.from(parts[0], 'base64');         const signature =
  | Buffer.from(parts[1], 'base64');                  if
  | (crypto.verify(null, data, pubkey, signature)) {             //
  | signature not verified, throw or return             // ...
  | }                  const state = JSON.parse(data);
  | 
  | As the above uses Ed25519 the signatures are quite small too.
  | It needs a bit more error checking, and might need extras like
  | expiry time and such, but should be roughly sufficient.
 
    | AndrewStephens wrote:
    | Good advice. For some apps you might also need to protect
    | against replay attacks to prevent the user from reverting to
    | a previous state in a way that you app should not allow
    | (undo'ing a change of bank balance, etc).
    | 
    | But if your state is so important it is probably better to
    | not uses these techniques at all and just store the state on
    | the server.
 
      | awb wrote:
      | This technique is client-side data only, so updating a
      | client-side state would only _appear_ to revert your bank
      | balance on the UI, but wouldn't trigger any server-side
      | functions to do so.
      | 
      | If you sent the client state to the server for some type of
      | CRUD action or persistence, you'd need to sanitize and
      | validate it first. And at that point like you said, why not
      | just keep the state secure server-side and not trust the
      | client.
 
  | hot_gril wrote:
  | > The disadvantages are that your representation of internal
  | state becomes part of the interface
  | 
  | This is the biggest reason to avoid this. URLs aren't meant to
  | be used this way. I'd only do it if I wanted a quick and dirty
  | solution.
 
    | AndrewStephens wrote:
    | > URLs aren't meant to be used this way
    | 
    | I disagree, URLs are supposed to point to a resource - in
    | this case the resource is the client state of the app (aka
    | "deep linking"). It is a useful approach.
    | 
    | Of course, it can easily be misused.
 
      | hot_gril wrote:
      | The suggestion was to put your entire client state into it,
      | which is probably more than just the resource location (aka
      | deep link).
 
  | amadeuspagel wrote:
  | > This is pretty common and has a bunch of advantages, like the
  | fact you can link to and bookmark a particular state.
  | 
  | Conversely, you can't link to the newest version.
 
| m00dy wrote:
| Another one would be passing ipfs hash in the url
 
| kevinfiol wrote:
| My favorite example of this is flems: https://flems.io/
 
| breck wrote:
| This is the way. Except I would never encode the data--just store
| state in plain text. That way you can edit urls by hand in a
| pinch (comes up a lot, actually). And more trustworthy and you
| can be sure you take your data with you.
| 
| I do this in all my apps. https://ohayo.computer/,
| https://simoji.pub/, https://try.scroll.pub/,
| https://jtree.treenotation.org/designer/ etc.
| 
| At one point I also made a tiny lib to make it easier:
| 
| https://breckyunits.com/patch/
 
| shepherdjerred wrote:
| Shinylive, which lets you create and share data science apps,
| takes this approach:
| https://shiny.rstudio.com/py/docs/shinylive.html
 
| oaiey wrote:
| Just do not. That or alternatively a hidden field is how asp.net
| WebForms did it state management 20 years ago. Worst possible
| idea ever.
| 
| Good for small scale unimportant external state management but
| not for your everyday web app.
 
  | jonathanoliver wrote:
  | When I saw this idea all I could think of was the ASP.NET
  | "postback" form data and URL data! I'm glad to not have to deal
  | with that anymore.
 
  | tomwheeler wrote:
  | It also degraded performance. I had to use a .NET-based issue
  | tracking web app 20 years ago. The performance was horrible,
  | with pages sometimes taking 20 seconds to load. I eventually
  | viewed the source of the page and discovered a form field
  | called "viewstate" that contained several megabytes of encoded
  | data.
 
| sidharthv wrote:
| I had to add compression into Mermaid live editor's URL state as
| the length limit was exceeded with big diagrams.
| 
| Wrote this SerDe which can be extended with better algorithms in
| the future. https://github.com/mermaid-js/mermaid-live-
| editor/blob/devel...
| 
| Eg:
| https://mermaid.live/edit#pako:eNo9kUGTnCAQhf8K1adNlesoqKiHV...
 
| debacle wrote:
| Congratulations, you've implemented ViewState from aspx.
 
| tomtomistaken wrote:
| We are testing this on a map[0], it's fun. When tweeting the
| link, the tweet and its state is added to the map. Also, map
| stories are possible through Twitter threads. We just implemented
| a draw function and are working on an implementation for external
| geojsons.
| 
| [0] https://map.decarbnow.space
 
| generalpf wrote:
| I remember when WebSphere Portal Server used to do this, and the
| URL would exceed 4096 characters, which was the limit that IE 6
| (?) would support, breaking the entire site.
 
  | markandrewj wrote:
  | There is still a browser URL limit, I have hit it recently
  | while applying a large number of filters to a search in Kibana.
 
| jcuenod wrote:
| This is a useful idea when you've got complex nested state. The
| problem is changing any particular part of the state is non-
| trivial. I ended up making `friendly-serializer`[0] to "make
| objects more accessible in urls."
| 
| The idea is that instead of a base64 string, you get something
| that is much easier to edit in the url bar:
| 
| > name=John%20Doe&age=42&address.street=123%20Main%20Street&addre
| ss.city=Anytown&address.state=CA&address.phoneNumbers.0=123-456-7
| 890&address.phoneNumbers.1=234-567-8901
| 
| There's a similar npm project to [0] that I discovered after I'd
| published this, but I don't recall the name. PRs welcome.
| 
| [0] https://www.npmjs.com/package/friendly-serializer
 
| berelig wrote:
| This is exactly how Shel Kaphan (first eng @ Amazon) handled the
| state of a shopping cart in the mid 90s. Here is him discussing
| some of the pitfalls: https://lists.w3.org/Archives/Public/www-
| talk/1995NovDec/020...
 
| 12311231231 wrote:
| [dead]
 
| withinrafael wrote:
| I don't have an opinion on this but do want to mention that
| LastPass does not (currently) encrypt the URL portion of its
| entries in the secure vault. Something to keep in mind!
 
| locofocos wrote:
| Neat. I did this for a very basic react ratio calculator I built.
| My state was so basic and small, it felt fine for a toy project.
| Certainly a practice I would scrutinize in a production
| application, but it could be useful.
| 
| https://locofocos.com/anything-ratio-calc
 
| brundolf wrote:
| Something to be careful about here is that browsers/proxies/etc
| have arbitrary length limits on URLs, and they can vary by quite
| a lot
| 
| We did this once with a web-based REPL, where you could click a
| button to copy a link to something you'd previously eval'd and
| then send that to someone else and their buffer would be
| preloaded with it. It was super nifty, until someone tried it
| with a buffer that was a few hundred lines long and at least one
| browser just said "nope". It sounds like OP is doing some smart
| stuff to minimize URL length, which should help, but there's
| still a limit somewhere
 
| actually_a_dog wrote:
| Only about 20 years late:
| https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arc...
 
| 867-5309 wrote:
| I'm just getting a 414 Error
 
| 2342346666 wrote:
| [dead]
 
| Zamicol wrote:
| We wrote a library for storing application state in the URL. Its
| novel feature is using fragment query, which prevents the
| information from being sent to the server.
| 
| https://github.com/Cyphrme/URLFormJS
| 
| I would love to see it get more use.
| 
| Here's a small demo:
| https://cyphrme.github.io/URLFormJS/#?first_name=Bob&last_na...
| 
| See my other comment on this page for some other examples of its
| use.
 
  | lelandfe wrote:
  | Does this necessarily prevent the ability to provide in-page
  | anchors?
 
    | vbezhenar wrote:
    | Since you're using JavaScript, you can implement anchors in
    | the way you want.
 
    | Zamicol wrote:
    | The fragment query delimiter is `?` with `&` for between
    | values, just like normal queries. It practice, browsers are
    | not fragment delimiter aware so at the moment I would treat
    | them as mutually exclusive.
    | 
    | There's a larger issue of URL fragment delimiters. For a
    | practical work around on Chrome only, fragment directive/text
    | fragment can be used as a delimiter. (In my opinion, Chrome's
    | delimiter should be re-worked with a new, more comprehensive
    | delimiter.) Example of the workaround: https://en.wikipedia.o
    | rg/wiki/URI_fragment#References:~:text...
 
  | notRobot wrote:
  | This looks very cool, and I'll definitely use it in a side
  | project sometime soon!
 
| aplummer wrote:
| I do like the approach and when it works great thats fantastic -
| please be careful of this as an attack vector.
| 
| I know of at least one bank where this was exploited, because
| people see the base url, assume safe, and can't read the embedded
| escape / javascript in the longer part. Especially when the link
| is zipped into a little clickable thing and you can't see the URL
| at all.
 
| jsight wrote:
| This reminds me of ASP.Net's viewstate feature, tbh.
 
| mrdoob2 wrote:
| Made this thing in 2010. I can't remember if I saw the trick
| being used in another website before or not...
| https://mrdoob.com/projects/voxels/#A/bnciaTraheakhacTSiahfa...
 
| gautamdivgi wrote:
| Web servers and http enabled services can limit the length of
| headers and url. While this can be done it runs the risk of
| having surprises when unknown limits are encountered. Most of
| these limits are buried deep in docs, not easily visible.
 
  | cdot2 wrote:
  | I've run into some obscure email servers which reject any mail
  | which includes URLs above a certain length limit and only throw
  | vague "Mail rejected" errors.
 
| stevebmark wrote:
| The Typescript Playground (https://www.typescriptlang.org/play)
| does something similar, it gzips the contents of your code and
| puts it in the URL. Pretty sweet!
 
| builwithlogic wrote:
| This is fine for f its public information and if thats fine then
| why not just add a param with a url encoded json string. Would
| this make the url shorter or longer? I think shorter. So why
| base64encode?
| 
| Just wondering never created an entire app around this idea
 
| jncraton wrote:
| I did something similar in a simple whiteboarding app that I made
| a while back:
| 
| https://github.com/jncraton/box-line-text
| 
| The data representation could probably improve in terms of
| readability, but I'm happy with the way it provides a concise
| encoding while still including the text from the document
| directly:
| 
| https://box-line-text.netlify.app/#;c2121Hello;5321;c1341Hac...!
 
| jerryzh wrote:
| one problem to me is that I run a script remove parameter in
| url(for tracker) ofc I can set a whitelist but it is a issue i
| consider
 
| jonnycomputer wrote:
| Why not use localStorage or sessionStorage instead of the URL?
 
  | temporallobe wrote:
  | I prefer this as well, but it's not as portable.
 
  | escot wrote:
  | URLs are great because you can share them. I may also add
  | support for local storage, but it won't remove the ability to
  | share via URL.
 
| martini333 wrote:
| Vuejs Playground does this. This is the url for the default
| tamplate:
| 
| https://sfc.vuejs.org/#eNo9j71uwzAMhF+F5eIWqCV0NZQA3foGXbikj...
 
| [deleted]
 
  | [deleted]
 
| strongpigeon wrote:
| Hah, I came up with the same thing for
| https://fivethreeone.app/calculator .
| 
| The reason I did the compression (using pako as well) was that
| without it the URL was too long to show a preview on iMessage. I
| also compress the state manually myself which saved a bunch of
| bytes too.
 
  | [deleted]
 
| paxys wrote:
| Base 64 is probably not the best encoding for this, since the URL
| allows for a wider range of characters. You can be more efficient
| by using them as well.
 
  | macspoofing wrote:
  | Base 64 is fine - he just needs to url-encode the parameter.
 
| manchmalscott wrote:
| I am pretty certain BeepBox (An online sequencer/tracker) does
| this as well. It's pretty neat to send something in progress to a
| friend with just a URL, no accounts needed.
 
___________________________________________________________________
(page generated 2023-01-09 23:00 UTC)