Blitz.js - Framework to build web apps fast

🍱Framework
javascript
react

Idea

Ruby on Rails for React Full-Stack Development

Convention over configuration

  • By default code is organized code by domain context

Can blitz be easily

Authentication

Example of admin/user separation:

import { Ctx, NotFoundError } from "blitz"
import db, { Prisma } from "db"

type GetProcessInput = Pick<Prisma.FindFirstProcessArgs, "where" | "include">

export default async function getProcess({ where, include }: GetProcessInput, ctx: Ctx) {
  ctx.session.authorize()

  const isAdmin = ctx.session.roles.includes("admin")

  const process = await db.process.findFirst({
    where: {
      ...where,
      userId: isAdmin ? undefined : ctx.session.userId,
    },
    include,
  })

  if (!process) throw new NotFoundError()

  return process
}

It prevents unauthorized access from other users: If you simply go to /processes/1, even you’re not the owner of the process, you would still be able to access it. When we check for the userId, we wouldn’t receive a process if it doesn’t match. As admin we should still have access to the process, so setting the userId to undefined let’s Prisma ignore this property when constructing the SQL-query.

Error handling

Deal with AuthenticationError:

if (error instanceof AuthenticationError) {
  return { authError: "Sorry, those credentials are invalid" }
}

Server-side Rendering

Server-side Rendering is also referred to as “SSR” or “Dynamic Rendering”.

Why?: Dynamic update of content…

Server-side rendering in blitz.js

  • getServerSideProps function

    To use Server-side Rendering for a page, you need to export an async function called getServerSideProps. This function will be called by the server on every request.

  • Serialization: Next.js forces values have to serialized.
  • Have to use Promises since I can’t use Blitz’s useQuery hook.
const serializeValue = <T extends { createdAt: Date; updatedAt: Date }>(valueToSerialize: T) => ({
  ...valueToSerialize,
  updatedAt: valueToSerialize.updatedAt.toISOString(),
  createdAt: valueToSerialize.createdAt.toISOString(),
})

export const getServerSideProps: GetServerSideProps = async ({ params, req, res }) => {
  const processId = Number(params?.processId)
  const session = await getSessionContext(req, res)

  const [automationPotentialResult, processResult] = await Promise.allSettled([
    getAutomationPotential(processId, { session }),
    getProcess({ where: { id: processId }, include: { assessments: true } }, { session }),
  ])

  const process =
    processResult.status === "fulfilled"
      ? (omit(processResult.value, ["assessments"]) as Process)
      : null
  const automationPotential =
    automationPotentialResult.status === "fulfilled" ? automationPotentialResult.value : null
  const assessments: Assessment[] =
    processResult.status === "fulfilled"
      ? (processResult.value as Process & {
          assessments: Array<Assessment>
        }).assessments
      : []

  return {
    // NB: everything must be serializable, so no `Date` or `undefined`
    props: {
      process: process ? serializeValue(process) : null,
      automationPotential,
      assessments: assessments.map(serializeValue),
      user: session.publicData,
    },
  }
}

Some related types:

type SerializedEntity<T> = Omit<T, "createdAt" | "updatedAt"> & {
  createdAt: string
  updatedAt: string
}
type SerializedProcess = SerializedEntity<Process>
type SerializedAssessment = SerializedEntity<Assessment>

Notes

Discuss on TwitterImprove this article: Edit on GitHub

Discussion


Explain Programming

André Kovac builds products, creates software, teaches coding, communicates science and speaks at events.