Skip to content

πŸ“‘ Simple and type safe json-rpc 2.0 server and client solution for node.js.

License

Notifications You must be signed in to change notification settings

Groupguanfang/lumirpc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

39cb9f3 Β· Feb 17, 2025

History

33 Commits
Feb 17, 2025
Feb 17, 2025
Feb 16, 2025
Feb 17, 2025
Feb 17, 2025
Feb 16, 2025
Feb 16, 2025
Feb 16, 2025
Feb 17, 2025
Feb 17, 2025
Feb 16, 2025
Feb 17, 2025
Feb 17, 2025
Feb 16, 2025
Feb 16, 2025
Feb 17, 2025

Repository files navigation

lumirpc

Simple and type safe json-rpc 2.0 server and client solution for node.js.

"Lumi" in french means "light", so it is a light weight rpc solution and can be easily used in any project.

Maybe the best style for server and client communication using TS ✨

At first, thanks for cell.js for the inspiration. 😁

Just a vite plugin:

// vite.config.ts
import LumiRpc from 'lumirpc/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    LumiRpc({
      entry: './server/main.ts',
    })
  ]
})

And then, create a main.ts file in the server folder, it is the entry file for the server:

// server/main.ts
import type { RpcApp } from 'lumirpc/server'
import process from 'node:process'
import { createRpcServer, InternalAdapter } from 'lumirpc/server'

import.meta.glob('./controllers/**/*.controller.ts', { eager: true })

export default async function main(): Promise<RpcApp> {
  const server = await createRpcServer(InternalAdapter.NodeHttp)
  if (import.meta.env.PROD)
    await server.listen(process.env.PORT ? Number.parseInt(process.env.PORT) : 3444)
  return server
}

if (import.meta.env.PROD)
  main()

Create a common folder inside the server folder, and create a welcome.protocol.ts file inside:

// server/common/welcome.protocol.ts

// Create a unique controller id, and export it
export const WelcomeController = 'WelcomeController'

// Define the controller interface, it will be used in the client and server
// You can define the same name with the controller id, this can be used as both a value and a type, and it is only imported once!
export interface WelcomeController {
  getWelcomeMessage(): Promise<string>
  getWelcomeMessageWithName(name: string): Promise<string>
}

Create a controllers folder inside the server folder, and create a welcome.controller.ts file inside:

// server/controllers/welcome.controller.ts
import { defineController } from 'lumirpc/server'
// This just import once! Very cool!
import { WelcomeController } from '../common/welcome.protocol'

// Define the controller with the controller id and the controller interface
export default defineController<WelcomeController>(WelcomeController, () => {
  return {
    async getWelcomeMessage() {
      return 'Hello World'
    },
    async getWelcomeMessageWithName(name: string) {
      return `Hello ${name}`
    },
  }
})

In your frontend, you can use the controller with your favorite http client, internally I make a axios helper to make it easier to use the controller for full type safety:

// apis/welcome.ts
import axios from 'axios'
import { createAxiosRpcClient } from 'lumirpc/axios'
// Same with the server side, it only import once 🍺
import { WelcomeController } from '../common/welcome.protocol'

const axiosInstance = axios.create({
  baseURL: import.meta.url ? '/api' : '/',
  method: 'POST',
})
const client = createAxiosRpcClient(axiosInstance)
// Use the controller with the controller id, it will be auto inferred type from the controller!
const welcomeController = client.request<WelcomeController>(WelcomeController)

// It return a promise and axios response object with full type safety, will be inferred from the controller!
welcomeController.getWelcomeMessage()

The best DX in frontend ✍️

Many of time when we using frontend frameworks like vue, you can create a utils folder and create a rpc.ts file inside, and then write the base code:

// utils/rpc.ts
import { createAxiosRpcClient } from 'lumirpc/axios'
import { defineStore } from 'pinia'

// Using pinia to make this hook as singleton
export const useLumiRpc = defineStore('lumirpc', () => {
  const axiosInstance = axios.create({
    baseURL: import.meta.url ? '/api' : '/',
    method: 'POST',
  })

  return createAxiosRpcClient(axiosInstance)
})

Create a apis folder and create a welcome.ts file inside:

// apis/welcome.ts
import { useLumiRpc } from '@/utils/rpc'

export function useWelcomeController() {
  const rpc = useLumiRpc()

  // Use the controller with the controller id, it will be auto inferred type from the controller!
  return rpc.request<WelcomeController>(WelcomeController)
}

You can inport this useWelcomeController hook in your component and use it directly, also have full type safety!

// components/Welcome.vue
<script setup lang="ts">
import { useWelcomeController } from '@/apis/welcome'

const welcomeController = useWelcomeController()

// Write your logic here... πŸͺ„
const { data } = await welcomeController.getWelcomeMessage()
</script>

Author

Naily Zero

License

MIT

About

πŸ“‘ Simple and type safe json-rpc 2.0 server and client solution for node.js.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages