Files
fivem-rpc/rpc
..
2026-09-28 02:05:40 +01:00
2025-07-08 22:27:10 +01:00
2026-09-28 02:05:40 +01:00
2026-09-28 02:05:40 +01:00

FiveM RPC

Call FiveM server, client and NUI listeners like async functions: typed, with timeouts, no event ping-pong

Installation, quick start and package overview: main readme. Typed events: shared-types. Upgrading from 0.1: migration guide.

Exports

Besides createRPC the package exports:

  • RPCError, RPCErrors, RPCErrorDetails - see Errors
  • RPCConfig, RPCEnvironment and the instance types RPCInstanceServer, RPCInstanceClient, RPCInstanceWebview
  • native event types RPCNativeServerEvents, RPCNativeClientEvents, RPCNativeClientNetworkEvents, RPCNativeClientNetworkEventsNames and the lists NATIVE_SERVER_EVENTS, NATIVE_CLIENT_EVENTS, NATIVE_CLIENT_NETWORK_EVENTS accepted by the onNative* methods

RPCConfig

type RPCConfig = {
    env: 'server' | 'client' | 'webview'
    debug?: boolean // default false, logs every registration, call and incoming payload
    timeout?: number // default 5000, ms to wait for a response, 0 disables it
}

An unknown env makes createRPC throw RPCError with code RPCErrors.UNKNOWN_ENVIRONMENT

Errors

Every error from this library is an RPCError. code is one of RPCErrors, message says what to fix, and details names the call when the error comes from one

enum RPCErrors {
    EVENT_NOT_REGISTERED = 'Event not registered',
    UNKNOWN_NATIVE = 'Unknown native event',
    UNKNOWN_ENVIRONMENT = 'Unknown environment',
    TIMEOUT = 'Timed out waiting for response',
    HANDLER_ERROR = 'Listener threw an error',
}

Example error

The server has no onClient('buyItem', ...) listener, so the call from the client rejects:

import { RPCError, RPCErrors } from '@entityseven/fivem-rpc'

try {
    await rpc.emitServer('buyItem', 'water')
} catch (e) {
    if (e instanceof RPCError && e.code === RPCErrors.EVENT_NOT_REGISTERED) {
        e.message // 'No listener for "buyItem" on server. Register it with rpc.onClient("buyItem", ...) in server code.'
        e.details // { event: 'buyItem', uuid: '<uuid>', from: 'client', to: 'server' }
    }
}

How it works

Routing

Server and client talk over FiveM network events, client and webview over NUI messages and NUI callbacks. Webview and server never talk directly: every call between them is relayed by the client of that player. So every client must run createRPC({ env: 'client' }), even with no listeners of its own, or those calls time out

One listener per event

Each on* method keeps one listener per event name. Registering the same name again replaces the previous listener, off* removes it. Directions are separate: onClient('x') and onWebview('x') on the server do not replace each other

Responses, errors and timeouts

  • emit* resolves with the value the listener returns (promises are awaited)
  • no listener on the target: the call rejects with RPCErrors.EVENT_NOT_REGISTERED
  • the listener throws: the target logs the error with console.error, the call rejects with RPCErrors.HANDLER_ERROR and the original message
  • no response within RPCConfig.timeout (default 5000 ms): the call rejects with RPCErrors.TIMEOUT and a late response is ignored. timeout: 0 waits forever
  • emitSelf calls the local listener directly, whatever it throws reaches the caller unchanged
  • emitClientEveryone does not wait for clients: it resolves once sent, failures stay on each client (console.error for a throwing listener, the rest with debug: true)

Player identity

Server listeners (onClient, onWebview) get the calling player's server id as the first argument. It comes from FiveM's source, never from the payload, so a client cannot pose as another player. Use it instead of player ids passed as arguments. A response to emitClient or emitWebview is only accepted from the player it was sent to

Server (source)

onClient

Listens to client event

rpc.onClient('clientServerEvent', (player, arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offClient

Stops listening to client event

rpc.offClient('clientServerEvent')

emitClient

Sends event to specified client

const response = await rpc.emitClient(playerServerId, 'serverClientEvent', someData)
// response will come from client listener with returned data

emitClientEveryone

Sends event to all clients. One-way: clients run their listener but do not answer

await rpc.emitClientEveryone('serverClientEvent', someData)

onWebview

Listens to webview event

rpc.onWebview('webviewServerEvent', (player, arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offWebview

Stops listening to webview event

rpc.offWebview('webviewServerEvent')

emitWebview

Sends event to the webview of specified player

const response = await rpc.emitWebview(playerServerId, 'serverWebviewEvent', someData)
// response will come from webview listener with returned data

onSelf

Listens to server event

rpc.onSelf('serverEvent', (arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offSelf

Stops listening to server event

rpc.offSelf('serverEvent')

emitSelf

Sends event to server

const response = await rpc.emitSelf('serverEvent', someData)
// response will come from server listener with returned data

onCommand

Registers chat command. args are the raw strings typed after the command, validate them yourself. With restricted set to true only players with the ACE permission command.<name> can use it (defaults to false)

rpc.onCommand('serverCommand', (player, args, rawCommand) => {
    // logic
}, true /* restricted */)

onNativeEvent

Listens to native server event (reference)

rpc.onNativeEvent('playerJoining', (source, oldId) => {
    // logic
})

Client (source)

onServer

Listens to server event

rpc.onServer('serverClientEvent', (arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offServer

Stops listening to server event

rpc.offServer('serverClientEvent')

emitServer

Sends event to server

const response = await rpc.emitServer('clientServerEvent', someData)
// response will come from server listener with returned data

onWebview

Listens to webview event

rpc.onWebview('webviewClientEvent', (arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offWebview

Stops listening to webview event

rpc.offWebview('webviewClientEvent')

emitWebview

Sends event to own webview

const response = await rpc.emitWebview('clientWebviewEvent', someData)
// response will come from webview listener with returned data

onSelf

Listens to client event

rpc.onSelf('clientEvent', (arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offSelf

Stops listening to client event

rpc.offSelf('clientEvent')

emitSelf

Sends event to client

const response = await rpc.emitSelf('clientEvent', someData)
// response will come from client listener with returned data

onCommand

Registers chat command. args are the raw strings typed after the command, validate them yourself

rpc.onCommand('clientCommand', (player, args, rawCommand) => {
    // logic
})

onNativeEvent

Listens to native client event (reference)

rpc.onNativeEvent('entityDamaged', (victim, culprit, weapon, baseDamage) => {
    // logic
})

onNativeNetworkEvent

Listens to native client network event (reference)

rpc.onNativeNetworkEvent('CEventShockingCarCrash', (entities, eventEntity, data) => {
    // logic
})

setWebviewFocus

Sets or removes focus and cursor from own webview

rpc.setWebviewFocus(true /* focus */, true /* show cursor */)

Webview (source)

onClient

Listens to client event

rpc.onClient('clientWebviewEvent', (arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offClient

Stops listening to client event

rpc.offClient('clientWebviewEvent')

emitClient

Sends event to own client

const response = await rpc.emitClient('webviewClientEvent', someData)
// response will come from client listener with returned data

onServer

Listens to server event

rpc.onServer('serverWebviewEvent', (arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offServer

Stops listening to server event

rpc.offServer('serverWebviewEvent')

emitServer

Sends event to server

const response = await rpc.emitServer('webviewServerEvent', someData)
// response will come from server listener with returned data

onSelf

Listens to webview event

rpc.onSelf('webviewEvent', (arg1, arg2, ...rest) => {
    // logic
    return someData // this will be forwarded back to caller
})

offSelf

Stops listening to webview event

rpc.offSelf('webviewEvent')

emitSelf

Sends event to webview

const response = await rpc.emitSelf('webviewEvent', someData)
// response will come from webview listener with returned data

License

Licensed under the Custom Attribution-NoDerivs Software License