Get Started with Create L3 App

Project Setup

To create a new project, simply run: npx create-l3-app@latest. This will prompt you to enter a project name, a web server port, and an API server port. The web server port is used by the Next.js server. The API server port is used by the tRPC server.

npx create-l3-app@latest

To get started, cd into the project directory and run: bun dev. You can also do npm run build and bun run start to build and start the project respectively.

NOTE: Bun is not able to build the project. This is a limitation of Bun for now.

Procedures

Procedures are the core of tRPC. They are used to define the API of your server. Procedures can be used to define queries, mutations, and subscriptions. Procedures can also be used to define context, which is used to pass data to the server.

Here is an example of a procedure definition:

const status = publicProcedure.query(async () => {
    return { status: 'active' };
});

const echoMessageInput = z.object({
    message: z.string()
});
const echoMessage = publicProcedure.input(echoMessageInput).query(async ({ input }) => {
    return { message: input.message };
});

export const procedures = { status };
Client Procedure Calls

The useAPI hook is used to make client procedure calls. It returns a trpc client, which can be used to make procedure calls. Here is an example of a client procedure call:

export default function ClientComponent() {
    const SERVER = useAPI();

    return (
        <button onClick={async () => {
            const message = prompt('Enter a message');
            const { message } = await SERVER.echoMessage.query({ message });
            alert('Server says: ' + message);
        }}
    );
}
Server Procedure Calls

Here's an example of a server procedure call:

export default async function ServerComponent() {
    const SERVER = await API();
    const { status } = await SERVER.status.query();

    return (
        <div>
            <h1>Server Status: {status}</h1>
        </div>
    );
}
Database

There is a lightweight DIY ORM for SQLite in server/database.ts. There are a few things to note:

The sql function is used to build SQL queries. Here's an example:

const users = sql`SELECT * FROM users`.all();

The .all() method is used to execute the query and return an array of objects. The .get() method is used to execute the query and return a single object.

Here's an example of how to create a table:

function buildDatabase() {
    dropTable('users');
    createTable('users', [
        new PrimaryKey('id'),
        new Column('name', 'TEXT', 'NOT NULL'),
        new Column('email', 'TEXT', 'NOT NULL'),
        new Column('createdAt', 'DATETIME', "NOT NULL DEFAULT (datetime(current_timestamp, 'localtime'))"),
        new Column('updatedAt', 'DATETIME', "NOT NULL DEFAULT (datetime(current_timestamp, 'localtime'))"),
    ]);
}

function testDatabase() {
    buildDatabase();
}

// testDatabase();

The createTable function is used to create a table. The dropTable function is used to drop a table. The buildDatabase function is used to build the database. The testDatabase function is used to test the database.

NOTE: If you haven't created the database yet, you will need to uncomment the testDatabase function in server/database.ts and run bun run server/database.ts to build the database.