Avec une simple API de livre, apprenez à définir un middleware dans Oak, à gérer la validation des demandes, à créer des gestionnaires de routes et à effectuer des opérations de base de base de données avec DenoKV.
Cet article vous guidera dans la création d’une API REST à l’aide de Deno, du framework Oak et d’une base de données DenoKV. Nous allons construire une API de livre simple qui montre les différentes façons de définir un middleware dans Oak, de gérer la validation des demandes, de créer des gestionnaires de routes et d’effectuer des opérations de base de base de données avec DenoKV.
Qu’est-ce que Deno ?
Deno résout de nombreux problèmes auxquels les développeurs sont confrontés avec Node. Son approche simple aide les développeurs à écrire du code JavaScript plus sécurisé, efficace et moderne. L’un de ses principaux arguments de vente est la sécurité. Par défaut, Deno n’autorise pas l’accès aux variables du système de fichiers, du réseau ou de l’environnement, sauf autorisation explicite du développeur.
Deno offre également aux développeurs une prise en charge native de TypeScript sans avoir besoin de configuration supplémentaire.
Qu’est-ce que le chêne ?
Oak est un framework middleware pour Deno qui aide les développeurs à créer des applications Web et des API. Il fournit des outils pour gérer les requêtes HTTP, gérer le routage et intégrer un middleware, similaire à Express dans Node.js. Il est livré avec TypeScript dès la sortie de la boîte et bénéficie de la sécurité et de l’environnement d’exécution moderne de Deno.
Qu’est-ce que DenoKV ?
DenoKV est une base de données clé-valeur qui gère les données structurées pour Deno.
Chaque élément de données ou « valeur » possède un identifiant unique ou « clé », ce qui facilite la récupération des données en référençant sa clé. Cette approche permet aux développeurs de gérer les données sans configurer un serveur de base de données distinct.
Configuration du projet
Exécutez la commande suivante pour installer deno pour macOS à l’aide de Shell :
curl -fsSL https://deno.land/install.sh | sh
Pour Windows utilisant PowerShell :
irm https://deno.land/install.ps1 | iex
Pour Linux utilisant Shell :
curl -fsSL https://deno.land/install.sh | sh
Pour tester votre installation, exécutez la commande suivante :
deno -version
Créer un nouveau projet
Ensuite, nous devons créer un nouveau projet. Exécutez la commande deno init deno-oak-demo
pour créer un nouveau projet appelé deno-oak-demo
puis cd-y.
Ensuite, nous devons créer trois nouveaux fichiers dans le deno-oak-demo
répertoire appelé book.routes.ts
, book.types.ts
et validation.ts
.
Ton deno-oak-demo
Le répertoire devrait maintenant ressembler à ceci.
Installer l’extension de code Deno VS
Nous devons installer l’extension VS Code officielle de Deno. Cette extension ajoute la prise en charge de Deno, par exemple en proposant des suggestions d’importation et en mettant automatiquement en cache les modules distants.
Installer du chêne
Nous utiliserons JSR pour installer Oak. JSR, ou JavaScript Repository, est un registre de packages conçu par les créateurs de Deno. Il permet aux développeurs de publier leur code TypeScript directement sans avoir besoin de le transpiler au préalable. Son principal avantage est qu’il prend en charge uniquement les modules ES et qu’il est d’abord TypeScript.
Nous utiliserons la commande deno add jsr:**@oak**/oak
pour installer le framework Oak en tant que dépendance. Si cela réussit, un deno.lock
le fichier sera créé.
Le
deno.lock
Le fichier aide à empêcher les modifications inattendues des dépendances pendant la vie de votre application en verrouillant des versions de dépendances spécifiques.
Ton deno.json
le fichier devrait maintenant ressembler à ceci.
Lorsque nous installons un package dans Deno, il est ajouté au deno.json
fichier en tant qu’importation. Si nous voulons importer ce package dans un fichier, nous pouvons utiliser l’alias défini dans le deno.json
ou référencez directement l’URL du package.
Enregistrement du middleware dans Oak
Les fonctions middleware sont des couches de traitement qui gèrent les demandes et les réponses dans notre application.
Une application Oak est une chaîne de diverses fonctions middleware, telles que des gestionnaires de routes, des fonctions de validation, des gestionnaires d’erreurs personnalisés et des enregistreurs. On peut enregistrer un middleware sur l’application dans son ensemble, un groupe de routes (un routeur) ou une route spécifique.
Voici comment enregistrer un middleware sur l’Application dans son ensemble :
import { Application, Context, Next } from "@oak/oak";
const app = new Application();
const firstMiddleware = async (context: Context, next: Next) => {
console.log("Running first Middleware");
await next();
};
app.use(firstMiddleware);
app.use((context) => {
console.log("Running second Middleware");
context.response.body = "Hello world!";
});
await app.listen({ port: 3000 });
Dans l’exemple ci-dessus, nous définissons d’abord une fonction middleware, puis nous l’enregistrons dans l’application dans son ensemble à l’aide du app.use
méthode.
Notez que la fonction middleware a deux paramètres, context
et next
. context
est un objet utilisé pour accéder au request
des données et response
méthodes, tandis que next
est une fonction utilisée pour appeler le prochain middleware de la chaîne.
Dans l’exemple ci-dessus, le premier middleware sera appelé, enregistrant ainsi « Exécution du premier middleware » sur la console. Ceci sera suivi par le deuxième middleware, qui enregistre « Exécution du deuxième middleware » sur la console et renvoie « Bonjour tout le monde ! dans le corps de la réponse.
Il est important de noter que l’ordre dans lequel le middleware est enregistré est important.
Jetez un œil à l’extrait de code ci-dessous.
import { Application, Context, Next } from "@oak/oak";
const app = new Application();
app.use((context) => {
console.log("Running second Middleware");
context.response.body = "Hello world!";
});
const firstMiddleware = async (context: Context, next: Next) => {
console.log("Running first Middleware");
await next();
};
app.use(firstMiddleware);
await app.listen({ port: 3000 });
Dans cet exemple, seul « Exécution du deuxième middleware » sera enregistré sur la console et « Bonjour tout le monde » sera envoyé comme corps de réponse. En effet, après avoir exécuté le premier middleware qui apparaît dans la chaîne, nous n’avons pas ajouté de next()
appel de fonction.
Voici comment enregistrer un middleware sur un routeur :
import { Application, Context, Next, Router } from "@oak/oak";
const app = new Application();
const router = new Router();
router.prefix("/greeting");
const firstMiddleware = async (context: Context, next: Next) => {
console.log("Running first Middleware");
await next();
};
router.use(firstMiddleware);
router.get("/one", (context) => {
context.response.body = "Hello, World!";
});
router.get("/two", (context) => {
context.response.body = "What's Up, World!";
});
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 3000 });
Dans l’exemple ci-dessus, nous définissons un nouveau routeur, définissons le préfixe de toutes ses routes, enregistrons une fonction middleware et créons deux gestionnaires de routes spécifiques.
Étant donné que notre fonction middleware est enregistrée au-dessus des deux gestionnaires de route, si « /greeting/one » ou « /greeting/two » est sélectionné, notre fonction middleware s’exécutera avant que le message d’accueil ne soit envoyé en tant que corps de réponse.
Voici comment enregistrer un middleware sur une route spécifique.
import { Application, Context, Next, Router } from "@oak/oak";
const app = new Application();
const router = new Router();
router.prefix("/greeting");
const firstMiddleware = async (context: Context, next: Next) => {
console.log("Running first Middleware");
await next();
};
router.get("/one", firstMiddleware, (context) => {
context.response.body = "Hello, World!";
});
router.get("/two", (context) => {
context.response.body = "What's Up, World!";
});
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 3000 });
Dans l’exemple ci-dessus, notre fonction middleware ne s’exécutera que lorsque « salutation/un » est activé.
Validation de la demande
Maintenant que nous savons comment fonctionne le middleware dans Oak, construisons notre API REST. Tout d’abord, nous allons créer une fonction middleware pour gérer la validation des demandes. Nous utiliserons cette fonction plus tard lors de l’ajout de gestionnaires de routes pour créer et mettre à jour des livres.
Deno prend en charge l’importation de packages npm à l’aide du npm: specifier
. Nous allons importer un package appelé Joi
du MNP.
Ajoutez le code suivant à votre validation.ts
déposer:
import { Context, Next } from "@oak/oak";
import Joi, { ObjectSchema } from "npm:joi";
export const createBookSchema = Joi.object({
title: Joi.string().required(),
author: Joi.string().required(),
description: Joi.string().required(),
});
export const updateBookSchema = Joi.object({
title: Joi.string().optional(),
author: Joi.string().optional(),
description: Joi.string().optional(),
}).or("title", "author", "description");
export const validate =
(schema: ObjectSchema) => async (context: Context, next: Next) => {
const body = await context.request.body.json();
const { error } = schema.validate(body, { abortEarly: false });
if (error) {
context.response.status = 400;
context.response.body = {
errors: error.details.map((d) => d.message),
};
} else {
await next();
}
};
Définir des types
Ensuite, dans le book.types.ts
fichier, définissons un type Book avec un id
, title
, author
et description
.
export interface Book {
id: string;
title: string;
author: string;
description: string;
}
Configurer le routeur de livres
Ensuite, importons le Oak
Routeur, Book
interface, createBookSchema
et updateBookSchema
dans le book.routes.ts
déposer:
import { Router } from "@oak/oak/router";
import type { Book } from "./book.types.ts";
import { createBookSchema, updateBookSchema, validate } from "./validation.ts";
Ensuite, initialisez le DenoKV
base de données, créez une bookRouter
et définissez son préfixe sur « /books » :
const kv = await Deno.openKv();
const bookRouter = new Router();
bookRouter.prefix("/books");
Ensuite, créez une fonction d’assistance pour obtenir un livre par son identifiant :
async function getBookById(id: string) {
const entry = await kv.get(["books", id]);
return entry.value as Book | null;
}
Dans l’extrait ci-dessus, kv.get
prend un tableau avec deux chaînes : l’une représente l’espace de noms « livres » et l’autre représente la clé du livre spécifique à récupérer.
Ensuite, définissons le gestionnaire de route pour obtenir un livre par ID :
bookRouter.get("/:id", async (context) => {
try {
const id = context.params.id;
const book = await getBookById(id);
if (book) {
context.response.body = book;
} else {
context.response.status = 404;
context.response.body = { message: "Book not found" };
}
} catch (error) {
console.log(error);
context.response.status = 500;
context.response.body = { message: "Failed to retrieve book" };
}
});
Ensuite, ajoutons un gestionnaire de route pour récupérer tous les livres :
bookRouter.get("https://www.telerik.com/", async (context) => {
try {
const entries = kv.list({ prefix: ["books"] });
const books: Book[] = [];
for await (const entry of entries) {
books.push(entry.value as Book);
}
context.response.body = books;
} catch (error) {
console.log(error);
context.response.status = 500;
context.response.body = { message: "Failed to fetch books" };
}
});
Dans l’extrait ci-dessus, kv.list
récupère toutes les paires clé-valeur qui partagent un préfixe commun (espace de noms).
Ensuite, ajoutez le gestionnaire de route pour créer un nouveau livre :
bookRouter.post("https://www.telerik.com/", validate(createBookSchema), async (context) => {
try {
const body = await context.request.body.json();
const uuid = crypto.randomUUID();
const newBook: Book = { id: uuid, ...body };
await kv.set(["books", uuid], newBook);
context.response.status = 201;
context.response.body = { message: "Book added", book: newBook };
} catch (error) {
console.log(error);
context.response.status = 500;
context.response.body = { message: "Failed to add book" };
}
});
Dans l’extrait ci-dessus, kv.set
peut être utilisé pour enregistrer une nouvelle paire clé-valeur dans la base de données. Dans ce cas, il prend deux paramètres : un tableau avec deux chaînes (l’espace de noms « books » et la clé uuid) et la valeur à sauvegarder (newBook
).
Ensuite, ajoutons le gestionnaire de route pour mettre à jour un livre par ID :
bookRouter.patch("/:id", validate(updateBookSchema), async (context) => {
try {
const id = context.params.id;
const existingBook = await getBookById(id);
if (!existingBook) {
context.response.status = 404;
context.response.body = { message: "Book not found" };
return;
}
const body = await context.request.body.json();
const updatedBook = { ...existingBook, ...body };
await kv.set(["books", id], updatedBook);
context.response.status = 200;
context.response.body = { message: "Book updated", book: updatedBook };
} catch (error) {
console.log(error);
context.response.status = 500;
context.response.body = { message: "Failed to update book" };
}
});
Dans l’extrait ci-dessus, kv.set
peut également être utilisé pour mettre à jour la valeur d’une paire clé-valeur. Dans ce cas, il faut deux arguments :
- Un tableau avec deux chaînes : l’espace de noms « books » et la clé dont la valeur sera l’ID mis à jour.
- La nouvelle valeur à mettre à jour (
updatedBook
).
Enfin, ajoutons le gestionnaire de route pour supprimer un livre par ID et exporter bookRouter
:
bookRouter.delete("/:id", async (context) => {
try {
const id = context.params.id;
const book = await getBookById(id);
if (!book) {
context.response.status = 404;
context.response.body = { message: "Book not found" };
return;
}
await kv.delete(["books", id]);
context.response.status = 200;
context.response.body = { message: "Book deleted", book };
} catch (error) {
console.log(error);
context.response.status = 500;
context.response.body = { message: "Failed to delete book" };
}
});
export { bookRouter };
Dans l’extrait ci-dessus, kv.delete
est utilisé pour supprimer une paire clé-valeur donnée.
Initialiser l’application Oak
Remplacez le code dans votre main.ts
fichier avec les éléments suivants :
import { Application } from "@oak/oak/application";
import { bookRouter } from "./book.routes.ts";
const app = new Application();
app.use(bookRouter.routes());
app.use(bookRouter.allowedMethods());
app.listen({ port: 3000 });
Enfin, nous devons apporter un petit changement à notre deno.json
fichier pour exécuter notre application. Ajoutez le --unstable-kv
et --allow-net
drapeaux à dev
tâche.
Outre votre version d’Oak et la bibliothèque assert, votre deno.json
devrait maintenant ressembler à ceci.
{
"tasks": {
"dev": "deno run --watch --unstable-kv --allow-net main.ts"
},
"imports": {
"@oak/oak": "jsr:@oak/oak@^17.1.3",
"@std/assert": "jsr:@std/assert@1"
}
}
Nous avons ajouté le --stable-kv
flag car DenoKV est toujours une API instable. Nous avons également ajouté le --allow-net
drapeau à accorder main.ts
accès à Internet.
Une fois cela en place, nous pouvons démarrer notre application en exécutant la commande deno run dev
.
Conclusion
Dans ce guide, nous avons construit une API de livre simple qui montre comment définir un middleware dans Oak, gérer la validation des demandes, créer des gestionnaires de routes et effectuer des opérations de base de base de données avec DenoKV.
Source link