First Steps

JStack makes it easy to build performant, reliable Next.js applications. You can start a new project in one of two ways:

  • Using the CLI (recommended)
  • Manual Setup

Especially if you use the CLI (which does all the setup automatically), this guide will help you understand key components of your new (and hopefully favorite ๐Ÿคž) Next.js & TypeScript tech stack.


Quickstart

The quickest way to get started is with the CLI:

Terminal

npx create-jstack-app@latest

That's it! ๐ŸŽ‰ The CLI creates a brand new project following best practices and modern Next.js patterns. I recommend reading on to understand each part of JStack.


1. File Structure

Let's explore the main building blocks of JStack. I recommend the following file structure for your Next.js application:

JStack File Structure

app/
  โ””โ”€โ”€ server/
      โ”œโ”€โ”€ jstack.ts        # Initialize JStack
      โ”œโ”€โ”€ index.ts         # Main appRouter
      โ””โ”€โ”€ routers/         # Router directory
          โ”œโ”€โ”€ user-router.ts
          โ”œโ”€โ”€ post-router.ts
          โ””โ”€โ”€ ...

2. Initialize JStack

This file is the point where we initialize JStack. Optionally, you can specify your environment variable type here, which makes it type-safe across your application.

server/jstack.ts

import { jstack } from "jstack"
 
interface Env {
  Bindings: { DATABASE_URL: string }
}
 
export const j = jstack.init<Env>()
 
/**
 * Public (unauthenticated) procedures
 * This is the base part you use to create new procedures.
 */
export const publicProcedure = j.procedure

This file is where we create Procedures and Middleware.


3. The App Router ๐Ÿง 

The core of JStack is the appRouter - it's the entry point to your Next.js backend and manages all your API routes:

app\server\index.ts

import { j } from "./jstack"
 
/**
 * This is your base API.
 * Here, you can handle errors, not-found responses, cors and more.
 */
const api = j
  .router()
  .basePath("/api")
  .use(j.defaults.cors)
  .onError(j.defaults.errorHandler)
 
/**
 * This is the main router for your server.
 * All routers in /server/routers should be added here manually.
 */
const appRouter = j.mergeRouters(api, {
  // ...
})
 
export type AppRouter = typeof appRouter
 
export default appRouter

When a new HTTP request comes in to your server, the appRouter knows where to route the request.

โ†’ Read more about the appRouter


4. Routers

Routers help you group related features. For example, you might have:

  • userRouter for user related operations
  • paymentRouter for all payment related operations
  • postRouter for blog post operations

This is what a basic router looks like:

server/routers/post-router.ts

import { j, publicProcedure } from "../jstack"
 
export const postRouter = j.router({
  recent: publicProcedure.get(({ c }) => {
    return c.json({ title: "first post" })
  }),
})

In this example:

  • postRouter is the name of your router
  • recent is the name of your procedure
  • The function gets a context object (c) with helpful utilities
  • We return a mocked post using c.json()

5. Connecting a Router

Let's connect our new router to make it available for API requests:

server/index.ts

import { j } from "./jstack"
import { postRouter } from "./routers/post-router"
 
const api = j
  .router()
  .basePath("/api")
  .use(j.defaults.cors)
  .onError(j.defaults.errorHandler)
 
const appRouter = j.mergeRouters(api, {
  post: postRouter,
})
 
export type AppRouter = typeof appRouter
export default appRouter

6. API Setup

To handle all incoming requests with our appRouter, create a catch-all API route:

app/api/[[...route]]/route.ts

import appRouter from "@/server"
import { handle } from "hono/vercel"
 
export const GET = handle(appRouter.handler)
export const POST = handle(appRouter.handler)

That's it! ๐ŸŽ‰

You can now send API requests to http://localhost:3000/api/post/recent. This path is made up of three different parts:

  • base path (/api)
  • Router name (post)
  • Procedure name (recent)

โ†’ Next: Make type-safe API requests in your Next.js app