Primeira aplicação
Neste guia você constrói uma API pequena que lista livros. Ela serve para ver as três peças com que se monta qualquer aplicação do viga: serviços, controllers e módulos. Antes, siga os passos da instalação.
Estrutura do projeto
Section titled “Estrutura do projeto”Ao terminar, o projeto terá estes arquivos:
src/ books/ books.service.ts books.controller.ts books.module.ts app.module.ts main.tsCada funcionalidade fica na sua própria pasta, com o seu módulo. app.module.ts reúne todas e main.ts inicia a aplicação.
O serviço
Section titled “O serviço”Um serviço contém a lógica da aplicação. Aqui ele devolve uma lista fixa de livros. Numa aplicação real, consultaria o banco de dados.
import { Injectable } from 'viga';
export interface Book { id: number; title: string;}
@Injectable()export class BooksService { private readonly books: Book[] = [ { id: 1, title: 'Dune' }, { id: 2, title: 'Fundação' }, ];
findAll() { return this.books; }}@Injectable() permite que o viga crie o serviço e o entregue a quem pedir.
O controller
Section titled “O controller”Um controller recebe as requisições HTTP e responde. Ele não contém lógica: pede ao serviço.
import { Controller, Get, Public, inject } from 'viga';import { BooksService } from './books.service.ts';
@Controller('/books')export class BooksController { private readonly books = inject(BooksService);
@Public() @Get() list() { return this.books.findAll(); }}@Controller('/books')define o prefixo de todas as rotas do controller.@Get()responde aGET /books. O que o método devolve é enviado como JSON e, se for uma promise, o viga espera ela ser resolvida.inject(BooksService)pede o serviço. O viga o cria uma vez e o compartilha.@Public()abre a rota. Sem ele, ela seria privada.
O módulo
Section titled “O módulo”Um módulo agrupa os controllers e serviços de uma funcionalidade.
import { Module } from 'viga';import { BooksController } from './books.controller.ts';import { BooksService } from './books.service.ts';
@Module({ controllers: [BooksController], providers: [BooksService],})export class BooksModule {}Os serviços vão em providers. Só os controllers e serviços deste módulo podem pedi-los, a menos que o módulo os exporte.
O módulo raiz
Section titled “O módulo raiz”O módulo raiz reúne os módulos da aplicação:
import { Module } from 'viga';import { BooksModule } from './books/books.module.ts';
@Module({ imports: [BooksModule] })export class AppModule {}Inicie a aplicação
Section titled “Inicie a aplicação”import { createApp } from 'viga';import { AppModule } from './app.module.ts';
const app = createApp({ rootModule: AppModule, globalPrefix: '/api' }).enableShutdownHooks();
await app.listen();globalPrefixcoloca/apina frente de todas as rotas.enableShutdownHooks()encerra a aplicação de forma ordenada quando o processo para, por exemplo comCtrl+C.listen()começa a escutar na porta 3000. A porta é opcional:listen(8080)usa outra. O viga não lê a variável de ambientePORTpor conta própria; se você a usa, passe-a você mesmo:app.listen(Number(process.env.PORT ?? 3000)).
Inicie em modo de desenvolvimento:
pnpm devTeste a API
Section titled “Teste a API”curl http://localhost:3000/api/books[{ "id": 1, "title": "Dune" }, { "id": 2, "title": "Fundação" }]As rotas são privadas por padrão
Section titled “As rotas são privadas por padrão”Remova @Public() do controller e teste de novo. A rota responde 401:
{ "statusCode": 401, "code": "errors.unauthorized", "message": "You must sign in to continue", "error": "Unauthorized"}Além disso, ao iniciar, a aplicação avisa quantas rotas privadas não têm como autenticar:
[App] No auth guard is configured, so the 1 private routes will respond 401; add app.useAuthGuard() or mark public routes with @Public()No viga, uma rota é aberta de propósito, nunca por esquecimento. Para as rotas que exigem sessão, você configura a sua própria autenticação com app.useAuthGuard(), como verá na seção do núcleo.
Próximos passos
Section titled “Próximos passos”Agora você tem uma aplicação com um módulo, um controller e um serviço. O próximo passo é separar o acesso a dados num repositório, para mudar de onde vêm os dados sem mexer no serviço e substituí-los nos testes. Depois, você pode validar os dados que chegam, organizar mais módulos e conectar um banco de dados, que é o que as próximas seções cobrem.