Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 57 additions & 24 deletions docs/cookbook/web/database/creating-objects.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
# Creating objects in a database

- Preparation: **1 minute**
- Preparation: **5 minutes**
- Requirements:
- Initiated Syncano project

### Problem to solve

You want to create two object of different classes.
A very common requirement for an application is to have a persistent storage of data. Syncano Provides this capability with a concept of Data Classes, which define the schema (type) of data you would like to store and Data Objects which store the data. This cookbook entry will walk you through a simple data set up.

### Solution

Create empty `hello-world` Socket and `hello` endpoint, use `data` from `@syncano/core` library.
We will create a Data Class - the "container" for your data, and learn how to create Data Objects in that class.

#### Create Socket
#### Create a new Socket

We will start with creating an empty Socket. You can skip this step, if you have an existing Socket you'd like to expand.

```sh
npx syncano-cli create hello-world --template example
npx s create hello-world

? Choose template for your Socket
Vanilla JS Socket - (@syncano/template-socket-vanilla)
❯ ES6 Socket - (@syncano/template-socket-es6)

✔ Your Socket configuration is stored at ...
```

#### Add data class

#### Add a Data Class


Add `book` class to the `syncano/hello-world/socket.yml` file:

Expand All @@ -28,41 +38,64 @@ classes:
- name: title
type: string
- name: pages
type: ineger
type: integer
```

#### Edit endpoint file
Save the `.yml` file and update your remote Socket:
```sh
npx s deploy hello-world
```

> See Docs on [Data Classes](https://0-docs.syncano.io/#/building-sockets/data-classes?id=overview) for more info on available data types

#### Edit Socket Endpoint file

Edit file `syncano/hello-world/src/hello.js` and change its content to:

```js
import Syncano from '@syncano/core'

export default (ctx) => {
const {data, response} = Syncano(ctx)

data.book.create({
title: 'Peter Pan',
pages: 334
})
.then(bookObj => {
response.json({msg: `Book with ID ${bookObj.id} created!`})
})
const { data, response } = new Syncano(ctx)
const { title, pages } = ctx.args

data.book.create({ title, pages })
.then(bookObj => {
response.json({msg: `Book with ID ${bookObj.id} created!`})
}).catch(({ data, status }) => response.json(data, status))
}
```

### How it works?
We've imported `data` and `response` modules from the Syncano Core library. `data` is the one you will use for data operations (list, create update, delete and so on).

Now you can call URL for `hello` endpoint using browser:
We also destructure `title` and `pages` from `ctx.args` object. This data will come from the client side.

Once you are finished editing the `hello.js` file, run `npx s deploy hello-world` to apply the changes to the script.

> Pro tip: use `npx s hot hello-world` to watch for changes and continuous deployment

### Client Side

The server side is ready so now the only thing left to do, is adding a client side implementation. Create an index.html file, add the code below, and save:

> Remember to change 'YOUR_INSTANCE' into the instance attached to your project. Run `npx s` in the project folder to have it printed out in your terminal.

```js
<script src="https://unpkg.com/@syncano/client"></script>
<script>
const s = new SyncanoClient('YOUR_INSTANCE')

s.post('hello-world/hello', { title: 'PeterPan', pages: 334 })
.then(res => {
console.log(res.msg)
})
</script>
```
https://<your_instance_name>.syncano.space/hello-world/hello/
```
> You can find URL for `hello` endpoint by typing `npx syncano-cli list hello-world`

You will get a response JSON response like this one:
Now, when you open the `index.html` file and look in the browser console, you will see a log similar to this one:

```js
{"msg": "Book with ID 123 created!"}
'Book with ID 3 created!'
```

Now you can retrieve it from Syncano by running `data.book.find(objectId)` with the core library.
236 changes: 236 additions & 0 deletions docs/cookbook/web/database/object-relations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# Data Object relation and reference fields

- Preparation: **30 minutes**
- Requirements:
- Initiated Syncano project
- An existing Socket

### Problem to solve

There are cases, where you'd like to have links between Data Objects. There are 3 possible options here:

one-to-one - One Data Object is connected to another.
one-to-many - One Data Object has relations with many other.
many-to-many - This type of relation allows for creating a net of connections.

We will dig into those solutions in the sections below.


### Creating one-to-one reference

one-to-one relationships can come in handy in a couple of situations:
- Limiting access to part of the stored data - In this case Data Objects could be in different Data Classes, where different users would have access to each of these Data Classes. This would allow making only part of the data visible.
- Splitting Data Object because of size limits - Data Object size limit is 32 kB. In case this is not enough you could store rest of the data in a referenced Data Object (another, often better, alternative is using the file field for data storage).

#### Add a Data Class

Use an existing Socket or create one with `npx s create`. Add the following in the `socket.yml` file:

```yaml
classes:
first:
- name: info
type: string
second:
- name: info
type: string
- name: reference
type: reference
target: public

endpoints:
create-objects:
description: Create two Data Objects
```

Save the `.yml` file and update your remote Socket:
```sh
npx s deploy <socket-name>
```


#### Edit Socket Endpoint file

Create/edit file in `syncano/<SOCKET>/src/<SOCKET_NAME>.js` and change its content to:

```js
import Syncano from '@syncano/core'

export default (ctx) => {
const { data, response } = new Syncano(ctx)

data.first.create({ info: 'A publicly available data' })
.then(dataObj => {
console.log(`Data Object ${dataObj.id} in first class created`)

data.second.create({
info: 'Additional data stored in a second object',
reference: dataObj.id })
.then(dataObj => {
console.log(`Data Object ${dataObj.id} in a second class created`)
})
})
.catch(({ data, status }) => {
return response.json(data, status)
})
}
```

> Pro tip: use `npx s hot <socket_name>` to watch for Socket changes and continuous deployment

Now you can run `npx s call <socket>/create-objects` and observe that two Data Objects were created. The second one will store the ID of a first one as a reference.

### Creating one-to-any and many-to-many relations

To create one-to-many or many-to-many relationships you can use Syncano's relation field. It's an array where you can store ids of connected Data Objects. So, for example if you had an authors and books Data Classes you could create a relation field in the authors Data Class. This field could hold an array of Data Object ids representing books from the books Data Class.

#### Add Data Classes and Socket Endpoint

Edit your `socket.yml` file to contain the following fields:

```yaml
classes:
books:
- name: title
type: string
authors:
- name: name
type: string
- name: books
type: relation
target: books

endpoints:
add-library-entry:
description: Add books and a corresponding author
parameters:
name:
type: string
description: A book author name
books:
type: array
description: An array of book objects
example: |
[
{ title: 'The Adventures of Tom Bombadil' },
{ title: 'A Dream of Spring'}
]
```

#### Edit the Socket Endpoint file

Here's a simple example of how you could use Socket Scripts to add Data Objects with relation fields. Add the `add-library-entry.js` file in the `/src` folder and paste the code below.


```js
import Syncano from '@syncano/core'

export default (ctx) => {
const { data, response } = new Syncano(ctx)
const { books, name } = ctx.args

// data.<class>.create also accepts an array of objects. and this is what
// I'm using this feature to populate the library with books as 'books'
// is an array of objects: [{ title: ...}, { title: ...}] etc.
data.books.create(books)
.then(res => {
// response is an array of created book Data Objects. I'm getting the
// ids to pass as relations to the author object
const ids = res.map(obj => obj.id)

data.authors.create({ name, books: ids })
.then(res => {
return response.json(res)
})
.catch(({ data, status }) => {
return response.json(data, status)
})
})
.catch(({ data, status }) => {
return response.json(data, status)
})
}
```

> Remember to save and deploy your socket with `npx s deploy <socket-name>`

#### Client side

On the client side, you would make a post request to the `add-library-entry` with a payload in form of an object containing the `name` and `books` properties. The server side code from the previous section would split the payload and create separate entries with relations in the database.

> Remember to change 'YOUR_INSTANCE' into the instance attached to your project. Run `npx s` in the project folder to have it printed out in your terminal.

```html
<script src="https://unpkg.com/@syncano/client"></script>
<script>
const s = new SyncanoClient('YOUR_INSTANCE')

s.post('new-sockit/add-library-entry', {
name: 'J. R. R. Tolkien',
books: [
{ title: 'The Adventures of Tom Bombadil' },
{ title: 'On Fairy-Stories, Smith of Wootton Major' }
]
})
.then(res => {
console.log(res)
})
</script>
```

When you observe the console output in your browser dev tools, you'll notice the created Data Object. It should look, more or less, like this:

```json
{
"id": 32,
"created_at": "2018-03-08T13:20:11.338191Z",
"updated_at": "2018-03-08T13:20:11.338224Z",
"revision": 1,
"acl": {},
"channel": null,
"channel_room": null,
"name": "J. R. R. Tolkien",
"books": [30, 31],
"links": {
"self": "/v2/instances/instance/classes/authors/objects/32/"
}
}
```

As you can see, the `books` relation field of this `author` Data Object got populated with the ids of `book` Data Objects.

#### Expanding the requested Data Objects with the referenced ones

The default behavior when making a get request for a Data Object with a reference field is to only return the referenced object ID in this field. It's possible, however, to get the reference field expanded with the second Data Object. This is the core library code that would achieve this:

```js
import Syncano from '@syncano/core'

export default (ctx) => {
const { data, response } = new Syncano(ctx)

data.authors.with('books')
.list()
.then(objects => {
return response.json(objects)
})
}
```

You will receive a list of full Data Objects available in the `books` relation field:

```json
[
{ "id": 6,
"name": "J. R. R. Tolkien",
"books": [
{
"id": 5,
"title": "On Fairy-Stories"
}
]
}
]
```

> `with` property works both for `relation` and `reference` fields. It will return an array of Data Objects for `relation` field and a nested Data Object for `reference` field.
2 changes: 0 additions & 2 deletions test

This file was deleted.