parsePath(…).query is a URLSearchParams, so those parameters are just strings, and it’s an untagged template string, so it is indeed a trivial SQL injection vector. And the params argument was right there! I don’t understand quite how you make a mistake like that, but it’s extremely worrying. This is fundamental, basic stuff.
The fun aspects of this are that:
(a) I could actually imagine trailbase.js contents that would make it not SQL injection: you could have parsePath(…).query.get(…) return objects with a toString() that escaped SQL. This would raise even more questions, and I was sure it wouldn’t be the case, but it’s possible.
(b) You could make it work safely, converting interpolation into parameters, by using a tagged template string. This could require only a tiny change:
return await query(
sql`SELECT Owner, Aroma, Flavor, Acidity, Sweetness
FROM coffee
ORDER BY vec_distance_L2(
embedding, '[${+aroma}, ${+flavor}, ${+acid}, ${+sweet}]')
LIMIT 100`
);
(You could even make it query`…`, but I think query(sql`…`) is probably wiser. As for the plusses I put in there, that’s to convert from strings to numbers.)
So we’re clear, the tagged template string approach gives you a string that uses parameters properly (see the example in the README of that project that does it); it’s perfectly compatible with prepared-statement caching.
And actually on second consideration, you probably can’t use binding parameters here: you’re trying to slot numbers in as JSON values inside a string! Maybe you’d have to write vec_distance_L2(embedding, ?), with parameter JSON.stringify([+aroma, +flavor, +acid, +sweet]).
:hide: thanks for pointing out this embarrassing plunder. No excuse, just feeling silly especially after being so careful throughout the rust base. It's fixed now. Please keep yelling if you find more issues
That type stinks. Kill the undefined bit by giving it an initial value, then you can skip the `?? []` later too; and replace Array<object> with the actual row type, probably naming it, maybe like this:
type Record = [string, number, number, number, number];
const [data, setData] = useState<Record[]>([]);
const params = new URLSearchParams({ aroma, flavor, acidity, sweetness });
I confess I’m puzzled about this one too, as trailbase.js suggests you know about URLSearchParams, which is what you should use for all typical query string manipulation, parsing and generation.
no, but the guy can also look at postgres.js that already implmented alot of the sql`SELECT * FROM ${table_name}` Where there vars are escaped. It ONLY works for postgres, but I'm sure you can make it work with SQLite as well.
Mucking about with SQL strings, to me, is akin to writing your own crypto: Don't. Unless you must (because, say, it doesn't exist yet.) Doing this is asking for security problems later. Trust that the SQLite team (or any other SQL engine) have more experience and have provided a correct interface. And if that interface isn't correct, don't just run off an make your own- contribute a fix so we can keep all the lessons learned together.
I found the Trailbase documentation did a very poor job of describing what this is. It solely describes Trailbase in terms of differences from Pocketbase and Supabase (and that's even the first entry in the FAQ), which assumes the reader knows what Pocketbase and Supabase are. In contrast, the Pocketbase documentation got to the point very quickly.
I was aware of Supabase, but still was confused as to what this project actually does. The mention of "1 file" and "app server" wasn’t helpful either. Does that mean a single binary? One SQLite file? Does it execute other binaries? I’m not sure. In contrast, when I visited the PocketBase website - it provided a much clearer explanation of its purpose.
"Application server" is an umbrella term and the documentation doesn't really help identifying what the perimeter is.
Beside, it states that it is so fast that there's no need for cache, and supports SQLite only. So it seems to target only very simple applications, eg. straightforward relational DB <-> Json Rest/HTTP.
> So it seems to target only very simple applications, eg. straightforward relational DB <-> Json Rest/HTTP.
Could you expand a bit on what more advanced capabilities you're missing for anything beyond a very simple app? I'm genuinely curious but also see SQLite often undersold
Processing beyond simple DB data retrieval or io dependent processes can be time consuming and fully justify a cache. Eg, geographic processing that goes beyond geoDB capabilities with millions of rows, eventually involving retrieval of data from third party providers can take long so you'll want to cache results one way or another.
No denying SQLite is very capable. However in my understanding it's designed for being used by a single application, it's not really scalable, and its functionalities are still limited compared to the big DBs. Anyway there's no single fit for all cases, and for core components flexibility is really important.
> retrieval of data from third party providers can take long so you'll want to cache results one way or another.
Agreed. Caching of slow external sources will always be beneficial (at least if you need it more than once :) ). You should use whatever makes the most sense. The argument is more that data from TB is already pretty quick, quick enough that you could even use it as a cache.
> However in my understanding it's designed for being used by a single application,
SQLite by design allows for concurrent writes and parallel reads from any number of processes.
> it's not really scalable,
What do you want to scale to? Postgres is fairly similar in terms of scalability, i.e. master writes + read replicas. Once you're going beyond that scale you get a lot more benefit out of more specialized, problem-oriented solutions.
> and its functionalities are still limited compared to the big DBs.
It full SQL and it's extensible. Depends on what you need. It certainly doesn't have the same rich ecosystem or flexibility of posgres where you can even swap out the storage engine. I was mostly curious, if there's some specific functionality you're missing.
> and for core components flexibility is really important.
Agreed: the right kind of flexibility for your problem.
I'll play devils advocate here: imagine you'd start to deeply depend on one specific postgres plugin and then that plugin get discontinued or you're hitting performance bottlenecks and you're to locked-in to move to a more specialized solution. Don't take this too seriously, I just wanna say that there's a balance.
Looks cool, but in what world is any of that 'single-file'? Like yes your examples are built out of single files, several of them in fact. And the sever certainly isn't a single file...
You folks are spot on. Single file in this case means single binary that is statically liked, i.e. w/o dependencies on shared library allowing you to just copy it from machine to machine similar to e.g. k3s.
Whether to use docker or not is really up to you. It's just a way to deploy other assets, e.g. a static html bundle. More importantly it saves me the trouble of providing windows builds at this current time :)
I'm missing something here. I'm familiar with TomCat, but that's an application server for JVM programs. Here the project specifies which technologies are used to develop it, but which kind of applications will it serve? Are the applications supported by this server using a specific runtime? Will it serve native applications? Sorry for the naive questions, I'm not into web development.
It's a continuation of a trend in which open source projects mimick the functionality of a tool called Firebase that Google bought in 2014 and is now part of Google Cloud (see also supabase and pocketbase). All these projects seem to be sort of like Redis in as much as they stick a datastore behind an API saving users from needing to use SQL and offering them a higher level API instead. And it also seems to do some sort of auth things.
I think it's meant for people who want to build a front end without building a backend.
> stick a datastore behind an API saving users from needing to use SQL and offering them a higher level API instead
supabase team here. Yes, we offer an auto-generated API (using PostgREST). But users can also connect directly to their Postgres database or through a connection pooler. People have preferences, so we offer options.
It's kinda like only the backend part of a cms system. Say if you took the backend part of Wordpress/Joomla/Drupal and exposed apis for a user developed frontend.
They do. My point is that for the vanishingly small number of use cases where you need to write application logic in PocketBase, and you need to write it in JavaScript, and you need to use unsupported super modern ES6 features, you might prefer Trailbase, but otherwise PocketBase is more polished at this point.
IMHO, this will greatly depend on your use-cases. If you have a rich client app, e.g. mobile or PWA, most of your logic will go into your client by default.
However, there might be some code you want to run on the server in a trusted environment, or use some existing JS library for mobile, or have an otherwise mostly static web app, ... depends
This looks quite nice overall and the demo is slick.
It looks like backend APIs need to be written in JS and and then deployed as separate files. Not quite what I want, but I can appreciate why they went that route.
It'd be neat to have a project like this as a Rust crate, where you can write your own APIs in Rust and compile the whole thing to a single file.
Always interesting to me these type of solutions choose SQLite (which is great) and not something like Postgres with Orioledb. With SQLite you have to also use Litestream for S3 backups and this is always something I'm surprised isn't found in these types of solutions.
I think the audience for these projects are frontend developers who don't want/need to write much of a backend. They need some kind of data store + a dumb CRUD API to use with their mobile app or javascript SPA.
But aren't these folks already served well by PocketBase ?
And by the time they get a million user, they will probably not be hosting on a single "tiny box" anyway. At what point do you think : okay, I need PocketBase, but ten times faster ?
I don't question people's projects. Creator obviously thinks there was a need and that they could do better than PocketBase. It's the same as asking why BSD when we have Linux. Why Wayland when we have Xorg. Who knows, maybe this will succeed PocketBase. Since when has competition been a bad thing?
If you want a reactive backend then this is similar to Convex’s open source backend, which also uses TS for writing backend functions and SQLite for persistence: https://github.com/get-convex/convex-backend
You get a consistent client for React, simple file storage, built-in text search…
Sorry, this is folks logging in and changing the password. Should have seen this coming :hide:. Instance gets reset every 60min. Not great but something to improve
I know naming a project is difficult so please don’t take offence when I write my first thought on reading the name trailbase and thinking “why would a project that wants to lead with speed name itself for moving slowly?” I’m sure they’re thinking of the other use of trail, not meandering or dragging.
That's very valid and also why it's funny. Naming is hard. To be honest, I didn't think much about "trailing", certainly trailing in terms of adoption :). I did like the sound of "trail run" on the command line.
Definitely considered a bunch of alternatives. "SecondBase" was fun :)
I'm no rustacean but to me it looks like start() in https://github.com/trailbaseio/trailbase/blob/main/trailbase... consumes a file and returns a database connection. If it were to step into the file and seek out an embedded file system I suspect that function would look different.
"single file" terminology in the context of applications does not typically include the DB. It is common for single file executables to use external config files and DB files.
But then how should redbean describe itself? There you have the executable plus any configuration files or other files you might possible need zipped inside one single file (and if you need a DB, also one more file for the sqlite database next to it, OK).
The admin UI is not good, text is overlapping everywhere, and want to see how you show relations in the UI, for me this is the first thing I look for in any DB UI
Sorry, this is folks logging in and changing the password. Should have seen this coming :hide:. Instance gets reset every 60min. Not great but something to improve
> If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License.
> This means that distributing OSL software on ordinary FTP sites, sending patches to ordinary mailing lists, or storing the software in an ordinary version control system, is arguably a violation of the license and would subject you to possible termination of the license. Thus, the Open Software License makes it very difficult to develop software using the ordinary tools of free software development.
That sounds concerning. Maybe OSL-3 is the wrong license. I am not a lawyer and spend too much time to admit on researching options before settling on OSL-3. My impression was that OSL-3 is roughly AGPL with a linkage exception to account for both use-cases of using it as a stand-alone service or as a rust library. Maybe a dual license would have been more appropriate? TBH, I'm also not sure I fully grasp: https://www.gnu.org/licenses/license-list.html#OSL
I mentioned commercial use where you don't modify the software, not sure why you bring that up.
Just put a link to the git repo somewhere in your service, like an about page or whatever, then people can go read the license or make their own copies and so on.
Is the string interpolation straight into the sql from the query string in your getting started docs (https://trailbase.io/getting-started/first-ui-app) safe? It smells a bit.
From a quick look (on mobile) I can see that the function takes parameters, but you're not using that.