Creating Data on the Server
Estimated Time: 5min Difficulty: Easy Version: 0.1.*
Context
Will teach you how to create an Item for the previous defined DataType
Prerequisites
- raclette CLI installed
- raclette Server running
- Node.js 24+
- You have finished the setting up a todo plugin example and it's up and running
Declare create Service
In our ./plugins/example-todoplugin/backend/todo.service.ts we now want to define the service logic for our data creation
typescript
import type {
Todo,
Todo as TodoType,
} from "./todo.schema"
import type { QueryOptions } from "@_/types/service"
import { v4 as uuidv4, validate } from "uuid"
import { createTodoPayload } from "./helpers/todoHelper"
import type {
PluginFastifyInstance,
FrontendPayload,
FrontendPayloadRequestData,
} from "@raclettejs/core/types"
import { Model } from "mongoose"
export class TodoService {
private todoModel: Model<Todo>
constructor(model: Model<Todo>) {
this.todoModel = model
}
[...]
async _createTodo(
fastify: PluginFastifyInstance,
todoBody: TodoCreate,
): Promise<TodoType> {
try {
if (todoBody._id) {
const uuidValid = validate(todoBody._id)
if (!uuidValid) {
throw new Error("Invalid ID - not a valid uuid v4")
}
const duplicate = await this.todoModel.findById(todoBody._id)
if (duplicate) {
throw new Error("An entry with this id already exists")
}
} else {
todoBody._id = uuidv4()
}
const todo = new this.todoModel(todoBody)
await todo.save()
fastify.log.info(`[API] Created todo #todo._id}`)
return todo.toObject ? todo.toObject() : todo
} catch (err: any) {
fastify.log.error(err.message)
throw err
}
}
/**
* Create a new todo with payload wrapping and event emission
*/
async createTodo(
fastify: PluginFastifyInstance,
requestData: FrontendPayloadRequestData,
todoBody: TodoCreate,
): Promise<FrontendPayload<TodoType[]>> {
const todo = await this._createTodo(fastify, todoBody)
const payload = await createTodoPayload(fastify, [todo], requestData)
if (requestData.broadcast) {
fastify.emit("todoCreated", payload)
}
return payload
}
[...]
}
export const createTodoService = (model: Model<Todo>) => {
return new TodoService(model)
}Declare route on the backend side
In our ./plugins/example-todoplugin/backend/routes/ we want to create a new route called todo.create.ts
typescript
import type { Static } from "@sinclair/typebox"
import type { FastifyReply, FastifyRequest } from "fastify"
import { Type } from "@sinclair/typebox"
import type { PluginFastifyInstance } from "@raclettejs/types"
const ParamsSchema = Type.Object({
_id: Type.String(),
})
type Params = Static<typeof ParamsSchema>
export default (fastify: PluginFastifyInstance) => {
const handler = async (
req: FastifyRequest<{
Params: Params
}>,
reply: FastifyReply,
) => {
try {
// Add owner and lastEditor from the authenticated user
const todoData = {
...req.body,
owner: req.user._id,
lastEditor: req.user._id,
}
const payload = await fastify.custom.todoService.createTodo(
fastify,
req.requestParams,
todoData,
)
return reply.status(201).send(payload)
} catch (err: any) {
fastify.log.error(err.message)
return reply.internalServerError(err.message)
}
}
return {
handler,
onRequest: [fastify.authenticate],
config: {
type: dataCreate,
broadcastChannels: ["todoCreated", "dataCreated"],
},
schema: {
summary: "Example todo post Route",
description: "Example todo create Route",
tags: ["myApp/todo"],
body: todoCreateSchema,
},
}
}Creating data in our widget
Now we can query our endpoint with the frontendApi fron our component
typescript
const { $data, [...] } = usePluginApi()
const {data, dataArr, response, query, execute, isLoading, error } = $data.todo.create({
params: {},
id: false,
options: {
immediate: false,
cb: (result) => {},
notify: true | false,
responseType: "json",
mode: "none" | "cors",
useCache: true | false,
useStore: true | false,
},
config: {}
})Returns
- data - The resultobject
- dataArr - The resultArray
- response - The response from the server
- query - the query object (only in reading queries)
- execute - an awaitable function to trigger the action call. The response and result returned from this functions are not reactive! Use dataArr or data
- isLoading - a boolean indicator for the loading/fetch state
- error - contains the error object if errors are catched
Props
- id - if set this will be used instead of the query instance nanoId
- params - The object you want to send to the backend
- options
- immediate - If true, the action will be executed right away, defaults to false
- cb - A function which will get called after the action is resolved. Receives the result object
- useStore - If false, will not write the data to the redux store. The data return will also not be available, use response instead. defaults to true
- useCache - If false, will bypass the global query cache, important for streams of if you always want fresh data if triggered. Cachetime is 1000ms and only makes sure that several unique queries, having the exact same params are not queried multiple times but instead only once within the ttl. Defaults to true
- notify - if false, will not write into the notification que and snackbar, defaults to true
- responseType - only used if you want to explicitly receive a http stream. Set to stream in that case, defaults to json
- mode - if responseType is stream, you can set mode "cors" here
After defining our action and the corresponding outputs we can now trigger the action when needed.
typescript
const createData = async (newItem) => {
// log the action outputs before and after to see what happens!
console.log(data, query, execute, isLoading, error)
const { result, response } = await execute(newItem)
// log the action outputs before and after to see what happens!
console.log(result, response)
console.log(data, query, execute, isLoading, error)
}TIP
Be aware that the returns of execute are not reactive like the data and dataArr