Blitz.js

Making Fullstack Web Development a Breeze

Acme Design is a full service design agency.

Who am I

Lead Blitz.js maintainer

 

Twitter: @aleksandrasays

GitHub: @beerose

Blog: aleksandra.codes

Agenda

Why was Blitz created in the first place?

What was its goal?

1

Blitz's goal

What is the Blitz framework?

What are the core concepts?

2

Blitz Framework

What are we working on currently?

How does the Blitz's future look like?

3

Blitz 2.0

The Goals

 

We're making full-stack web development a breeze.

 

Blitz aims to make you as productive as possible.

 

We want to give you the best DX in the world.

 

 

What is Blitz?

How do we achieve these goals?

1

Blitz framework was created by Brandon and the community

3

We decided to fork Next.js, weekly downloads stagnated

5

We released an alpha version of the Blitz 2.0

2

Blitz reaching 11k GitHub stars. People loved it

4

We posted an RFC about Blitz pivot, it was very well received

Feb, 2020

2020 2021

Mar, 2021

Dec, 2021

May, 2021

Blitz

The Fullstack JavaScript Framework

Next.js

Recipes

Zero-API Layer

React-query

Code scaffolding

AuthN & AuthZ

Prisma

Zod utilites

Misc utilities

Blitz 2.0

The Fullstack JavaScript Toolkit

Blitz Toolkit

@blitzjs/next

@blitzjs/auth

@blitzjs/rpc

blitz core

blitz core

Blitz Toolkit

@blitzjs/next

@blitzjs/auth

@blitzjs/rpc

  • CLI
  • Env vars loading
  • Plugins
  • Middlewares
  • Code scaffolding
  • Recipes
  • Utilities

blitz core

Blitz Toolkit

@blitzjs/next

@blitzjs/auth

@blitzjs/rpc

  • gSP and gSSP functions
  • api wrapper for API Routes
  • Client & server plugins setup for Nextjs apps

blitz core

Blitz Toolkit

@blitzjs/next

@blitzjs/auth

@blitzjs/rpc

  • Authentication & Authorization
  • Passport adapter for 3rd party auth
  • Configurable session storage

blitz core

Blitz Toolkit

@blitzjs/next

@blitzjs/auth

@blitzjs/rpc

  • Zero-API Data Layer
  • React-query-like functions
  • Configurable QueryClient

blitz core

Getting started

npm i -g blitz@alpha

blitz new my-new-blitz-project
// app/blitz-server.ts
import { setupBlitzServer } from "@blitzjs/next"
import { AuthServerPlugin, PrismaStorage } from "@blitzjs/auth"
import { db } from "db"
import { simpleRolesIsAuthorized } from "@blitzjs/auth"

const { gSSP, gSP, api } = setupBlitzServer({
  plugins: [
    AuthServerPlugin({
      cookiePrefix: "blitz-app",
      storage: PrismaStorage(db),
      isAuthorized: simpleRolesIsAuthorized,
    }),
  ],
})

export { gSSP, gSP, api }

Server setup

// app/blitz-client.ts
import { AuthClientPlugin } from "@blitzjs/auth"
import { setupBlitzClient } from "@blitzjs/next"
import { BlitzRpcPlugin } from "@blitzjs/rpc"

export const { withBlitz } = setupBlitzClient({
  plugins: [
    AuthClientPlugin({
      cookiePrefix: "blitz-app",
    }),
    BlitzRpcPlugin({
      reactQueryOptions: {
        queries: {
          staleTime: 7000,
        },
      },
    }),
  ],
})

Client setup

Zero-API Layer

Zero-API Layer

// app/products/queries/getProduct.ts
import db from "db"
import * as z from "zod"

const GetProject = z.object({
  id: z.number(),
})

export default async function getProject(
  input: z.infer<typeof GetProject>,
) {
  const data = GetProject.parse(input)

  const project = await db.project.findOne({ where: { id: data.id } })

  return project
}

Zero-API Layer

import { Suspense } from "react"
import { useQuery, useRouter, useParam } from "blitz"
import getProject from "app/projects/queries/getProject"

function Project() {
  const router = useRouter()
  const projectId = useParam("projectId", "number")
  const [project] = useQuery(getProject, { where: { id: projectId } })

  return <div>{project.name}</div>
}

function ProjectPage() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Project />
    </Suspense>
  )
}

export default ProjectPage

Zero-API Layer

// app/products/mutations/createProduct.tsx
import db from "db"
import * as z from "zod"

const CreateProject = z
  .object({
    name: z.string(),
  })

export default async function createProject(
  input: z.infer<typeof CreateProject>,
) {
  const data = CreateProject.parse(input)

  const project = await db.project.create({ data })

  return project
}

Zero-API Layer

import {useMutation} from 'blitz'
import updateProject from 'app/projects/mutations/updateProject'

function (props) {
  const [updateProjectMutation] = useMutation(updateProject)

  return (
    <Formik
      onSubmit={async values => {
        try {
          const project = await updateProjectMutation(values)
        } catch (error) {
          alert('Error saving project')
        }
      }}>
      {/* ... */}
    </Formik>
  )
}
// app/auth/mutations/signup.ts
import { resolver, SecurePassword } from "blitz"
import db from "db"
import { Signup } from "app/auth/validations"
import { Role } from "types"

export default resolver.pipe(resolver.zod(Signup), async ({ email, password }, ctx) => {
  const hashedPassword = await SecurePassword.hash(password.trim())
  const user = await db.user.create({
    data: { email: email.toLowerCase().trim(), hashedPassword, role: "USER" },
    select: { id: true, name: true, email: true, role: true },
  })

  await ctx.session.$create({ userId: user.id, role: user.role as Role })
  return user
})

AuthZ & AuthN

// app/products/queries/getProduct.tsx
import {Ctx} from "blitz"
import db from "db"
import * as z from "zod"

const GetProject = z.object({
  id: z.number(),
})

export default async function getProject(
  input: z.infer<typeof GetProject>,
  ctx: Ctx
) {
  const data = GetProject.parse(input)
  
  ctx.session.$authorize(),

  const project = await db.project.findOne({ where: { id: data.id } })

  return project
}

AuthZ & AuthN

import { Routes } from "@blitzjs/next"

const Page: BlitzPage = () => {
  return <div>{/* ... */}</div>
}


Page.redirectAuthenticatedTo = "/"

Page.redirectAuthenticatedTo = Routes.Home()

Page.redirectAuthenticatedTo = ({ session }) =>
  session.role === "admin" ? "/admin" : Routes.Home()
  
Page.authenticate = true

Page.authenticate = {redirectTo: '/login'}

Page.authenticate = {redirectTo: Routes.Login()}

AuthZ & AuthN

Current

Toolkit

Zero-API Data Layer                                            ✅                                     

Authentication, authorization                           ✅                                     

New app templates                                             ✅                                     

Code scaffolding                                                  ✅                                     

Recipes                                                                  ✅                                     

Websockets                                                                                                   🚀

Cron, scheduled job, job queues                                                               🚀 

Mailers/Email integration                                                                            🚀

File uploads                                                                                                   🚀

Billing integration                                                                                         🚀

GraphQL data layer                                                                                     🚀

 

Takeaways

➡️ https://canary.blitzjs.com/

➡️ https://github.com/blitz-js/blitz

We'd love your feedback!

Thank You!

Palette

By Aleksandra Sikora

Private