CRUD Operations
Introduction​
Once you created your entities, you probably want to interact with them. It is easy to connect your client to your Manifest backend.
Manifest provides out-of-the-box CRUD endpoints through the REST API or the JS SDK.
CRUD operations can be restricted using access policies.
Using the REST API​
Manifest exposes a REST API for CRUD operations. The OpenAPI documentation is automatically generated on http://localhost:1111/api. Have a look!
For CRUD endpoints, this prefix is followed by collections
for collections entities and singles
for single entities and by the slug of your entity (you can change it in the entity params)
Examples:
http://localhost:1111/api/collections/cats
gets the list of the catshttp://localhost:1111/api/singles/home-content
gets the home content
In addition to CRUD endpoints that are generated automatically, you also can create your own custom endpoints to add your custom logic.
Using the JavaScript SDK​
The Manifest JS SDK is used to fetch and manipulate your data from your JS client using an elegant and human-friendly interface.
The SDK can be integrated in any frontend stack app like React, Vue, Svelte.... Or even by another server using NodeJS!
Install it via the terminal:
npm i @mnfst/sdk
Use the SDK directly in your favorite frontend:
import Manifest from '@mnfst/sdk'
// Initialize client with default backend URL: http://localhost:1111.
const manifest = new Manifest()
// Initialize client with custom base URL.
const manifest = new Manifest('https://example.com')
// Perform CRUD operations...
const cats = await manifest.from('cats').find()
Collections​
The following CRUD operations can be done on collections entities. A collection is a list of items that share a similar schema.
Get a list of items​
This operation will fetch a list of items from a collection.
- REST API
- JS SDK
Request URL: GET /api/collections/:slug
GET /api/collection/users
{
"data": [
{
"id": 1,
"name": "Lara"
},
{
"id": 2,
"name": "Karl"
}
],
"currentPage": 1,
"lastPage": 1,
"from": 1,
"to": 10,
"total": 3,
"perPage": 10
}
List filters
You can filter by property to refine the list of items. Use suffix to pass logic to it:
Suffix | Description | Example |
---|---|---|
_eq | equals | isActive_eq=true |
_neq | not equals | name_neq=alice |
_gt | greater than | birthdate_gt=2020-01-01 |
_gte | greater than or equal | age_gte=4 |
_lt | less than | amount_lt=400 |
_lte | less than or equal | amount_lte=400 |
_like | like | name_like=%bi% |
_in | included in | customer_in=1,2,3 |
Pagination
All list requests are paginated by default. Just use the page
parameter to choose your page and the perPage
param if you want to change the number of items per page.
Param | Description | Example |
---|---|---|
page | The number of the page requested | page=3 |
perPage | The number of items of each page | perPage=40 |
Order
Order your list by a defined property. By default the results are ordered by id
in a DESC
order and thus shows the new ones first.
Param | Description | Example |
---|---|---|
orderBy | The name of property you want to order by. | orderBy=age |
order | Ascending 'ASC' or Descending 'DESC' | order=DESC |
// Get all users.
const users = await manifest.from('users').find()
console.log(users);
// Output: {
// "data": [
// {
// "id": 1,
// "name": "Lara"
// },
// {
// "id": 2,
// "name": "Karl"
// }
// ],
// "currentPage": 1,
// "lastPage": 1,
// "from": 1,
// "to": 10,
// "total": 3,
// "perPage": 10
// }
List filters
You can filter by property to refine the list of items. Use the where()
function with the correct operator to do it.
const cats = await manifest
.from('cats')
.where('breed = siamese')
.andWhere('active = true')
.andWhere('birthDate > 2020-01-01')
.find()
Filter operators
Operator | Description | Example |
---|---|---|
= | equals | .where('isActive = true') |
!= | not equals | .where('name != alice') |
> | greater than | .where('birthdate > 2020-01-01') |
>= | greater than or equal | .where('age >= 4') |
< | less than | .where('amount < 400') |
<= | less than or equal | .where('amount <= 400') |
like | like | .where('name_like=%bi%') |
in | included in | .where('customer_in=1,2,3') |
Pagination
All list requests are paginated by default. Just use the page
parameter to chose your page and the perPage
param if you want to change the number of items per page.
const cats = await manifest.from('cats').find({ page: 1, perPage: 10 })
Order
Order your list by a defined property. By default the results are ordered by id
in a DESC
order and thus shows the new ones first.
// Order cats.
const cats = await manifest.from('cats').orderBy('age', { desc: true }).find()
Get a single item​
This operation will fetch a single item based on its ID.
- REST API
- JS SDK
Request URL: GET /api/collections/:slug/:id
GET /api/collections/cats/34
{
"name": "Mina",
"description": "A really cute cat"
}
// Get cat with ID 34
const cat = await manifest.from('cats').findOneById(34)
console.log(cat);
// Output: {
// name: "Mina",
// description: "A really cute cat"
// }
Create a new item​
This operation will create a new item and store it in the database. The newly created item is returned as response.
- REST API
- JS SDK
Request URL: POST /api/collections/:slug
POST /api/collections/pokemons
Content-Type: application/json
Body:
{
"name": "Pikachu",
"type": "electric",
"level": 3,
}
{
"id": 25,
"name": "Pikachu",
"type": "electric",
"level": 3,
}
// Create a new item in the "pokemons" entity.
const newPokemon = await manifest.from('pokemons').create({
name: "Pikachu",
type: "electric",
level: 3,
})
console.log(newPokemon);
// Output: {
// id: 25,
// name: "Pikachu",
// type: "electric",
// level: 3
// }
Update an item​
This operation will replace an existing item by the payload provided in the request and returns the updated item.
Unlike partial updates, this operation will replace the whole item by the new one. Missing or empty properties will delete the previous ones.
- REST API
- JS SDK
Request URL: PUT /api/collections/:slug/:id
PUT /api/collections/pokemons/25
Content-Type: application/json
Body:
{
"name": "Raichu",
"type": "electric",
"level": 8
}
{
"id": 25,
"name": "Raichu",
"type": "electric",
"level": 8
}
// Updates the Pokemon item with ID 25.
const newPokemon = await manifest.from('pokemons').update(25, {
name: "Raichu",
type: "electric",
level: 8,
})
console.log(newPokemon);
// Output: {
// id: 25,
// name: "Raichu",
// type: "electric",
// level: 8
// }
Patch an item​
This operation will partially replace an existing item and return the updated item.
Unlike fully replacement, this operation will only modify the properties provided in the payload and leave the other ones as they are.
- REST API
- JS SDK
Request URL: PATCH /api/collections/:slug/:id
PATCH /api/collections/pokemons/25
Content-Type: application/json
Body:
{
"level": 5,
}
{
"id": 25,
"name": "Pikachu",
"type": "electric",
"level": 5
}
// Patches the Pokemon item with ID 25.
const newPokemon = await manifest.from('pokemons').patch(25, {
level: 5
})
console.log(newPokemon);
// Output: {
// id: 25,
// name: "Pikachu",
// type: "electric",
// level: 5
}
Delete an item​
This operation will delete permanently an item from the database. This is an irreversible action. The deleted item is returned in the response.
- REST API
- JS SDK
Request URL: DELETE /api/collections/:slug/:id
DELETE api/collections/cats/60
{
"name": "Fido",
"description": "A cute black cat"
}
// Delete the cat with ID 60.
const deletedCat = await manifest.from('cats').delete(60)
console.log(deletedCat);
// Output: {
// name: "Fido",
// description: "A cute black cat"
// }
Singles​
The following operations can be done on singles entities. As there is only a single item per entity, there is no list-related operations, and single items cannot be deleted as we always want to return an object, even if empty.
Get a single item​
- REST API
- JS SDK
Request URL: GET /api/singles/:slug
GET /api/singles/homepage
{
"title": "My title",
"description": "Welcome to my website!"
}
// Get the homepage entity.
const homepage = await manifest.single('homepage').get()
console.log(homepage);
// Output: {
// title: "My title",
// description: "Welcome to my website!"
// }
Update an item​
This operation will replace an existing item by the payload provided in the request. Unlike partial updates, this operation will replace the whole item by the new one. Missing or empty properties will delete the previous ones.
- REST API
- JS SDK
Request URL: PUT /api/singles/:slug
PUT /api/singles/homepage
Content-Type: application/json
Body:
{
"title": "My new title",
"description": "My new description"
}
{
"title": "My new title",
"description": "My new description"
}
// Update single entity.
const newHomepage = await manifest.single('homepage').update({
title: 'My new title',
description: 'My new description'
})
console.log(newHomepage);
// Output: {
// title: "My new title",
// description: "My new description"
// }
Patch an item​
This operation will partially replace an existing item. Unlike fully replacement, this operation will only modify the properties provided in the payload an leave the other ones as they are.
- REST API
- JS SDK
Request URL: PATCH /api/singles/:slug
PATCH /api/singles/homepage
Content-Type: application/json
Body:
{
"title": "My new title",
}
{
"title": "My new title",
"description": "Welcome to my website!"
}
// Update single entity partially.
const homepage = await manifest.single('homepage').patch({
title: 'My new title'
})
console.log(homepage)
// Output: {
// title: "My new title",
// description: "Welcome to my website!"
// }
Work with relations​
If you added relationships between your entities, you probably want to include them in the CRUD operations.
Load relations​
You can specify which relations you want to load with your entities in your query. Eager relations are loaded automatically.
- JS SDK
- REST API
// Fetch entities with 2 relations.
const cities = await manifest
.from('cities')
.with(['region', 'mayor'])
.find()
// Fetch nested relations.
const cities = await manifest
.from('cities')
.with(['region', 'region.country', 'region.country.planet'])
.find()
// Fetch entities with 2 relations.
GET http://localhost:1111/api/dynamic/city?relations=region,mayor
// Fetch nested relations.
GET http://localhost:111/api/dynamic/city?relations=region,region.country,region.country.planet
Filter by relation​
Once the relation is loaded, you can also filter items by their relation id or properties.
- JS SDK
- REST API
// Get all cats that belong to owner with id 1.
const cats = await manifest
.from('cats')
.with(['owner'])
.where('owner.id = 1')
.find()
// Get all cats that have an owner with name "Jorge".
const cats = await manifest
.from('cats')
.with(['owner'])
.where('owner.name = Jorge')
.find()
// Get all cats that belong to owner with id 1.
GET http://localhost:1111/api/dynamic/cats?relations=owner&owner.id_eq=1
// Get all cats that have an owner with name "Jorge".
GET http://localhost:1111/api/dynamic/cats?relations=owner&owner.name_eq=Jorge
Store relations​
To store or update an item with its relations, you have to pass the relation id(s) as a property that end with Id for many-to-one and Ids for many-to-many like companyId
or tagIds
- REST API
- JS SDK
// Store a new player with relations Team and Skill.
POST http://localhost:1111/api/dynamic/players
Content-Type: application/json
{
"name": "Mike",
"teamId": 10,
"skillIds": [1,2,3,4,5]
}
// Store a new player with relations Team and Skill.
const newPlayer = await manifest.from('players').create({
name: 'Mike',
teamId: 10,
skillIds: [1,2,3,4,5]
})
When storing many-to-many relations, you always need to pass an array, even if you just have one single value.
Update relations​
As for updating properties, you can either do a full replacement using the update function (PUT) or a partial replacement using the patch function (PATCH).
- REST API
- JS SDK
// Replaces the whole skill relations by the new skillIds array.
PUT http://localhost:1111/api/dynamic/players/1
Content-Type: application/json
{
name: 'Mike',
teamId: 10,
skillIds: [10, 11]
}
// Updates the team without changing the skills or the name.
PATCH http://localhost:1111/api/dynamic/players/1
Content-Type: application/json
{
teamId: 5,
}
// Replaces the whole skill relations by the new skillIds array.
await manifest.from('players').update(1, {
name: 'Mike',
teamId: 10,
skillIds: [10, 11]
})
// Updates the team without changing the skills or the name.
await manifest.from('players').patch(1, {teamId: 5})