Introduction
The Magellan project provides compiler tooling and a runtime API for remote execution of service functions written in TypeScript.
In many applications, backend developers have to create REST APIs, e.g., using swagger.io and frontend developers implement client code in their components to present domain logic and data in the browser. During development, frontend and backend developers usually have numerous discussions about this API, followed by changes and extensions on both sides. Magellan simplifies this process by providing a compiler that generates all involved code for both sides. It abstracts away the transport layer between the browser and the server and automatically serializes input and output values.
Magellan is a TypeScript library that provides a compiler and runtime API for service functions with the following features:
- Transparent support to write services that consume node modules in the frontend
- npm package generation of TypeScript server code for remote execution through node
- (Almost) invisible transport layer between browser and server.
- Effortless configuration of service endpoints
- Automatic serialization of input/output values
- Transparent error messages and exception handling
Prerequisites
Before you can use Magellan, you need to install the dependencies. You'll need a working Node environment with Node 18+, npm and npx (or yarn 1.22.0+)
npm -v # v8
npx -v # v8
node -v # v18
Getting Started
In this interactive tutorial, we'll implement a small frontend application with a service that returns a greeting and a React component that displays it in the browser. This service will automatically be extracted from the frontend code and be executed on the server.
Create a new @quatico/magellan-starter project
- Run one of the following commands to create a new project.
With pnpm
:
pnpm create @quatico/magellan-starter
With npm
:
npm init @quatico/magellan-starter
- Follow the instructions in the terminal to complete the setup.
cd <name-of-your-project>
# install dependencies
pnpm install
# start the project
pnpm start
The react app at http://localhost:3030 presents us with greetings by the service running on the server. You can see this from the processor architecture in the greeting message (see screenshot below).
You can also verify this by looking at the network tab in the browser's developer tools where you'll see the calls made to the server for executing the greetMe
function:
How does it work?
Have a look at the src/services
folder to see the example function that returns a greeting src/services/greet-me.ts
.
The function is annotated with the @service()
decorator which marks it as a service function.
Magellan will automatically extract this function and generate the corresponding server code.
import { type Context } from "@quatico/magellan-shared";
import { type Serialization } from "@quatico/magellan-shared";
// @service()
export const greetMe = async (name: string, context?: Context, serialization?: Serialization): Promise<string> => {
// This code must be executed on the server. In the browser, accessing process.arch causes an error.
return `Hello ${name}! I'm Magellan running on "${
typeof window === "undefined" ? `${process.arch}" server` : "browser"
}!`;
};
To verify, that the function can also be run in the browser, we can run the react app without Magellan.
pnpm run start:frontend
The react app at http://localhost:3030 presents us with greetings by the service running in the browser
Where does the server run?
In this example, the server runs on a different port (3001) than the frontend.
To tell the Magellan client code where the server is running, we need to set the endpoint
in the setNamespace
call in src/App.tsx
.
Have a look at the following code snippet:
import { useEffect, useState } from 'react';
import logo from './logo.svg';
import { greetMe } from "./services/greet-me";
import { setNamespace } from "@quatico/magellan-client"
import './App.scss';
// we need to tell the Magellan client code, that the server is on a different port than the frontend
setNamespace("default", { endpoint: "http://localhost:3001/api" });
function App() {
...
}
export default App;