Hacker News new | past | comments | ask | show | jobs | submit login
Build a hit counter for Gatsby with React, AWS Lambda, and FaunaDB (joshwcomeau.com)
45 points by joshwcomeau 12 months ago | hide | past | favorite | 48 comments

I may be missing something here, but... all that for a hit counter? Even using React for the front-end seems overkill for a little widget.

The hit counter is merely an example! I see this blog post as an intro to how-to-connect React up to a hosted database over this fancy serverless paradigm thingy.

And more importantly this is what a lot of people are doing in industry, for better or worse, and so seeing this all laid out together is important IMO.

these days the hard part isn't the view counter logic, it's managing the 395,000 dependencies in your project

I used to be “dependency-first” for library code, but I’ve noticed lately that my first question has been “why wouldn’t it be a good idea to build in house?”

Hard same. After having a few experiences where a dependency hardly does anything, my approach is to read the docs first and see if I can implement the parts I need in a couple of hours. Often I can.

Two reasons not to build in house:

1. Edge cases. You cannot possibly cover every possible edge case. This is why back in the day we had "Site works best on IE". Libraries are often used by a lot of people thus edge cases you couldn't possibly think about are covered.

2. Someone else maintains it. If your job is to build an application, why are you spending all your time maintaining a library that just facilitates your application? If I'm building a racecar, I'm not gonna make the wheels in house, let people who are better at that do that while I focus on what I do best.

One reason to not not build-in house:

1. Edge cases. Chances are you do not need to cover every edge case, at least when building an MVP. It can take more time to learn a complex API than to build in house. See also premature optimization.

2. Someone else maintains it. In the event you decide to use someone's dependency and they decide to unpublish it with the click of a button.... (ok at least this one has been fixed, but API versioning can still be a bear).

To be clear I am not advocating switching to React in order to add a hit counter. The article is meant for folks who already have a Gatsby/Next site.

I don't see any locking either, so I imagine it misses some counts.

The right way to do it with FaunaDB: https://news.ycombinator.com/item?id=24618998

I may be missing something here, but... all that for a hit counter?

Up next, the Angular-powered guestbook in just 10k lines of code.

Then stay tuned for the Go-powered e-card sender in just 50k lines of code.

And then, the Typescript web ring... the cloud... using BespokeDB... and service workers... in WebASM!

It reads to me more like a sponsored blog post by the db saas mentioned in the article.

i totally agree. plus the fact that he's using a DAS for storing the data. if there is one thing i refuse to do is not be in charge of my database or using something proprietary.

Hey folks! Author here. Just a quick clarification, this is NOT a sponsored post by Fauna, as some have claimed. As the article states, the choice for database is somewhat arbitrary, you can swap it out for Mongo or whatever else you’d like and not much code would need to change.

This is going to sound crazy but about a few years ago I sent you an email about learning Javascript. I think you were at Unsplash about to go to Khan Academy? Or was it the other way around haha.

I just want to say thank you for your kindness - the tips and advice you gave me definitely contributed to the career path I have today. I know that responding back to some random kid may not seem like a big deal, but I would not have been able to get here without the help and generosity of experts like yourself.

Glad to hear it was helpful :D

I suspect this is intended to be just an example tutorial that isn't intended to be "this is how a hit counter SHOULD BE".

Queue the OMG modern web complaints that are correct that this is a lot for a hit counter, but this also I don't think that's the point here.

If someone were to take this article and think "oh this is how to build a web counter" that's not OMG modern web, that's a different problem.

I can't help but feel like this article is a good summary of what's wrong with the modern internet. It's not a bad article, it just feels like a parody.

'Ok guys, now here's the modern way to make one of the more simple things from yesteryear.

First, lets bust out react....

A few hundred loc later...

Ok guys the database...

Sign up for account

Several more hundred loc later...

'Now to actually write our functions'

Download some dependencies Several hundred loc later...

And...here's your hit counter guys...

This whole article just captures perfectly the essence of all the things we complain about with the modern web.

Overengineered, convoluted solutions to simple problems.

Honestly, it would be nice sometimes if modern solutions remembered sometimes, it's ok to just keep it simple.

I did have a quick look around to see if i could find a simple, 'modern' hit counter.


Personally, I prefer the simplicity of this to the article myself.

This article is meant for folks who already have a static React site, and want to dip their toes into doing some backend stuff. It isn’t really meant to be the simplest way to build a hit counter.

That said, I challenge the assumption that this way is _that_ much more complicated. I’ve built similar things with PHP, a long long time ago, and it was a lot of the same pieces. The only part that feels truly more complex is the re-render necessary in React, since the data comes in async, but this is hugely beneficial since it means the page doesn’t need to be server-rendered / the user isn’t staring at a white page waiting for the RPC to the database.

Edit: realizing that there are ways to do this in PHP that aren’t blocking, like if you use an image tag that resolves to the right image, but honestly that way seems wayyy more complex to me, especially when factoring in accessibility / screen-reader-friendliness.

> ... it means the page doesn’t need to be server-rendered / the user isn’t staring at a white page waiting for the RPC to the database.

React-based frontend didn't invent XHR. In the old days you just do

Edit: Not saying old hit counters are implemented using XHR.

The old way was really just a image or iframe. It would update on the fly since it was just some perl code in a CGI-BIN writing to a text file

I meant the old way to achieve exactly what the React version does.

Really??? Who knew

My point is that my example is complex because some client-side code is necessary. The snippet you shared is nice pseudo-code but a real implementation would be comparable in complexity. Either way you need to learn some stuff, and then it’s a few lines of code.

The code snippet is working code (as long as you substitute the correct endpoint), not pseudo code. Unless you meant I didn't add a trivial error handling callback.

Ah, I thought “load” was a jQuery plug-in. In that case it’s real code, but it’s only handling the data-fetching; I don’t need React to do that either, but I’d need to generate the hit counter itself (for it to be exactly equivalent, it would need to generate the numbers with SVG as well as add aria labels for screen readers).

The only React-specific thing is the useEffect call, which is essentially comparable to window.onLoad.

these examples of the simplicity of the "old days" are certainly leaving out a lot of stuff that would still need to be done to make the hit counter work and as such they are really not comparable.

It is starting to remind me of a guy in 2006 approximately that said all you needed to parse display an rss feed in your webpage was a call to rss.parse(my rss url); ignoring all the other stuff that needed to be done (using third party script with all rss parsing built in, having the correct css from the third party library to make the output html look good, make sure not to have any conflicting classes in your page because this third party script from the old days didn't take that kind of thing into account , having a div in your page that had the right id for the rss.parse method to append its output to etc. etc)

it's jquery[0], so this is only the "old days" if 2006 was the "old days" for you (which obviously for many it was).

Before 2006 you'd probably use XMLHttpRequest directly- but of course that was only widely available in 2004 or 2005. Before that you would have used a hidden frame- or more likely you would use CGI to either replace a directive with the counter, or generate an image with the correct numbers. That's what I would call the "Old days": a dynamically generated gif with bold white-on-black numbers displaying the visit count.

[0]: https://api.jquery.com/load/

> it's jquery[0], so this is only the "old days" if 2006 was the "old days" for you

People don't remember so well. Even backdating it that far is too generous. Just as a reference point, on New Year's Day 2006, jQuery didn't even exist yet. There was a time post-2010 when the scourge of jQuery showing up for trivial use cases was considered a real problem and a huge source of bloat on the web. (Side note: it's more than a little annoying, for this reason, to see people when trying to talk about changes in Web developer trends to lump jQuery and vanilla JS together.) To give another reference point, Firefox's then-new Tab Groups feature demanded the creation of a jQuery-like library for handling the tab canvas. John Resig was a longtime friend of the project and one-time Mozilla Corporation employee, but jQuery itself was out of the question because of bloat/performance. That was for Firefox 4, which would have been 2011, since it was released at the end of Q1 that year...

There's a lot more concentrated change in the 2010―2015 timeframe than is often accounted for when people think or talk about the Web.

I understand where you're coming from, but I'm going to play devil's advocate and think through everything that needs to be considered for a simple, "back in the day" solution that involves apache, php, and a mysql database:

1. Provisioning the server, either via VPS or setting it up on your local machine and exposing port 443.

2. Installing Apache on your operating system

3. Installing a cert, getting it signed by an authority

4. Installing php, enabling it in apache

5. Installing mysql

At this point you'll have to write your php from scratch or use something like wordpress. Assuming we want to keep it as simple as possible and write php from scratch, you'll want to consider file permissions so your database credentials aren't accidentally leaked.

6. Creating the schema and tables on your database

7. Distributing your site's static content through a CDN

And this doesn't even involve automated deployments, which these services give out of the box.

My point is that a "traditional" approach can appear just as overengineered and convoluted if we want to replicate the scalability, stability and security of solid PaaS services like Vercel and Fauna.

I get that a hit counter is very simple, but I assume the point of this article is to provide a simple "hello world" example that uses these PaaS services, which again, provide a lot of benefits over manually configuring infrastructure.

Your point is valid, but only in cases where you don’t already have everything you listed set up.

I think the OP’s comment was coming from a perspective of someone adding a hit counter to an app that already has app servers, etc, already in place.

if that's the case then the OP's comment should take into consideration the perspective of someone who already has most of the things listed in those steps setup.

In which case - as an example - you don't need to set up a serverless account and have several hundred loc for that because you've got that in your app already, and probably only need 1 loc to call what you need.

Now I'm arguing on the side of serverless, sheesh.

The one you've linked to doesn't seem to actually store the count anywhere, which seems to be the hard part.

Agree with a lot of what you wrote, but the example is weird. It's a client side counter with no persistence.

Yeah, i realize that, it was just something quick I found after about a 30 second search. I wasn't trying to complain about the original article itself, it's obviously a production ready example whereas my linked one not so much.

It's just the general idea that, such things are required even for something so simple just seems over the top when you look at something like a hit counter alone.

It's just overall, if we tried to simplify, the simple things, would all the rest of the frameworks and apps end up being so convoluted and overengineered?

Yes, I imagine there's some example that uses Redis or similar with a proper increment and locking somewhere, perhaps in an eventually consistent way that doesn't block the page load.

yeah, but what you link to isn't a real hit counter? It makes something that looks like a hit counter without any of the backend stuff you need for a real hit counter.

As far as how simple it was back in the day https://news.ycombinator.com/item?id=24618580 already said.

I don't think this is at all in the spirit of hit counters of the past; IIRC they were an img tag that would be dynamically rendered serverside. Static markup » dynamic content.

Perhaps a more apples-to-apples comparison would be an img tag that returns an SVG, allowing CSS styling. You can still choose serverless backend, or you can use the tried-and-true apache/nginx with memcache, just like the 90s.

More like CGI with local state file and locking, or a DB back-end. I'm sure some major sites used memory, but at the point you need that you might be using a load balancer, reducing the usefulness of the technique.

Taking a step back, it’s truly insane that it is standard practice to apply this level of complexity to the storage, incrementing, and rendering of a single integer. The number of dependencies involved is surely enormous. Perhaps I am underestimating the complexity of the task at hand, but how is this not like a 2 line flask app or something comparable.

> q.Update(document.ref, { data: { hits: document.data.hits + 1, ...

Does FaunaDB have an "atomic increment" a la Firestore? Knowing nothing about FaunaDB, I suspect this code in the blog post has the potential to "lose" hits that come in at roughly the same time...

FaunaDB appears to have some kind of transaction support[1], so atomic updates of the sort you mention seem feasible. That said, I'm a huge SQL partisan, and I'd much rather talk to a SQL database than this custom thing.


I did find an example of an atomic increment in Faunadb. Sometimes SQL is easier :) :

        data: { 
            clickCount: Add(

Pretty funny that the push towards static serverless has made something you used to get essentially for free on your dynamic client-server app into quite a complex undertaking.

Static compile-time sites are fantastic for a huge swath of applications, but this is a good reminder that you can shift the work around and even come up with better isolated, decoupled approaches but somewhere it still has to get done. Introducing functionality that is dynamic outside the browser and requires state (i.e. persistence) is definitely an area that seems to "fight" the natural inclinations of frameworks like Gatsby et al.

Now compare this with Elixir + a good old database: https://dashbit.co/blog/homemade-analytics-with-ecto-and-eli...

I'd just like to say that this is one of the most beautiful websites I've ever seen!

Wouldn't it be easier to access Google Analytics using API and pull data from there?

Paid article of Fauna.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact