5 ms·
I am making a competitor to this and I am really happy that the approach seems to be gaining traction. Having the database as the source of truth and migrating
by kristiandupont 5y ago
I am making a competitor to this and I am really happy that the approach seems to be gaining traction. Having the database as the source of truth and migrating it with SQL feels very right for me.
It does mean that architectural decisions start in the database as well, which I am trying to govern with "schema linting" (https://github.com/kristiandupont/schemalint https://github.com/kristiandupont/schemalint). If you are using Postgres, I would love help with building this.
- yasserf 5y agoThats very cool! I'm also massively in favour of having the db as the source of truth. When I first started looking into building my own SaaS I actually tried to go down the database first design, where most of your functionality runs within postgres triggers. But it wasn't near as easy to do things and writing SQL logic is much harder to test. Figured the next best thing is to just tie the database in extremely closely using typescript into the backend (I rarely define custom types, always try to use Pick/Omit/Partial combinations all the way to the client SDKs). I ended building generating forms based on the meta data in postgres (select fields know the values from enums and multiselect from enum arrays), so if you define a table in postgres, you just specify which fields you want to render (out of the available columns) and in what order and it pretty much gives you a UI form that is directly rendered in react. Works really well when you have a form component library at your disposal.
- random_kris 5y agoYou have any of this on github?
- yasserf 5y agoThe form building part I don't no, I built it for a client that has a few dozen forms with hundreds of fields / options which is hard to keep up with. So we settled on using a csv file that is essentially a products owner way of saying, i want this field on this entity of this type. For example: entity,field,type,constraints delivery,arrived_at,Date,{ before: 0 } And in the form: form,field_1,field_2,... delivery_details,left_at,arrived_at,... Our CI then checks that the field actually exists in the typescript schema, and if not the build fails on our development environment (and suggests the basic SQL to fix it). The company is small enough for us to be able copy the sql into a migration script and let it run without skipping a beat. It's not a magical pill, renaming, deleting and custom migrations need to be dealt with manually. But for the most part 80%_of our changes are additions and this way it just works.
- yasserf 5y agoI am looking to release the setup I use for deployment but tbh it isn't tested (via tests, since I rely on typescript for most of my data shuttling) and there's alot to it so need to write alot of documentation which i don't have time for. But, the jist is, we APIs like this (this can be improved with less generic json schemas): export const updateSamf: APIFunction<UpdateSamf, void> = async ({ database }, { samfId, ...data }) => { if (Object.keys(data).length === 0) { throw new InvalidParametersError('No fields to update') } if (data.title && data.title.length < 3) { throw new InvalidParametersError('Title needs to be at least 3 characters') } if (data.tags) { data.tags = data.tags.map((t) => t.toLowerCase()) } await database.crudUpdate<Samf>('samf', data, { samfId }, new SamfNotFoundError()) if (data.cover && previousCover) { await content.delete(previousCover) } } export const routes: APIRoutes = [{ type: 'patch', route: StudioRoutePath.SAMF_CRUD, func: updateSamf, schema: 'UpdateSamf', permissions: isSamfOwner }]
- PhilipTrauner 5y agoThere's a similar project (https://github.com/erik/squabble https://github.com/erik/squabble), that uses a portable version of Postgres' SQL parser to spit out an AST, which lints can consequently traverse. This approach alleviates the need for bringing up a Postgres cluster to lint the schema. I'd be curious to know if there's something to be gained by depending on a running database instance (especially because it complicates CI).
- yasserf 5y agoThat's an interesting approach. I usually run a postgres db (as a docker image on circleCI) when I'm running the build. I'm not sure how scalable it is (currently we have a hundred migrations and it's a few seconds so not bad). The flow is: - run migrations on an empty DB - create the interfaces and enums - run typescript - derive all the json schemas from the interfaces for API validation This means we gitignore the actual DB Types which is great in order to reduce the amount of sources of truth and deal with possible issues This all runs as a pre-commit hook as well. I guess by doing so we sort of validate the SQL is legit via postgres itself. The only issue we face after a commit gets passed the setup stage and tries to run the migration itself is if theres a data* inconsistency we weren't aware of (trying an enum value we thought wasn't used but was). But that sort of things requires special tasks anyways and the migration fails so nothing ever goes wierd (which is why I <3 SQL migrations )
- PhilipTrauner 5y ago> This all runs as a pre-commit hook as well. Keep in mind that this might become problematic for folks that are running Postgres inside Docker on macOS or Windows, as IO performance is quite poor. I put together a up / down migration validation system a while ago (start up two databases, apply all-1 up migrations on A and all up migrations + final down migration on B) for a pretty sizable schema. Folks that were using Docker had to wait upwards of two minutes, while natively installed Postgres would finish in under 10sec.
- yasserf 5y ago
- brailsafe 5y agoWhat exactly do you mean by competitor? Is there a reason you'd want more people using your open source project than another?