[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) (HTM) web link (www.scottantipa.com) (TXT) 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?<state>) and instead using a hash | string (path/to#<state>), 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)