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

realight

v0.1.27

Published

Really small react framwork for [Bun](https://bun.sh) to fetch data and mutate them easily, inspired by [Remix](https://remix.run) api's

Downloads

20

Readme

Realight

Really small react framwork for Bun to fetch data and mutate them easily, inspired by Remix api's

Can only be used for toy projects and is really far from being production ready (it will never be!).

Getting started

Installation

bun i realight

You first route

Realight is based on the file system router that Bun provides which is itself based on the Nextjs Page router.

First create a views folder which will contain all the routes, then create a home.tsx file. It will create the /home url.

In each view you have to export a default react component, for example:

export default function View() {
  return <p>My first route !</p>;
}

APIs

Loading data

Realight uses the same api than remix to fetch data by asking you to write a special async function in the same file as your view:


export async function query() {
    const data = await fetch("https://jsonplaceholder.typicode.com/todos")
    const todos = await data.json() as Array<Record<string, unknown>>
    return {
        title: "Arbitrary data",
        todos,
    };
}

From which you can retrieve the content in your view using a special hook: useQueryData

import {useQueryData} from "realight"

export async function query() {
    const data = await fetch("https://jsonplaceholder.typicode.com/todos")
    const todos = await data.json() as Array<Record<string, unknown>>
    return {
        title: "Arbitrary data",
        todos,
    };
}

export default function View() {
    const {title, todos} = useQueryData<typeof query>()
    return (
        <article>
            <h1>{title}</h1>
            <ul>
                {todos?.map(todo => {
                    <li key={todo.id}>{todo.title}</li>
                })}
            </ul>
        </article>
    )
}

Mutate data

To mutate data you have to use the useForm hook that returns a Form component and a state react state.

import { useForm } from "realight";

export default function View() {
  const form = useForm(); // or const {Form, state} = useForm()
  return (
    <form.Form method="POST">
      <input type="email" name="email " />
      <button type="submit">
        {form.state === "submitting"
          ? "loading"
          : "Subscribe to the newsletter !"}
      </button>
    </form.Form>
  );
}

To retrieve the form data you can export an async mutate function:

import { useForm } from "realight";
import { fakeDb } from "fake-db-service";

export default function View() {
  const form = useForm(); // or const {Form, state} = useForm()
  return (
    <form.Form method="POST">
      <input type="email" name="email " />
      <button type="submit">
        {form.state === "submitting"
          ? "loading"
          : "Subscribe to the newsletter !"}
      </button>
    </form.Form>
  );
}

export async function mutate({ req }: { req: Request }) {
  const formData = req.formData;
  const success = await fakeDb.add({
    email: formData.get("email"),
  });
  return JsonResponse({ success });
}

Retrieve the result

If you need to retrieve the result of the mutate function in your view you can use the useMutationData.

import {useForm, useMutationData} from "realight"
import {fakeDb} from "fake-db-service"

export default function View() {
    const form = useForm()
    const {success} = useMutationData<typeof mutate>()
    return (
        <>
            <form.Form method="POST">
                <input type="email" name="email " />
                <button type="submit">
                    {form.state === "submitting" ? "loading" : "Subscribe to the newsletter !"}
                </button>
            </form.Form>
            <p>{success ? "Thank you !" : null}</p>
        </>
    )
}

export async function mutate({ req }: { req: Request }) {
    const formData = req.formData
    const success = await fakeDb.add({
        email: formData.get("email")
    })
    return JsonResponse({success});
}

Revalidation

When you update some data in the mutate function, Realight automaticcaly re-run the query and show the updated data right after the mutation is done without you having to do anything.

import {useForm, useMutationData, useQueryData} from "realight"
import {fakeDb} from "fake-db-service"

export default function View() {
    const form = useForm()
    const {emails} = useQueryData<typeof query>()
    return (
        <>
            {emails?.map(email => (
                <form.Form method="POST">
                    <input type="hidden" name="id" value={email.id}>
                    <input type="email" name="email" value={email.email} />
                    <button type="submit">
                        {form.state === "submitting" ? "loading" : "Update email"}
                    </button>
                </form.Form>
            )}
        </>
    )
}

export async function query() {
    const emails = await fakeDb.get("emails")
    return {emails};
}

export async function mutate({ req }: { req: Request }) {
    const formData = req.formData
    const success = await fakeDb.update(formData.get("id"), {
        email: formData.get("email")
    })
    return JsonResponse({success});
}

It allows the view to always be in sync with the data.

If you want not to refetch anything to avoid unnecessary request, you can use the revalidate option:

export async function mutate({ req }: { req: Request }) {
    const formData = req.formData
    const success = await fakeDb.update(formData.get("id"), {
        email: formData.get("email")
    })
    return JsonResponse({success}, {revalidate: false});
}

Redirect

If you want to redirect to a new url after the mutation you can use RedirectResponse:

export async function mutate({ req }: { req: Request }) {
    const formData = req.formData
    const success = await fakeDb.update(formData.get("id"), {
        email: formData.get("email")
    })
    return RedirectResponse("/home");
}

Meta data

If you want to change the title description and favicon you can export a meta const from your view:

import type {Meta} from "realight"

export const meta: Meta = {
    title: "My blog"
    description: "The description of my blog"
    icon: "favicon.png"
}

The favicon will be searched in the public folder. If you want to customize your title and description based on the query data you can export a function instead which will have the data passed as an argument:

import type { Meta } from "realight";

export const meta: Meta<typeof query> = (data) => {
  title: data.meta.title;
  description: data.meta.description;
  icon: "favicon.png";
};

Database

If you want to store data in a database you can use the realight/db package that exposes a bun sqlite database. You can also use the native bun sqlite db but you'll have to import it dynamically in either the query or mutate function. Using the realight/db you'll be able to import it from the top.

Middlewares

If you want to execute something at each request you can create a middleware.ts file in your src folder. This file should export a default function of type MiddlewareType:

// src/middleware.ts
import { RedirectResponse, type RequestType } from "realight";

const middleware: MiddlewareType = ({request, params, searchParams}) => {
    if (Math.random() > 0.5) {
        return RedirectResponse("https://example.com")
    }
}

export default middleware

Bootstrap scripts

If you want to execute something before the server starts you can create a bootstrap.ts file in your src folder. This file should export a default function of type BootstrapType:

// src/bootstrap.ts
import createDb from "super-db"

export default () => {
    createDb()
}