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
Errorwill 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]
}