Like you were calling a function locally
// Server
function sum(a, b) {
return a + b
}
// Client
const result = await sum(5, 5)
console.log(result) // 10
Old style using Promises
// Client
sum(5, 5).then(result => {
console.log(result) // 10
})
You can pass or recive any valid JSON value
// Server
function sum({ a, b }) {
return { result: a + b }
}
// Client
const { result } = await sum({ a: 5, b: 5 })
console.log(result) // 10
Remote Procedure Calls are the funniest part of this protocol because functions are created dynamically. Which means we can call a remote function and pass a local one as an argument.
// Server
function square(n, callback) {
callback(n * n)
}
// Client
square(5, result => {
console.log(result) // 25
})
This will work as expected, but is a bad pattern. Because we are creating a new function every time we call square. Instead we should do something like this to create only one function on server.
// Server
function square(n, callback) {
callback(n * n)
}
// Client
function callback(result) {
console.log(result) // 25
}
square(5, callback)
We can throw an error. And catch it on the client.
// Server
function login(email, password) {
if (email === 'johndoe@mail.com' && password === '1234') {
return true
} else {
throw 'Invalid login'
}
}
// Client
try {
const isLogged = await login('wrong', 'wrong')
} catch (message_error) {
console.error(message_error) // > 'Invalid login'
}
If the error throwed is instance of
Error
will be a normal error on server instead of throwing it to the client.
When calling a remote function, the last argument is always a deferred promise.
// Server
function login(email, password, ...args) {
const request = args[args.length - 1]
setTimeout(() => {
if (email === 'johndoe@mail.com' && password === '1234') {
request.resolve(true)
} else {
request.reject('Invalid login')
}
}, 1000)
// We must return request to make this function asynchronous
return request
}
Or you can use your own Promise
function login(email, password) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (email === 'johndoe@mail.com' && password === '1234') {
resolve(true)
} else {
reject('Invalid login')
}
}, 1000)
})
}
Dop does not handle authentication because not all situations needs it. But when we have the typical Server-Client architecture most of the time we need to know what client is calling our functions on server.
When a function is being called remotely the last argument is a Promise instance with an extra property named node
. Which is the node that is calling the function. Is the same instance/object that we got using createNode
.
// Server
function login(email, password, ...args) {
const { node } = args[args.length - 1]
}