Adds experimental RedwoodRecord package#3734
Conversation
|
I know there has been a long, multi-year philosophical discussion about whether Prisma is or is not an ORM or query builder or ham sandwich and I have never used ActiveRecord so this may be a naive question, but I have a question based on this part of the README:
What exactly do you mean by "database access packages," and what would be some examples of those? Where would tools like Sequelize, TypeORM, or Knex fall along this spectrum? |
I'm using this to mean Prisma, and other libs like that. I don't know what other ones exist in the JS world, but I'm thinking of those similar to Prisma that act as the JS interface to the database. An ORM will generally return an instance of a class with additional functionality besides the plain objects that something like Prisma returns. |
|
Although Prisma does, literally, map a row in a database to an object, I think the "object" in ORM was originally meant to be an Object as in Object Oriented Programming. That object can be enhanced with custom functionality that mixes in with your data. For example, if you only stored first and last name in the database, you could add: export class User extends RedwoodRecord {
get fullName() {
return this.firstName + ' ' + this.lastName
}
}And now you can use I haven't got to this in the README yet but validations are built into RR as well, so now before any record can even be saved to the database it can be checked for validity first, and return I've also got a whole lifecycle workflow coming, so you can define something to do Unfortunately it can lead to bad habits where you might want to, say, send a welcome email after a new user is created. Well I'll just create an |
|
I can't wait to hear what people think of the mixin concept, where you can add functionality to a class at definition time, not just by extending from another object! It's very common in Ruby (of course) but it's actually a thing in JS: https://javascript.info/mixins I'm using a different syntax where you basically wrap the class you're extending in the mixin: import Core from './Core'
import ValidationMixin from './ValidationMixin'
export default class RedwoodRecord extends ValidationMixin(Core) {
// ...
}This allows me to keep the functionality self-contained in As the saying goes, you should prefer composition over inheritance! |
I have! I've started out separating the concept of validation into a mixin so it could be added to any class. The idea being that you could create some kind of class that needed validation and reporting errors, outside of saving to a database. Only the There are a couple other classes for reading through the relationships between models and creating properties/methods, which are also specific to how Prisma organizes things, but they could be expanded to work with more backends as well. Lifecycle callbacks ( |
|
Awesome, sounds great indeed! Do you plan on updating your "Readme Driven Development" document with thoughts/specs on this part? |
|
I know this is a work in progress and all that jazz, but just wanted to say I LOVE EVERYTHING ABOUT THIS AND I CAN'T WAIT FOR IT TO DROP! It's like y'all read my mind or something. Or maybe used the same framework for a decade before building this one. 😉 |
|
Docs in redwoodjs.com are ready as well: https://deploy-preview-889--redwoodjs.netlify.app/docs/redwoodrecord PR here: https://github.com/redwoodjs/redwoodjs.com/pull/889 |
|
Okay, I think this is ready for review! We need to figure out a typescript strategy...there are no types here because I couldn't figure out how to do it: the properties on the class are created dynamically based on the data that comes back from the database. From what little I know about Typescript, this wouldn't be something TS would be good at? We are clearly marking this as experimental so maybe we ship it as is and let community feedback drive how we do it going forward. I'm getting a build error I've never seen before: https://github.com/redwoodjs/redwood/runs/4317926050?check_suite_focus=true It's suggesting to run |
|
@cannikin I'll have a look at the |
|
This looks very cool 👍🏻 |
THE FUTURE IS UPON US
This adds RedwoodRecord as a package, but we'll mark it as "experimental." This means that to start we won't necessarily recommend people add it to their nuclear power plant control software, but it's there to play around with. We'll try to hold it to the same standards as the rest of the code (making it as bug-free as we can) but we haven't fully vetted the side effects to the developer experience. Is it easier to write services, but encourages some bad habits? Does it make larger codebases easier or harder to reason about? We won't know until we get some folks using it and they let us know! It also means we won't make big, splashy announcements about it on social media or the forums just yet. But if it turns out to be the best thing to happen to Javascript since the arrow function syntax, you better believe we'll be telling the world!
This was originally built right inside of a Redwood app so it's going to take some work to port over, mostly to get tests working again since there's no database to test against. Time to mock all of Prisma. 😭
Usage
There will be a full doc available when this makes it into
main. Here's the original Readme Driven Development document: https://gist.github.com/cannikin/c22309dd3b87d9bb4342910d2c930b5aAnd here are the basics in three easy steps:
Step 1
Create an
api/src/modelsdirectory and in there, create a model for every database table you want to make accessible via RedwoodRecord (generator coming soon). The name of the file/class inside should match the case you gave the model inschema.prisma. For example, pretty much everyone will have a User:Pretty simple. This gets you the base functionality of RedwoodRecord, but this is a class so you can add your own functionality (functions and properties).
Step 2
Run the following command to generated a cached copy of
schema.prismain JSON format, and addapi/models/index.jswhich sets up your models and exports them for use in your app:You'll need to run this command any time you add/remove a model file, as well as any time you change your database schema (by adding/editing/removing models in
schema.prisma). We should be able to watch those files and run this command for you, but as of right now it's a manual process.Step 3
Start using it! Eventually there will be full documentation with usage examples, but for now you can look through the README in
packages/recordor this gist.The most life-changing use of it will be in
auth.jswhere you can havegetCurrentUser()return an instance of a User model:And suddenly all of your services have access to an instance of your User model via
context.currentUser, including all of the relationships between models! So if a User has many Posts, then in yourposts.jsservice can simply become:Pretty cool!! Everything is scoped to the current user automagically (assuming you setup your relationships properly in
schema.prismaand create bothUserandPostmodels).You'll see a warning in the console if you have a relationship defined in
schema.prismabut don't have a corresponding model for it. I can add an ENV flag to suppress that warning, in case some folks really only want part of their schema defined as RedwoodRecord models.TODO