npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

grubba-rpc

v0.12.8

Published

RPC e2e safe

Downloads

184

Readme

RPC

What is this package?

Inspired on zodern:relay

This package provides functions for building E2E type-safe RPCs. The functions are:

  • crateMethod
  • createPublication
  • createModule
  • createClient

How to download it?

meteor npm i grubba-rpc @tanstack/react-query zod

How to use it?

import {
  ReturnMethod, // <- Type
  ReturnSubscription, // <- Type
  Config, // <- Type
  SubscriptionCallbacks, // <- Type
  createMethod, // <- function
  createPublication // <- function
  createModule, // <- function
  createClient, // <- function
} from 'grubba-rpc';

createMethod


const test1 = createMethod('name', z.any(), () => 'str');
const result = await test1();
//    ˆ? is string and their value is 'str'

For semantics uses you can as well use the methods below with the same output as createMethod:

const joinStr = createMutation(
  'join',
  z.object({ foo: z.string(), bar: z.string() }),
  ({ foo, bar }) => foo + bar);
const result = await joinStr({ foo: 'foo', bar: 'bar' });
//    ˆ? is string and their value is 'foobar'
const query = createQuery(
  'query',
  z.object({ _id: z.string() }),
  async ({ _id }) => {
    const someData = await DB.findOne(_id);
    const otherData = await DB.find({ _id: { $ne: someData._id } }).fetchAsync();
    return { someData, otherData };
  });
const result = await query({ _id: 'id' });
//    ˆ? is string and their value is the item you was querying

example of use

createMethod accepts 4 arguments:

  • name: string
  • schema: ZodSchema (validator)
  • handler (optional): function that receives the arguments of the method and returns the result
  • config (optional): object with the following properties:
type Config<S, T> = {
  rateLimit?: {
    interval: number,
    limit: number
  },
  hooks?: {
    onBeforeResolve?: Array<(raw: unknown, parsed: S,) => void>;
    onAfterResolve?: Array<(raw: Maybe<T>, parsed: S, result: T) => void>;
    onErrorResolve?: Array<(err: Meteor.Error | Error | unknown, raw: Maybe<T>, parsed: S) => void>;
  }
}

createPublication

  const publication = createPublication('findRooms', z.object({ level: z.number() }), ({ level }) => Rooms.find({ level: level }));
const result = publication({ level: 1 }, (rooms) => console.log(rooms));
//                                            ˆ? subscription

example of use

createPublication accepts 4 arguments:

  • name: string
  • schema: ZodSchema (validator)
  • handler (optional): function that is being published
  • config (optional): object with the following properties:

note that subscription returns the subscription handler the same way as Meteor.publish

type Config<S, T> = {
  rateLimit?: {
    interval: number,
    limit: number
  },
  hooks?: {
    onBeforeResolve?: Array<(raw: unknown, parsed: S,) => void>;
    onAfterResolve?: Array<(raw: Maybe<T>, parsed: S, result: T) => void>;
    onErrorResolve?: Array<(err: Meteor.Error | Error | unknown, raw: Maybe<T>, parsed: S) => void>;
  }
}

Advanced usage

you can take advantage of the hooks to add custom logic to your methods and publications


const fn = createMethod('name', z.any(), () => 'str', {
  hooks: {
    onBeforeResolve: [
      (raw, parsed) => {
        console.log('before resolve', raw, parsed);
      }
    ],
    onAfterResolve: [
      (raw, parsed, result) => {
        console.log('after resolve', raw, parsed, result);
      }
    ],
    onErrorResolve: [
      (err, raw, parsed) => {
        console.log('error resolve', err, raw, parsed);
      }
    ]
  }
});
// valid ways as well
fn.addErrorResolveHook((err, raw, parsed) => {
  console.log('error resolve', err, raw, parsed);
});
fn.addBeforeResolveHook((raw, parsed) => {
  console.log('before resolve', raw, parsed);
});
fn.addAfterResolveHook((raw, parsed, result) => {
  console.log('after resolve', raw, parsed, result);
});
const result = await fn();

Using safe methods

check this example that illustrates this 'secure way' of using safe methods, as it is not bundled in the client


import { createMethod } from 'grubba-rpc'
import { z } from "zod";

const DescriptionValidator = z.object({ description: z.string() });

// tasks.mutations.ts
// it expects the return type to be a void
export const insert = createMethod('task.insert', DescriptionValidator).expect<void>();

// tasks.mutations.js
// If you are using javascript, you can use the following syntax
export const insert = createMethod('task.insert', DescriptionValidator).expect(z.void());
// or you can use other name such as:
export const insert = createMethod('task.insert', DescriptionValidator).returns(z.void());

// ---------

// tasks.methods.ts
import { insert } from './tasks.mutations.ts'

insertTask = ({ description }) => {
  TasksCollection.insert({
    description,
    userId: Meteor.userId(),
    createdAt: new Date(),
  });
};

insert.setResolver(insertTask);

// ---------


// client.ts
import { insert } from './tasks.mutations.ts'

insert({ description: 'test' });
//^? it return void and it will run
// if resolver is not set it will throw an error

createModule

const Tasks = createModule('tasks', {insert, remove, setChecked}).build();
const foo = createModule('foo')
  .addMethod('bar', z.string(), () => 'bar' as const)
  .addMethod('baz', z.string(), () => 'baz')
  .addQuery('get', z.string(), () => 'get')
  .addSubmodule('task', Tasks)
  .build();
const k = await foo.bar();
//   ?^ 'bar'

Examples?

in the examples folder you can find a simple example of how to use this package it uses simpletasks as a base

for downloading it you can do the command below or just access this link

git clone https://github.com/Grubba27/meteor-rpc-template.git

React focused API

For now, only works for methods


const test1 = createMethod('name', z.any(), () => 'str');
// in react context / component.tsx


const { data } = test1.useQuery();
// works like useSuspenseQuery from https://tanstack.com/query/latest/docs/react/reference/useSuspenseQuery


// or you can use for mutation
const { mutate } = test1.useMutation();
// uses the https://tanstack.com/query/v4/docs/react/reference/useMutation under the hood

method.useQuery

This uses the same api as useSuspenseQuery

method.useMutation

This uses the same api as useMutation

Using in the client

When using in the client you should use the createModule and build methods to create a module that will be used in the client and be sure that you are exporting the type of the module

You should only create one client in your application

You can have something like api.ts that will export the client and the type of the client

// server.ts

const otherModule = createModule()
  .addMethod('bar', z.string(), () => 'bar')
  .build();
const server = createModule()
  .addMethod('foo', z.string(), () => 'foo')
  .addMethod('bar', z.string(), () => 'bar')
  .addSubmodule('other', otherModule)
  .build();

export type Server = typeof server;


// client.ts


const app = createClient<Server>();

app.foo("str") // <--- This is type safe
app.other.bar("str") // <--- This is type safe