create-nickjs
v0.1.24
Published
Nick.js is a lightweight web framework (glorified starter project, really) built with [Bun](https://bun.sh/), [ElysiaJS](https://elysiajs.com), [Kysely](https://github.com/kysely-org/kysely), [React](https://react.dev), and [Tailwind](https://tailwindcss.
Downloads
8
Maintainers
Readme
Nick.js
Nick.js is a lightweight web framework (glorified starter project, really) built with Bun, ElysiaJS, Kysely, React, and Tailwind. It is designed to be type-safe, performant, easy to use, and flexible.
It is is very beta software, mostly intended for personal use. Exercise caution if using for any serious project.
Installation
To create a new Nick.js project:
bun create nickjs
Be sure to verify that you get the latest version of Nick.js (i.e. the version
in package.json
). There is currently a Bun bug that results in bunx
using stale versions of packages.
SSR
Nick.js supports basic server-side rendering + hydration. Each incoming page request is routed to a controller (i.e. an async function), which is responsible for retrieving any data the page needs, then returning a React element. The server's initial response will include only the rendered HTML plus two script
tags - one to load React (typically already in the browser cache) and one to load javascript that responsible for hydrating the current page.
There is one piece of magic required to make SSR and hydration work: Each React element returned by a controller must passed through @ssr/app/clientRoot
. At build time, a custom Bun plugin will replace this function call with code that renders the given component to an html string and appends scripts to hydrate the page.
While not necessary, it's often convenient for controllers to use the @server/util/pageController
and @server/util/controller/decoratePageProps
helpers to add type information and pass common props (e.g. currentUser
) to the page component.
const userController = pageController<UserSchema>(async (props) => {
const user = await client
.selectFrom('User')
.selectAll()
.where('id', '=', props.params.id)
.executeTakeFirstOrThrow();
return clientRoot(UserPage, decoratePageProps(props, { user }));
});
export { userController };
function UserPage({ currentUser, user }: PageProps<{ user: User }>) {
return (
<Page title={user.name}>
<div className="flex flex-row items-center gap-2">
<Avatar url={user.avatarUrl} />
<span className="font-bold">{user.name}</span>
{user.id === currentUser?.id && (
<a href={`/users/${user.id}/edit`}>Edit</a>
)}
</div>
</Page>
);
}
export { UserPage };
Code Organization
Server code lives in src/server
, aliased as @server
.
Client code lives in src/client
, aliased as @client
.
Shared code (e.g. types, runtime-agnostic utility functions) lives in src/shared
, aliased as @shared
.
ESlint rules are configured to prevent client code from importing any Bun/Node packages or code outside of the @client
and @shared
directories. This helps to ensure that server-side code is never referenced in a client bundle.
Server
The server is configured in @server/server.tsx
. By default, Nick.js enables ElysiaJS plugins for authentication, Tailwind styles, error handling, compression, rate limiting, securing http response headers, serving static files/folders, and adding request ids. It's recommended to use ElysiaJS schemas to type and validate request inputs (e.g. path params, search params, body). Follow the patterns in @shared/type/controller
to define schema validations for ElysiaJS and derive types that can be passed into ControllerProps
.
Database
Nick.js uses Kysely to interact with a PlanetScale-hosted MySQL database. Migrations can be found in @server/database/migration
. By default, projects come with User
, Oauth
, and Session
tables for basic authentication. New migrations can be run with bun run migrate:to-latest
, which will have the effect of auto-generating + saving database types to @shared/type/db/generated.ts
. When executing queries, use the client
instance from @server/database/client
.