deco-api v1.4.1
deco-api
Développement
Le package deco-api est destiné à être utilisé dans une application maitresse. Dès lors, pour développer le package deco-api il est idéal d'en avoir une copie résidant dans l'application maitresse afin de pouvoir utiliser les outils de débogage conventionnels.
Pour cela, voici quelques conseils à suivre:
Importer le package via un fichier proxy
Créer dans l'application maitresse un fichier deco-api-proxy.ts et placer le contenu suivant avec le contenu suivant:
export * from 'deco-api';Ensuite, dans l'application, au lieu d'écrire import { Deco } from 'deco-api', l'astuce est d'écrire import { Deco } from './deco-api-proxy';. Cela aura pour effet que chaque importation passe par le fichier proxy au lieu de directement importer le paquet dépendant.
Une fois, ce proxy en place, on peut l'utiliser pour activer un mode debug lorsque c'est nécessaire.
Utiliser une version locale (copiée du package)
Tout d'abord, il convient de copier une version locale du package deco-api dans l'application maitresse. Supposons donc une application avec l'arborescence suivante:
/build
/app # contenant le code source non compilé de l'app
/app/deco-api-proxy.ts # le fichier proxy créé ci-dessus
/node_modules
/.gitignore
/package.jsonDepuis le dossier / de l'application lancez la commande suivante:
rm -rf ./app/deco-api # efface le dossier dans le cas où une copie antérieure y réside encore
cp -R ../deco-api/src ./app/deco-api
echo "*" >> ./app/deco-api/.gitignore # Important pour éviter que le dossier deco-api soit commité dans l'application principaleEn temps normal, le fichier deco-api-proxy.ts charge le package deco-api depuis sa version packagée dans node_modules. Mais lorsqu'on souhaite développer deco-api on peut changer le fichier deco-api-proxy.ts en:
export * from './deco-api'; // notez le "./" ajouté par rapport à la version originaleUne fois cette modification effectuée l'application utilisera la version locale du code deco-api et tous les outils de débogage standards pourront être utilisés.
Valider les changements dans le dossier package original
Il s'agit tout simplement de recopier le code modifié dans le package original:
rm ./app/deco-api/.gitignore
cp -R ./app/deco-api/* ../deco-api/src
rm -rf ./app/deco-apiStructure description
Au travers de l'usage de décorateurs javascript, deco-api facilite la mise en oeuvre d'une API Rest en NodeJS et express.
Le code est structuré de la façon suivante:
/decoratorscontient le code des décorateurs à proprement parler. On y trouvera par exemple le code de@model()dans le fichier./model.tsou les décorateurs de type comment@type.stringdans/types/basics.ts/helperscontient une série de codes utiles pour résoudre des fonctions spécifiques. On y trouvera notamment la gestion de la base de données (datastore.ts, le service pour les emails ou sms, un parser, un utilitaire de date ou encore un helper pour gérer les requêtesquery)/interfacescontient des fichiers d'interfaces facilitant le typing (typescript)/middlewarescontient une série de middlewares express destinés à être utilisés sur des routes./modulesregroupes différents modules fournis pardeco-api. On y trouvera le moduleappqui gérer les applications,userpour la gestion des utilisateurs,dicopour la gestion des traductions,dynamicpour la gestion des données dynamiques (configurable dynamiquement),memberspour la gestion des droits d'accès via liste de membres
Gestion des données
Un des objectifs de deco-api est de faciliter la création d'une API REST liée à des modèles définis en typescript avec des décorateurs. Cette mise en oeuvre se fait en 2 étapes:
- Créer un fichier de modèle pour définir la structure de données
- Créer un contrôleur pour définir les routes qui interagissent avec le modèle de données
La bonne pratique veut que les fichiers soient regroupés dans des modules par fonction. Supposons que l'on veuille créer un module pour gérer un shop on peut imaginer la structure suivante:
/app/shop/
/app/shop/customer.model.ts
/app/shop/customer.controller.ts
/app/shop/order.model.ts
/app/shop/order.controller.ts
/app/shop/product.model.ts
/app/shop/product.controller.tsCréer un modèle de données avec les décorateurs
Pour créer un model product, il faut commencer par créer un fichier de modèle de données, par exemple product.model.ts.
import { model, Model } from '../deco-api-proxy';
@model('shop_products') // 'shop_products' ici sera le nom de la collection mongo liée à ce modèle
export class ProductModel extends Model {
}Ensuite pour définir des propriétés dans le modèle de données on va donner des propriétés typescript à notre modèle et les décorer pour indiquer le type:
import { model, Model, type, io } from '../deco-api-proxy';
@model('shop_products') // 'shop_products' ici sera le nom de la collection mongo liée à ce modèle
export class ProductModel extends Model {
@type.string
@io.all
public name: string;
@type.string
@io.all
public description: string;
@type.select({options: ['red', 'blue']})
@io.all
public color: 'red' | 'blue';
@type.boolean
@io.all
public inStock: boolean;
@type.date
@io.all
public availableFrom: Date;
}Pour décorer une propriété, on a plusieurs catégories de décorateurs à notre disposition:
@type.sont les décorateurs de type. Les types de bases fournis pardeco-apisont:id,string,select,integer,float,date,boolean,array,files,geojson,increment-by-app,increment,metadata,model,models,object,random@io.sont les décorateurs qui définissent la façon dont la propriété est traitée dans le flux d'une requête. Les possibilités sont:@io.inputqui indique qu'il est possible de modifier la valeur de cette propriété via des requêtes POST ou PUT@io.outputqui indique que cette propriété est renvoyée par l'API lors de l'appel à ce modèle@io.toDocumentqui indique que cette propriété doit être persistante dans la base de données@io.allqui est un raccourci pour décrire les 3 décorateurs ci-dessus comme il est souvent souhaité d'avoir la combinaison des trois fonctions
@validate.pour indiquer des critères requis pour la validation de la donnée. Il s'agit de:@validate.required@validate.minLength(value: number)@validate.maxLength(value: number)@validate.email@validate.slugqui requiert une valeur n'utilisant que des caractères alphanumériques et sans accents@validate.uniqueByAppqui requiert une valeur unique pour ce modèle dans toute l'application
@query.qui regroupe des décorateurs affectant les requêtes sur le modèle. Par exemple:@query.searchableindique que la propriété est incluse lors de recherches sur ce modèle (requêtes avec&q=<value>dans le query string)@query.filterable({type: 'auto'})pour indiquer que cette propriété peut être utilisée dans des requêtes filtrées (ex:&name=<value>)dans le query string)@query.sortableindique que la propriété peut être utilisée pour effectuer un tri lors de requêtes (ex:&sort=namedans le query string)
@mongo.index()pour créer un index mongo sur cette propriété. Ce décorateur a plusieurs options (voir code source), l'usage le plus standard est:@mongo.index({type: 'single'})
Créer un controller pour définir des routes sur le modèle
Pour commencer nous allons créer un controller basic permettant de faire des requêtes GET, POST, PUT et DELETE sur notre modèle. Créons un fichier pour le contrôleur, par exemple product.controller.ts:
import { ProductModel } from './product.model';
import { AppMiddleware, AuthMiddleware, ControllerMiddleware } from '../deco-api-proxy';
import { Router, Request, Response, NextFunction } from 'express';
const router: Router = Router();
let controller = new ControllerMiddleware(ProductModel);
// This first block create a GET / route that return *all* elements in this model
// the AppMiddleware.fetchWithPublicKey is a middleware that forces the request to filter elements in the current app (via the apiKey)
// controller.prepareQueryFromReq() applies all the filter, search and sort
// addCountInKey is an optional option to return the total amount of items (without search/filter) in a property (__count) in case one want to build pagination
router.get(
ControllerMiddleware.getAllRoute(),
AppMiddleware.fetchWithPublicKey,
controller.prepareQueryFromReq(),
controller.getAll(null, {addCountInKey: '__count'})
);
// This block create a GET /:elementId route to fetch a specific element
// The AuthMiddleware.authenticateWithoutError allow the route to try to
// authenticate the user (then it is available in res.locals.user)
// but if it fails to authenticate it's OK, the user is then not available
// in res.locals.user but the route can still be reached
router.get(
ControllerMiddleware.getOneRoute(),
AppMiddleware.fetchWithPublicKey,
AuthMiddleware.authenticateWithoutError,
controller.getOne()
);
// This block create a POST / route to create a new element
// of this model
// The AuthMiddleware.authenticate requires a valid authenticated user
// to reach this route
router.post(
ControllerMiddleware.postRoute(),
AppMiddleware.fetchWithPublicKey,
AuthMiddleware.authenticate,
controller.post()
);
// This block create a PUT /:elementId route to edit an existing element
// The AuthMiddleware.authenticate requires a valid authenticated user
// to reach this route
router.put(
ControllerMiddleware.putRoute(),
AppMiddleware.fetchWithPublicKey,
AuthMiddleware.authenticate,
controller.put()
);
// This block create a PUT /:elementId route to edit an existing element
// The AuthMiddleware.authenticate requires a valid authenticated user
// to reach this route
router.delete(
ControllerMiddleware.deleteRoute(),
AppMiddleware.fetchWithPublicKey,
AuthMiddleware.authenticate,
controller.delete()
);
export const ProductController: Router = router;Une fois que l'on a un contrôleur prêt pour notre modèle, il est temps de l'inclure dans l'application. Pour cela on va utiliser le contrôleur dans l'instance de l'application express. La bonne pratique est d'avoir une variable app qui contient l'app et d'écrire:
import { ProductController } from './products/product.controller';
// omitted code created and serving the application in app
app.use('/product', ProductController);2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago