With prepared statements it's like saying to the DB, here's the query: SELECT * FROM users WHERE email = ? AND password = ?. And here's the data: ["whatever", "whatever"]. You can literally send whatever your heart desires for those two strings, unescaped, to the DB server and let them deal with it.
From within a language, you somehow need to restrict how you build up the query. E.g. a query builder or ORM usually makes it hard to use anything besides parameters, but they usually have escape hatches (e.g. a where method that takes arbitrary sql strings).
One trick you can do in Go is to create a type like `type sqlLiteral string`. The type is private, so other packages can't directly instantiate it, but a string literal will automatically coerce to it. Basically, you can't compute sqlLiteral from arbitrary expressions. It must be a single hardcoded string literal.
This is solely a mysqljs bug.
A library in a strongly typed language would still need to use parameterised queries or escape the strings just like a JS library. And it would be just as easy or difficult to have an incorrect sanitising function.
In fact, the specified string is not invalid input, it would be perfectly valid to store backticks in a string field in MySQL. They just need to be correctly escaped before being submitted to the database.
Again, the only bug here is the completely incorrect serialization that mysqljs does.
Sanitization is an ignorantly dangerous practice, because it obscures much safer practices.
Validation? That's for business rules. No more order items than there are things in stock, and stuff like that. If you need to do that because something in your stack needs it, then it's far past the time to reconsider your stack.
Which is exactly the equivalent of using JSON.parse(). You will find more library combinations, and for many of them the options can't be considered bugs like you do for mysqljs here, and you definitely won't be able to catch it.
The solution is parsing beyond JSON.parse(). You can't have arbitrary user provided structures floating around your program.
First off, most other DB drivers will use real prepared statements, so what is passed down to the DB is actually the templated query and a set of values. It looks like mysqljs actually parses and interpolates the string before sending it to the DB engine.
Perhaps more importantly, though, this bug is basically the exact type of bug that caused the log4shell fiasco: trying to be too "smart" with user provided values that should just be treated as "dumb" scalars. The fundamental way of filling out template parameters with something that can refer to DB column names is a major flaw IMO.