Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
terrablue committed Jul 28, 2023
1 parent 8d4e403 commit 6bc1264
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 49 deletions.
2 changes: 2 additions & 0 deletions docs/guide/extending-primate.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ All modules are just subscription objects. You can therefore easily create and
pass modules directly in your configuration file.

```js caption=primate.config.js
import console from "runtime-compat/console";

export default {
modules: [{
name: "ad-hoc module",
Expand Down
40 changes: 35 additions & 5 deletions docs/guide/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,28 @@ Types are defined in the `types` directory, unless specified
filenames are alphanumeric and lowercase-first -- any files not starting with a
lowercase letter will be ignored.

Type files must export a function as their default export, and Primate will
refuse to start if it detects a type that is not a function.
Type files can be described using either implicit or explicit notation. They
either export a function as their default export, or an object containing a
`validate` function property.

Here is an example for a `number` type, a type that makes sure a string is
convertible to a number and outputs a number.
convertible to a number and outputs a number. This example uses implicit
notation.

```js caption=types/number.js
import {is} from "runtime-compat/dyndef";
const numeric = n => !Number.isNaN(Number.parseFloat(n)) && Number.isFinite(n);

export default value => {
/* make sure value is a string, otherwise throw */
// make sure value is a string, otherwise throw
is(value).string();

try {
return numeric(value) && Number(value);
} catch() {
throw new Error(`\`${value}\` is not a number`);
}
}
};
```

If a string can be successfully converted to a number, a `number` type will
Expand All @@ -53,6 +55,34 @@ types like `number` or `bigint` as input, but your main input type will usually
be strings.
!!!

Here is the same example using explicit notation.

```js caption=types/number.js
import {is} from "runtime-compat/dyndef";
const numeric = n => !Number.isNaN(Number.parseFloat(n)) && Number.isFinite(n);

export default {
validate(value) {
// make sure value is a string, otherwise throw
is(value).string();

try {
return numeric(value) && Number(value);
} catch() {
throw new Error(`\`${value}\` is not a number`);
}
},
};
```

!!!
Which notation you use is up to you. If you're only using types for validating
input provided by `request.{body,path,query,headers,cookies}`, the implicit
notation should be enough. If you're planning on store values of those types in
a data store, the explicit notation is better as it allows you to define
additional properties relevant for persisting values.
!!!

You can also create more elaborate types, like `uuid`.

```js caption=types/uuid.js
Expand Down
18 changes: 18 additions & 0 deletions docs/modules/drivers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Data store drivers

## In-memory

## JSON file

The JSON driver accepts a configuration object with the `path` property to
indicate in which file the data will be managed. This file doesn't have to
exist and will be created for you if it doesn't, but you must have permissions
to write to the path.

## SQLite

## MongoDB

## PostGreSQL

## SurrealDB
1 change: 0 additions & 1 deletion docs/modules/mongodb.md

This file was deleted.

1 change: 0 additions & 1 deletion docs/modules/postgresql.md

This file was deleted.

1 change: 0 additions & 1 deletion docs/modules/sqlite.md

This file was deleted.

92 changes: 52 additions & 40 deletions docs/modules/store.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,30 @@ Import and initialize the module in your configuration.
import store from "@primate/store";

export default {
modules: [store()],
modules: [
store(),
],
};
```

By default the module uses an in-memory driver which persists the data only as
long as the application runs. Alternatively you can use the JSON driver which
persists onto a file.
By default the module uses an [in-memory][in-memory] driver which keeps the
data only as long as the application runs. Alternatively you can use the
[JSON file][json-file] driver which persists onto a file.

```js caption=primate.config.js | using the JSON driver
import {default as store, json} from "@primate/store";

export default {
modules: [store({driver: json({path: "/tmp/db.json"})})],
modules: [
store({
driver: json({
filename: "/tmp/db.json",
}),
}),
],
};
```

The JSON driver accepts a configuration object with the `path` property to
indicate in which file the data will be managed. This file doesn't have to
exist and will be created for you if it doesn't, but you must have permissions
to write to the path.

### Use

Create the different stores of your application and their fields in the
Expand All @@ -65,14 +68,15 @@ range of values this field may hold. We here define a `User` store representing
a user of our application.

```js caption=stores/User.js
import {id, string, u8, email} from "primate/@types";
import {primary, string, u8, email, date} from "primate/@types";

export default {
id,
id: primary,
name: string,
age: u8,
email,
}
created: date,
};
```

Adding that store definition to `stores` makes the `User` store available to
Expand All @@ -96,22 +100,21 @@ Unless you [configure this module](#configuration-options) elsewise, all store
definitions are loaded from the `stores` directory. Store files must start with
a capital letter; any other JavaScript files will be ignored. You may
arbitrarily nest store files using directories, creating namespaces:
`stores/Comment.js` and `stores/Post/Comment.js` describe different stores.
Directories must also start with a capital letter and will be otherwise
ignored.
`stores/Comment.js` and `stores/post/Comment.js` describe different stores.
Directories must start with a lowercase letter and will be otherwise ignored.

```js caption=stores/Comment.js
/* this store will be available as `request.store.Comment` in routes */
import {id, string} from "primate/@types";
import {primary, string} from "primate/@types";

export default {
id,
id: primary,
text: string,
};
```

```js caption=stores/Post/Comment.js
/* this store will be available as `request.store.Post.Comment` in routes */
```js caption=stores/post/Comment.js
/* this store will be available as `request.store.post.Comment` in routes */
import {id, string} from "primate/@types";

export default {
Expand All @@ -138,37 +141,44 @@ export default {
```

!!!
If you define your database field `user.age` as a string (for example,
`varchar` in PostGreSQL) and use the above store definition, Primate will
attempt to unpack the value into a JavaScript number that is between 0 and 120.
In case it fails (because you have something like "thirty-two" in this field),
Primate will throw a
If you define your database field `user.age` as a string (for example, `text`
in PostGreSQL) and use the above store definition, Primate will attempt to
unpack the value into a JavaScript number that is between 0 and 120. In case it
fails (because you have something like "thirty-two" in this field), it will
throw a
[`CannotUnpackValue`](/reference/errors/primate/store#cannot-unpack-value)
error and roll back the transaction.
!!!

Types are not only used for mapping to database fields, but also for validating
data before saving it. One of the store actions you can use in routes is
`validate`, which allows you to check that a document is valid before
saving it.
saving it. Normally, you wouldn't call `validate` directly but have `insert` or
`update` call it for you.

```js caption=routes/create-user.js | POST /create-user
import {redirect} from "primate";

export default {
post(request) {
/* prepare a user */
// prepare a user, normally this data would come from a form
const user = {
name: "Donald",
age: 32,
hobbies: ["Fishing"],
};

/* get the User store */
// get the User store
const {User} = request.store;

/* save if valid */
if (await User.validate(user)) {
await User.insert(user);
};
// save if valid
try {
const {id} = await User.insert(user);
return redirect(`/user/${id}`);
} catch (error) {
// return validation errors as JSON to the client
return error.errors;
}
}
}
```
Expand All @@ -177,12 +187,11 @@ export default {
You may have noticed that the document passed validation despite `id` being
unset. This is because unless configured otherwise, stores permit empty field
values. Additionally, `id` is taken to be the primary field, which is
automatically generated on an insert. Had we tried to `update`, Primate would
have thrown an error.
automatically generated on an insert.
!!!

In addition to using type functions, Primate supports using an object with a
`type` function property for validation.
`validate` function property for validation.

```js
import {id, u8, array} from "primate/@types"
Expand All @@ -192,7 +201,7 @@ const between = ({length}, min, max) => length >= min && length <= max;
export default {
id,
name: {
type(value) {
validate(value) {
if (typeof value === "string" && between(value, 2, 20)) {
return value;
}
Expand All @@ -205,10 +214,11 @@ export default {
};
```

When trying to validate the `name` field, Primate will run the `type` function
to determine if the field has passed validation. In case of failure, it would
stop the execution of the route function with the given error. For saving
this field into the database, it will use the driver's base type `"string"`
When trying to validate the `name` field, Primate will run the `validate`
function to determine if the field has passed validation. In case of failure,
it would stop the execution of the route function with the given error. For
saving this field into the database, it will use the driver's base type
`"string"`.

### Strict

Expand Down Expand Up @@ -456,3 +466,5 @@ using `export const strict = false;`.

[repo]: https://github.com/primatejs/primate/tree/master/packages/store
[memory]: https://github.com/primatejs/primate/blob/master/packages/store/src/drivers/memory.js
[in-memory]: /modules/drivers#in-memory
[json-file]: /modules/drivers#json-file
2 changes: 1 addition & 1 deletion docs/modules/types.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Types

This module adds prefined runtime types to your application.
This module adds common runtime types to your application.

## Install

Expand Down

0 comments on commit 6bc1264

Please sign in to comment.