doc: databases

This commit is contained in:
cha0s 2024-01-05 13:47:03 -06:00
parent 154cbc9c32
commit 8556ae0b4c

View File

@ -46,30 +46,82 @@ Let's create a fleck that makes a model so we can get a feel for how it works.
First, create a fleck in your application:
<Create pkg="tags" type="fleck" />
<Create pkg="content" type="fleck" />
Now, let's hop into `packages/tags/src/index.js` and add a hook implementation:
Now, let's hop into `packages/content/src/index.js` and add a hook implementation:
```javascript title="packages/tags/src/index.js"
```javascript title="packages/content/src/index.js"
export const hooks = {
'@flecks/db/server.models': (flecks) => {
const {Model, Types} = flecks.fleck('@flecks/db/server');
class Tags extends Model {
class Content extends Model {
static get attributes() {
return {
key: {
type: Types.STRING,
allowNull: false,
},
value: {
type: Types.STRING,
text: {
type: Types.TEXT,
allowNull: false,
},
};
}
};
return {
Tags,
Content,
};
},
}
```
Now, `npm start` your application and you will see that line looks different:
```
@flecks/core/flecks gathered '@flecks/db/server.models': [ 'Content' ] +0ms
```
Our model is recognized!
Let's add one more model and create an association between them:
```javascript title="packages/content/src/index.js"
export const hooks = {
'@flecks/db/server.models': (flecks) => {
const {Model, Types} = flecks.fleck('@flecks/db/server');
class Content extends Model {
static get attributes() {
return {
text: {
type: Types.TEXT,
allowNull: false,
},
};
}
static associate({Tag}) {
this.hasMany(Tag);
}
};
class Tag extends Model {
static get attributes() {
return {
value: {
type: Types.STRING,
allowNull: false,
},
};
}
static associate({Content}) {
this.belongsTo(Content);
}
};
return {
Content,
Tag,
};
},
}
@ -80,43 +132,48 @@ export const hooks = {
`@flecks/db` uses [Sequelize](https://sequelize.org/) under the hood. You can dive into
[their documentation](https://sequelize.org/docs/v6/getting-started/) to learn even more.
The static `attributes` method above is sugar on top of
[`sequelize.define`](https://sequelize.org/docs/v6/core-concepts/model-basics/#using-sequelizedefine)
and you should consult that documentation for more details on how to define and validate your
models.
To implement associations between models, there is a static `associate` method. Suppose you also
had a `Post` model along with your `Tags` model. You might do something like this in your `Tags`
model:
```
static associate({Post}) {
Post.hasMany(this);
this.hasMany(Post);
}
```
:::
Now, `npm start` your application and you will see that line looks different:
If you were to `npm start`, you would see the line:
```
@flecks/core/flecks gathered '@flecks/db/server.models': [ 'Tags' ] +0ms
@flecks/core/flecks gathered '@flecks/db/server.models': [ 'Content', 'Tag' ] +0ms
```
Our model is recognized!
## Gathering models
## Providing models with `Flecks.provide()`
When building Real:tm: applications we are usually going to need a bunch of models. If we add all
of them into that one single file, things are going to start getting unwieldy. Let's create a
`src/models` directory in our `packages/tags` fleck and add a `tags.js` source file with the
following code:
`src/models` directory in our `packages/content` fleck and add a `content.js` and `tag.js` source
files with the following code:
```javascript title="packages/tags/src/models/tags.js"
```javascript title="packages/content/src/models/content.js"
export default (flecks) => {
const {Model, Types} = flecks.fleck('@flecks/db/server');
return class Tags extends Model {
return class Content extends Model {
static get attributes() {
return {
text: {
type: Types.TEXT,
allowNull: false,
},
};
}
static associate({Tag}) {
this.hasMany(Tag);
}
};
};
```
```javascript title="packages/content/src/models/tag.js"
export default (flecks) => {
const {Model, Types} = flecks.fleck('@flecks/db/server');
return class Tag extends Model {
static get attributes() {
return {
key: {
@ -129,16 +186,24 @@ export default (flecks) => {
},
};
}
static associate({Content}) {
this.hasMany(Content);
}
};
}
};
```
Notice that this looks very similar to how we defined the model above, but this time we're only
returning the class.
Notice that this looks very similar to how we defined the models above, but this time we're only
returning the classes.
Now, hop over to `packages/tags/src/index.js` and let's rewrite the hook implementation:
Now, hop over to `packages/content/src/index.js` and let's rewrite the hook implementation:
```javascript title="packages/content/src/index.js"
import {Flecks} from '@flecks/core/server';
```javascript title="packages/tags/src/index.js"
export const hooks = {
'@flecks/db/server.models': Flecks.provide(require.context('./models')),
}
@ -146,65 +211,111 @@ export const hooks = {
We're passing the path to our models directory to `require.context` which is then passed to
`Flecks.provide`. This is completely equivalent to our original code, but now we can add more
models by adding individual files in `packages/tags/src/models` and keep things tidy.
models by adding individual files in `packages/content/src/models` and keep things tidy.
:::info
For a more detailed treatment of gathering in flecks, see [the gathering guide](#todo).
For a more detailed treatment of gathering and providing in flecks, see
[the gathering guide](#todo).
:::
## Working with models
Let's do something with it. Edit `packages/tags/src/index.js` again like
Let's do something with them. Edit `packages/content/src/index.js` again like
so:
```javascript title="packages/tags/src/index.js"
```javascript title="packages/content/src/index.js"
import {Flecks} from '@flecks/core/server';
export const hooks = {
// highlight-start
'@flecks/server.up': async (flecks) => {
const {Tags} = flecks.get('$flecks/db.models');
console.log('There were', await Tags.count(), 'tags.');
const {Content, Tag} = flecks.get('$flecks/db.models');
console.log(
'There were',
await Content.count(), 'pieces of content',
'and',
await Tag.count(), 'tags.',
);
},
// highlight-end
'@flecks/db/server.models': Flecks.provide(require.context('./models')),
}
```
We have to configure `build/flecks.yml` so that the database comes up before we try to use it:
```yml
'@db_test/content:./packages/content/src': {}
'@flecks/core':
id: db_test
'@flecks/db': {}
// highlight-start
'@flecks/server':
up:
- '@flecks/db'
- '@db_test/content'
// highlight-end
```
Now, another `npm start` will greet us with this line in the output:
```
There were 0 tags.
There were 0 pieces of content and 0 tags.
```
Not very interesting. Let's add some, but only if there aren't any tags yet:
Not very interesting. Let's add some, but only if there aren't any yet:
```javascript title="packages/tags/src/index.js"
```javascript title="packages/content/src/index.js"
export const hooks = {
'@flecks/server.up': async (flecks) => {
const {Tags} = flecks.get('$flecks/db.models');
console.log('There were', await Tags.count(), 'tags.');
const {Tag} = flecks.get('$flecks/db.models');
console.log(
'There were',
await Content.count(), 'pieces of content',
'and',
await Tag.count(), 'tags.',
);
// highlight-start
if (0 === await Tags.count()) {
await Tags.create({key: 'foo', value: 'bar'});
await Tags.create({key: 'another', value: 'thing'});
if (0 === await Content.count()) {
await Content.create(
{text: 'lorem ipsum', Tags: [{value: 'cool'}, {value: 'trending'}]},
{include: [Tag]}
);
await Content.create(
{text: 'blah blah', Tags: [{value: 'awesome'}]},
{include: [Tag]}
);
}
console.log('There are', await Tags.count(), 'tags.');
console.log(
'There are',
await Content.count(), 'pieces of content',
'and',
await Tag.count(), 'tags.',
);
// highlight-end
},
'@flecks/db/server.models': Flecks.provide(require.context('./models')),
}
```
Another `npm start` and we see the tags created!
Another `npm start` and we see:
```
There were 0 pieces of content and 0 tags.
There are 2 pieces of content and 3 tags.
```
Great!
## Persistence
You'll notice that if you run it again, it will always say
```
There were 0 tags.
There are 2 tags.
There were 0 pieces of content and 0 tags.
There are 2 pieces of content and 3 tags.
```
What's up with that? Remember in the beginning:
@ -212,12 +323,13 @@ What's up with that? Remember in the beginning:
> By default, flecks will connect to an in-memory SQLite database to get you started instantly.
This means that the database will only persist as long as the life of your application. When you
restart it, you'll get a fresh new database every time. Obviously, this isn't very helpful for
any real purpose. Let's make a change to our `build/flecks.yml`:
restart it, you'll get a fresh new database every time. It was **quick to get started** developing,
but this isn't very helpful for any real purpose. Let's make a change to our `build/flecks.yml`:
```yml title="build/flecks.yml"
'@db_test/tags:./packages/tags': {}
'@flecks/core': {}
'@db_test/content:./packages/content/src': {}
'@flecks/core':
id: db_test
'@flecks/db': {}
// highlight-start
'@flecks/db/server':
@ -229,16 +341,16 @@ any real purpose. Let's make a change to our `build/flecks.yml`:
Now `npm start` again. You'll see our old familiar message:
```
There were 0 tags.
There are 2 tags.
There were 0 pieces of content and 0 tags.
There are 2 pieces of content and 3 tags.
```
This time though, our application wrote the SQLite database to disk at `./persistent.sql`. If we
give it one more go, we'll finally see what we expect:
```
There were 2 tags.
There are 2 tags.
There were 2 pieces of content and 3 tags.
There are 2 pieces of content and 3 tags.
```
A persistent database!
@ -277,8 +389,9 @@ Let's add another fleck to our project:
Configure `build/flecks.yml`:
```yml title="build/flecks.yml"
'@db_test/tags:./packages/tags': {}
'@flecks/core': {}
'@db_test/content:./packages/content/src': {}
'@flecks/core':
id: db_test
'@flecks/db': {}
// highlight-start
'@flecks/db/server':
@ -291,13 +404,13 @@ Configure `build/flecks.yml`:
up:
- '@flecks/docker'
- '@flecks/db'
- '@db_test/tags'
- '@db_test/content'
// highlight-end
```
Notice that we configured `@flecks/server.up` to make sure to enforce a specific order in which
our server flecks come up: first `@flecks/docker` to spin up the database, then
`@flecks/db` to connect to the database, and finally our `@db_test/tags` fleck to interact with
`@flecks/db` to connect to the database, and finally our `@db_test/content` fleck to interact with
the database. This is important!
Now `npm start` will reveal the following message in the logs: