TypeScript Runtime #
It’s useful to implement game code you would not want to run on the client, or trust the client to provide unchecked inputs on. You can think of this Nakama feature as similar to Lambda or Cloud Functions in other systems. A good use case is if you wanted to grant the user a reward each day that they play the game.
You will need to have these tools installed to work with TypeScript for your project:
- Node v14 (active LTS) or greater.
- Basic UNIX tools or knowledge on the Windows equivalents.
The TypeScript compiler and other dependencies will be fetched with NPM.
Initialize the project #
These steps will set up a workspace to write all your project code to be run by the game server.
Define the folder name that will be the workspace for the project. In this case we’ll use “ts-project”.
Use NPM to set up the Node dependencies in the project. Install the TypeScript compiler.
Use the TypeScript compiler installed to the project to set up the compiler options.
You’ll now have a “tsconfig.json” file which describes the available options that are run on the TypeScript compiler. When you’ve trimmed the commented out entries and updated it a minimal file will look something like:
Add this configuration option to the
Add the Nakama runtime types as a dependency to the project and configure the compiler to find the types.
Add this configuration option to the
"compilerOptions" block of the “tsconfig.json” file:
This completes the setup and your project should look similar to this layout:
Develop code #
All code must start execution from a function that the game server looks for in the global scope at startup. This function must be called
"InitModule" and is how you register RPCs, before/after hooks, and other event functions managed by the server.
The code below is a simple Hello World example which uses the
"Logger" to write a message. Name the source file “main.ts” inside the “src” folder. You can write it in your favorite editor or IDE.
We can now add the file to the compiler options and run the TypeScript compiler.
To compile the codebase:
Registering functions #
nkruntime.Initializer must be declared in the global scope rather than referenced from a variable.
There is no support for libraries that require Node, web/browser APIs, or native support (e.g. via Node).
You cannot call TypeScript functions from the Go runtime, or Go functions from the TypeScript runtime.
Global state #
Single threaded #
This allows the server to guarantee that JS modules cannot cause fatal errors - the runtime code cannot trigger unexpected client disconnects or affect the main server process.
Running the project #
With Docker #
The easiest way to run your server locally is with Docker.
To do this, create a file called
Next create a
docker-compose.yml file. For more information see the Install Nakama with Docker Compose documentation.
Now run the server with the command:
Without Docker #
Remember you need to build the
build/index.js file by running
npx tsc from the Terminal before you can execute the above command.
Confirming the server is running #
The server logs will show this output or similar which shows that the code we wrote above was loaded and executed at startup.
Bundling with Rollup #
The setup above relies solely on the TypeScript compiler. This helps to keep the toolchain and workflow simple, but limits your ability to bundle your TypeScript code with additional node modules.
Rollup is one of the options available to bundle node modules that don’t depend on the Node.js runtime to run within Nakama.
Configuring Rollup #
When configuring your TypeScript project to use Rollup there are a few additional steps and alterations you will need to make to your project if you have followed the steps above.
The first thing you will need to do is install some additional dependencies that will allow you to run Rollup to build your server runtime code. These include Babel, Rollup, several of their respective plugins/presets and
To do this, run the following command in the Terminal, which will install the dependencies and add them to your
package.json file as development dependencies:
With Rollup installed as a dev dependency of your project, you now need to modify the
build script in
package.json to run the
rollup -c command instead of the
tsc command. You should also add a
type-check script that will allow you to verify your TypeScript compiles without actually emitting a build file.
Next, you must add the following
rollup.config.js file to your project.
Followed by adding a
babel.config.json file to your project.
There are also changes to the
tsconfig.json file that must be made. Using Rollup simplifies the build process and means you no longer have to manually update the
tsconfig.json file every time you add a new
*.ts file to your project. Replace the contents of your existing
tsconfig.json file with the example below.
Next, you need to include a line at the bottom of your
main.ts file that references the
InitModule function. This is to ensure that Rollup does not omit it from the build.
You will also need to create a configuration for nakama called
Finally, you need to make a slight alteration to your
Dockerfile to ensure you copy across the
babel.config.json files. You must also change the
RUN command to run your updated build command rather than using the TypeScript compiler directly. Replace the contents of your
Dockerfile with the example below.
Building your module locally #
Ensure you have all dependencies installed:
Perform a type check to ensure your TypeScript will compile successfully:
Build your project:
Running your module with Docker #
To run Nakama with your custom server runtime code, run:
If you have made changes to your module and want to re-run it, you can run:
This will ensure the image is rebuilt with your latest changes.
Error handling #
try catch block.
Returning errors to the client #
When writing your own custom runtime code, you should ensure that any errors that occur when processing a request are passed back to the client appropriately. This means that the error returned to the client should contain a clear and informative error message and an appropriate HTTP status code.
Internally the Nakama runtime uses gRPC error codes and converts them to the appropriate HTTP status codes when returning the error to the client.
The Nakama TypeScript runtime defines the error codes in the
nkruntime.Codes enum. You can use these to define your own custom
nkruntime.Error objects. The following are some examples of errors you might define in your module.
Identifying your current version #
When looking to upgrade your Nakama server you should begin by identifying the current version you are using. You can do this either by looking at your
Dockerfile and the version tagged at the end of the image name (e.g.
heroiclabs/nakama:3.19.0) or by looking at your
package-lock.json if using the latest at the time of installation, which will give the exact commit hash) for the
nakama-runtime (also known as Nakama Common). With the latter, once you have identified your current
nakama-runtime version you can consult the compatibility matrix to identify the version of the Nakama binary you are using.
Identifying changes #
With the current Nakama version established, you should look at the Server-Runtime Release Notes to see what changes have been made since the version you are currently on. This will help you identify any breaking changes or changes which may affect the custom server runtime code you have written.
Installing the latest version #
Once you are sure which version of Nakama you want to upgrade to, you should update the version of
nakama-runtime in your project. By consulting the compatibility matrix again you can identify which version of the
nakama-runtime package you should install.
You can then install it as follows (where
<version> is a github tag such as
Upgrading the Nakama binary #
With the version of the
nakama-runtime package upgraded, you must then upgrade the version of the Nakama binary your server is using.
If you are using the binary directly, you can download the appropriate version directly from the Nakama GitHub releases page.
If you are instead using Docker, you must update your
Dockerfile by specifying the correct version in the final
Common issues #
TypeError: Object has no member
If you receive the above error message, chances are you are using a Nakama function that is not available in the version of Nakama that your server is running. This could happen if you install a later version of
nakama-runtime package in your TypeScript project than is compatible with the version of the Nakama binary you are using. Check the compatibility matrix to ensure you are using compatible versions of Nakama and Nakama Common (
Sandboxing and restrictions #
There are several key restrictions to be aware of when developing your server runtime code using TypeScript:
- Your code cannot interact with the OS in any way, including the file system
- You cannot use any module that relies on NodeJS functionality (e.g.
fs, etc.) as your code is not running in a Node environment
For specific compatibility issues present within
Goja see the Goja known incompatibilities and caveats.
Global state #
The TypeScript runtime cannot use global variables as a way to store state in memory.
Next steps #
Have a look at the Nakama project template which covers the following Nakama features: