AccueilClientsExpertisesBlogOpen SourceJobsContact

2 septembre 2020

Découverte de Blitz JS

6 minutes de lecture

Découverte de Blitz JS

BlitzJS est un framework React basé sur Next.js qui se veut totalement fullstack contrairement à Next qui est plus orienté front-end. Blitz ajoute notamment des fonctionnalités de connexion à une base de données, l'ajout de middlewares, mais aussi un système d'authentification. Blitz s'est inspiré de Ruby on Rails (framework Ruby pour créer des applications web fullstack), on retrouvera notamment certains aspects au niveau de la structure du projet.

Nous allons créer une petite application web à l'aide de Blitz où un utilisateur peut s'authentifier, puis accéder à une liste d'articles de blog qu'il pourra créer, éditer et supprimer.

Initialisons notre projet :

npx blitz new myapp
cd myapp && yarn start

Durant la phase de création, il vous sera demandé quelle librairie de formulaires vous souhaitez utiliser, nous allons utiliser react-hook-form, mais libre à vous de choisir celle avec laquelle vous êtes le plus à l'aise.

Avant de nous jeter sur le code, jetons un oeil sur la structure de notre projet :

myapp
├── app
│   ├── auth
│   │   └── components
│   │   |   ├── LoginForm.tsx
│   │   ├── mutations
│   │   │   ├── login.ts
│   │   │   ├── logout.ts
│   │   │   └── signup.ts
│   │   ├── pages
│   │   │   ├── login.tsx # http://localhost:3000/login
│   │   │   └── signup.tsx # http://localhost:3000/signup
│   │   ├── auth-utils.ts
│   │   └── validations.ts
│   ├── components
│   │   ├── Form.tsx
│   │   └── LabeledTextField.tsx
│   ├── hooks
│   │   └── useCurrentUser.ts
│   ├── layouts
│   │   └── Layout.tsx
│   ├── pages
│   │   ├── _app.tsx
│   │   ├── _document.tsx
│   │   ├── 404.tsx
│   │   └── index.tsx
│   └── users
│       └── queries
│           └── getCurrentUser.ts
├── db
│   ├── migrations
│   ├── db.sqlite
│   ├── index.ts
│   └── schema.prisma
├── integrations
├── node_modules
├── public
│   ├── favicon.ico
│   └── logo.png
├── utils
├── .eslintrc.js
├── .env
├── .prettierignore
├── babel.config.js
├── blitz.config.js
├── jest.config.js
├── package.json
├── README.md
├── tsconfig.json
└── yarn.lock

Comme on peut le voir, un des premiers avantages est que notre application est générée en TypeScript. En se rendant dans les composants app/components/Form.tsx et app/components/LabeledTextField.tsx, on se rend compte qu'ils ont été générés par rapport à la librairie de formulaire que nous avons choisie. Ce composant peut être modifié selon la librairie UI que vous souhaitez utiliser, ou selon votre CSS.

Nous avons aussi toute notre partie authentification qui a été générée, cela inclut nos routes API pour la connexion, l'inscription et la déconnexion. Le formulaire dans app/auth/components/LoginForm.tsx a été généré selon les différents champs de notre modèle User.

Base de données

Blitz utilise par défaut Prisma 2 pour gérer la connexion à notre base de données, mais il est toutefois possible de se passer de Prisma au profit d'autres librairies, comme par exemple TypeORM. Un exemple d'utilisation de Blitz sans Prisma est disponible sur le GitHub.

Ouvrons notre fichier db/schema.prisma. On peut constater que Blitz a généré automatiquement 2 modèles: User et Session. Nous allons maintenant devoir ajouter un modèle Articles qui correspondra à nos articles de blog. Pour cela, le CLI de Blitz fournit une commande permettant à la fois de générer ce modèle, mais aussi de générer nos pages et routes API liées à ce modèle :

yarn blitz generate all article title content belongsTo:user

En se rendant dans notre fichier db/schema.prisma, on peut voir notre modèle Article. Apportons-y des modifications en renommant nos champs user et userId respectivement en author et authorId. Voici ce à quoi devraient ressembler les modèles après ces modifications :

model User {
  id             Int       @default(autoincrement()) @id
  createdAt      DateTime  @default(now())
  updatedAt      DateTime  @updatedAt
  name           String?
  email          String    @unique
  hashedPassword String?
  role           String    @default("user")
  sessions       Session[]
  articles       Article[]
}

model Session {
  id                 Int       @default(autoincrement()) @id
  createdAt          DateTime  @default(now())
  updatedAt          DateTime  @updatedAt
  expiresAt          DateTime?
  handle             String    @unique
  user               User?     @relation(fields: [userId], references: [id])
  userId             Int?
  hashedSessionToken String?
  antiCSRFToken      String?
  publicData         String?
  privateData        String?
}

model Article {
  id        Int      @default(autoincrement()) @id
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  title     String
  content   String
  author    User   @relation(fields: [authorId], references: [id])
  authorId  Int
}

Enfin lançons la commande suivante pour lier ces modifications à notre base de données via une migration :

yarn blitz db migrate

Les commandes de génération de modèles et de migrations ne fonctionnent que si vous utilisez Prisma. À l'heure actuelle, le CLI ne permet pas de se brancher à un autre client tel que TypeORM.

Un nom de migration vous sera demandé, nous allons la nommer AddArticle. À l'issue de cette migration, Prisma aura généré des types TypeScript liés à vos modèles. Néanmoins ces types seront incomplets, par exemple le champ author de notre modèle Article ne sera pas présent dans le type associé. Je vous déconseille fortement d'accéder au fichier de type de Prisma, car cela cassera l'autocompletion sur VSCode, et vous devrez alors totalement désinstaller puis réinstaller les node_modules, suivi d'un yarn blitz db migrate.

En plus du modèle Prisma, notre commande de migration a généré tout un dossier au sein de app.

app
└── articles
    ├── components
    │   └── ArticleForm.tsx
    ├── mutations
    │   ├── createArticle.ts
    │   ├── deleteArticle.ts
    │   └── updateArticle.ts
    ├── pages
    │   ├── [articleId]
    │   │   └── edit.tsx # http://localhost:3000/articles/:id/edit
    │   ├── [articleId].tsx # http://localhost:3000/articles/:id
    │   ├── index.tsx # http://localhost:3000/articles
    │   └── new.tsx # http://localhost:3000/articles/new
    └── queries
        ├── getArticle.ts
        └── getArticles.ts

À la manière de Ruby on Rails, on a ici un CLI qui nous génère tout le nécessaire pour pouvoir effectuer des actions CRUD sur une ressource donnée, le tout en y ajoutant les vues nécessaires.

Interaction avec la base de données

Contrairement à Next.js, Blitz nous permet d'interagir directement avec notre base de données sans avoir besoin de générer des routes API. C'est ce qu'on retrouvera au sein des fichiers situés dans les dossiers mutations et queries de nos ressources. Prenons par exemple le contenu de createArticle.ts :

import { SessionContext } from 'blitz'
import db, { ArticleCreateArgs } from 'db'

type CreateArticleInput = {
  data: Pick<ArticleCreateArgs['data'], 'content' | 'title'>
}
export default async function createArticle(
  { data }: CreateArticleInput,
  ctx: { session?: SessionContext } = {}
) {
  ctx.session!.authorize()

  const article = await db.article.create({
    data: {
      content: data.content,
      title: data.title,
      author: {
        connect: { id: ctx.session?.userId },
      },
    },
  })

  return article
}
  • Dans un premier lieu nous vérifions si la requête est authentifiée grâce à ctx.session!.authorize(). Cette session est créée par nos mutations situées dans app/auth/mutations/login.ts et app/auth/mutations.signup.ts.
  • Nous effectuons ensuite la requête vers notre base de données pour créer un article dont l'auteur correspond à l'utilisateur courant.

Cette fonction createArticle est appelée depuis notre page app/articles/pages/new.tsx :

const onSubmit = async (values: ArticleInputType) => {
  try {
    const article = await createArticle({ data: values })
    router.push('/articles/[articleId]', `/articles/${article.id}`)
  } catch (error) {
    toast({
      title: 'Error',
      description: 'Failed to create article, please retry',
      status: 'error',
      duration: 3000,
    })
  }
}

On interagit donc ici avec notre base de données directement depuis notre composant sans devoir passer par une route API comme sur Next.js.

Déploiement

Tout comme pour une application Next.js, il est possible de déployer une application Blitz sur Vercel. Il est toutefois nécessaire de posséder une base de données hébergée sur un autre serveur. Plus de détails ici.

En conclusion

Blitz.js est un framework très complet apportant son lot de fonctionnalités intéressantes manquants à Next.js. La partie authentification se veut assez complète avec notamment une gestion de rôles que nous n'avons pas abordée. Il est aussi possible d'ajouter d'autres types d'authentification (Twitter, Github, etc). L'outil de générations du CLI ainsi que le support de plusieurs librairies de formulaires populaires nous accordent un gain de temps non négligeable dans le développement. À l'heure actuelle, le framework est encore en alpha et des bugs peuvent subsister. Néanmoins, le développement est à un état assez avancé pour une mise en production. Je vous conseille en tout cas d'essayer ce framework si pour vous, avoir une application Next.js + un autre framework back-end est plus exhaustif qu'autre chose.

La documentation, très complète, se trouve ici.

Vous pourrez aussi retrouver le projet de démo ici.

18 avenue Parmentier
75011 Paris
+33 1 43 57 39 11
hello@premieroctet.com

Suivez nos aventures

GitHub
Twitter
Flux RSS

Naviguez à vue