npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

waiter-api

v0.1.1

Published

WAITER is a fast, secure and simple TypeScript API library.

Readme

WAITER

WAITER e uma biblioteca TypeScript para criar APIs de forma rapida, segura, simples e legivel.

Ela foi pensada para duas pessoas ao mesmo tempo:

  • quem esta aprendendo o que e uma API e como ela funciona
  • quem ja sabe construir sistemas e quer uma base limpa para criar rapido sem perder qualidade

O objetivo e ser uma biblioteca que voce consiga entender em poucos minutos, usar em poucas linhas e levar para um projeto serio sem sentir que precisou trocar tudo depois.

Filosofia

WAITER tenta seguir 4 ideias simples:

  • ser facil de ler
  • ser facil de aprender
  • ser segura por padrao
  • continuar pequena o suficiente para nao virar uma biblioteca pesada

Isso significa que a API foi desenhada para parecer natural. Voce escreve o que quer que aconteca e a biblioteca cuida do resto: rota, entrada, resposta, erro, seguranca e organizacao.

O que e uma API, em palavras simples

Se voce nunca viu uma API, pense assim:

  • o cliente faz uma pergunta para o servidor
  • a pergunta chega como uma requisicao
  • o servidor decide qual regra executar
  • a regra processa os dados
  • o servidor devolve uma resposta

WAITER organiza exatamente esse caminho.

Instalacao

npm install waiter-api

Se voce estiver trabalhando dentro do proprio repositorio, instale as dependencias do projeto com:

npm install

Primeiro exemplo

Este e o exemplo mais simples possivel.

import { createWaiter } from "waiter-api";

const app = createWaiter();

app.get("/hello/:name", ({ params }) => {
  return {
    message: `Hello, ${params.name}!`
  };
});

const response = await app.handle(new Request("http://localhost/hello/waiter"));
console.log(await response.json());

O que aconteceu aqui

  • createWaiter() cria a aplicacao
  • app.get(...) registra uma rota que responde a requisicoes GET
  • "/hello/:name" define uma rota com parametro dinamico
  • params.name pega o valor da URL
  • app.handle(request) executa a aplicacao contra uma requisicao
  • o retorno vira uma Response pronta para ser lida como JSON

Como ler a WAITER mentalmente

Pense na WAITER como 5 pecas:

  • configuracao global
  • rotas
  • middleware
  • contexto da requisicao
  • resposta e erro

Voce nao precisa entender tudo de uma vez. O normal e comecar por rotas, depois middleware, depois validacao e auth.

createWaiter

Essa e a porta de entrada da biblioteca.

const app = createWaiter({
  notFoundMessage: "Rota nao encontrada",
  maxBodySizeBytes: 1024 * 1024
});

Por que existe

Porque quase toda API precisa de algumas regras globais:

  • mensagem padrao quando a rota nao existe
  • limite maximo do corpo da requisicao
  • headers de seguranca padrao

Configuracao disponivel

  • notFoundMessage: personaliza a resposta de rota nao encontrada
  • maxBodySizeBytes: limita o tamanho do corpo da requisicao
  • securityHeaders: liga, desliga ou personaliza headers de seguranca
  • errorHandler: reservado para fluxos avancados de tratamento de erro

Rotas

Rotas sao os caminhos que sua API entende.

app.get("/users", () => {
  return { ok: true };
});

app.post("/users", () => {
  return { created: true };
});

app.put("/users/:id", ({ params }) => {
  return { updated: params.id };
});

Metodos disponiveis

  • get
  • post
  • put
  • patch
  • delete
  • options
  • head
  • on para registrar qualquer metodo manualmente

Por que isso existe

Porque uma API fala por verbos HTTP:

  • GET para ler
  • POST para criar
  • PUT e PATCH para atualizar
  • DELETE para remover

WAITER deixa isso direto e legivel.

Parametros de rota

Parametros servem para capturar partes variaveis da URL.

app.get("/users/:id", ({ params }) => {
  return {
    userId: params.id
  };
});

Se a requisicao for GET /users/42, entao params.id vira 42.

Por que isso existe

Porque muitas APIs trabalham com recursos identificados por um valor dinamico:

  • usuario por id
  • produto por slug
  • pedido por numero

Contexto da requisicao

Toda rota recebe um contexto. Ele e o objeto que carrega tudo que a sua regra precisa saber.

app.get("/debug", ({ request, url, params, query }) => {
  return {
    method: request.method,
    path: url.pathname,
    params,
    query: Object.fromEntries(query.entries())
  };
});

Campos principais

  • request: a requisicao original
  • url: a URL analisada
  • params: parametros da rota
  • query: query string da URL
  • auth: estado da autenticacao
  • validation: dados validados por middleware
  • state: espaco livre para guardar dados da requisicao
  • text(): le o corpo como texto
  • json(): le o corpo como JSON
  • formData(): le o corpo como formulario
  • respond(): converte um valor em Response

Por que isso existe

Porque a sua rota nao deve precisar reinventar tudo sempre. O contexto centraliza o que ja foi coletado pela biblioteca.

Respostas

A WAITER aceita varios formatos de retorno.

app.get("/plain", () => {
  return "hello";
});

app.get("/json", () => {
  return { message: "hello" };
});

app.get("/raw", () => {
  return new Response("raw response");
});

Como a biblioteca interpreta isso

  • string vira texto puro
  • objeto, numero e boolean viram JSON
  • Response passa direto
  • null e undefined viram resposta sem corpo

Por que isso existe

Porque o retorno de uma rota deve ser simples de escrever. O desenvolvedor nao deveria precisar montar uma Response toda hora.

Middleware

Middleware e um passo que roda antes da rota final.

app.use(async (_context, next) => {
  console.log("antes");
  const result = await next();
  console.log("depois");
  return result;
});

Para que serve

Middleware e muito util para coisas que precisam acontecer em varias rotas:

  • log
  • autenticacao
  • validacao
  • rastreamento
  • tempo de resposta

Ordem de execucao

  1. a requisicao entra
  2. os middlewares rodam na ordem em que foram registrados
  3. a rota executa
  4. a resposta volta

Regra importante

next() so deve ser chamado uma vez dentro do mesmo middleware.

Validacao de entrada

Validacao serve para garantir que os dados recebidos estao no formato esperado.

WAITER usa Zod para isso.

import { z } from "zod";
import { createValidationMiddleware, createWaiter } from "waiter-api";

const app = createWaiter();

app.use(
  createValidationMiddleware({
    body: z.object({
      name: z.string().min(3)
    })
  })
);

app.post("/users", ({ validation }) => {
  const body = validation.body as { name: string };

  return {
    created: true,
    name: body.name
  };
});

O que isso faz

  • le o corpo da requisicao
  • valida com o schema
  • guarda o resultado validado em validation.body
  • se falhar, responde com erro 400 e VALIDATION_ERROR

Por que isso existe

Porque dados invalidos sao uma das maiores fontes de bugs e inseguranca em API.

Boas praticas de validacao

  • valide sempre dados externos
  • use schemas pequenos e claros
  • defina mensagens e regras objetivas
  • trate entrada como nao confiavel ate provar o contrario

Autenticacao Bearer

Bearer auth e um jeito simples de proteger rotas usando um token no header authorization.

import { createBearerAuth, createWaiter } from "waiter-api";

const app = createWaiter();

app.use(
  createBearerAuth({
    tokens: ["demo-token"]
  })
);

app.get("/secure", ({ auth }) => {
  return {
    authenticated: auth.isAuthenticated,
    token: auth.token
  };
});

Como usar

Envie o header:

Authorization: Bearer demo-token

O que acontece

  • a WAITER le o header
  • verifica se o esquema e Bearer
  • valida se o token esta na lista permitida
  • marca auth.isAuthenticated = true
  • libera o acesso para a rota

Por que isso existe

Porque muitas rotas nao podem ser publicas.

Boas praticas de auth

  • nunca guarde token real no codigo fonte
  • use variaveis de ambiente
  • valide permissao alem de autenticacao quando necessario
  • trate rota protegida como rota sensivel

HttpError

HttpError e a forma padrao de sinalizar erros conhecidos.

import { HttpError } from "waiter-api";

app.get("/admin", () => {
  throw new HttpError("Forbidden", {
    status: 403,
    code: "FORBIDDEN"
  });
});

Por que usar

Porque nem todo erro e inesperado. Muitas vezes voce quer dizer claramente:

  • nao autorizado
  • proibido
  • recurso nao encontrado
  • validacao falhou

Formato da resposta

WAITER devolve algo assim:

{
  "error": {
    "message": "Forbidden",
    "code": "FORBIDDEN",
    "details": null
  }
}

Boas praticas de erro

  • use HttpError para erros esperados
  • deixe erros inesperados virarem 500
  • seja claro na mensagem
  • nao exponha segredo interno em mensagens de erro

Seguranca padrao

WAITER adiciona headers de seguranca nas respostas por padrao.

Headers incluidos por default:

  • x-content-type-options
  • x-frame-options
  • referrer-policy
  • permissions-policy
  • cross-origin-opener-policy
  • cross-origin-resource-policy

Por que isso existe

Porque varias protecoes basicas nao devem depender da lembranca do desenvolvedor.

Como personalizar

const app = createWaiter({
  securityHeaders: {
    "content-security-policy": "default-src 'none'"
  }
});

Como desativar

const app = createWaiter({
  securityHeaders: false
});

Limite de corpo da requisicao

Voce pode limitar o tamanho do corpo que a API aceita.

const app = createWaiter({
  maxBodySizeBytes: 1024 * 1024
});

Por que isso existe

Porque corpo de requisicao muito grande pode causar:

  • consumo excessivo de memoria
  • lentidao
  • abuso de entrada

Boa pratica

Defina limites coerentes com o tipo de endpoint:

  • formulario simples: limite pequeno
  • upload ou payload grande: limite especifico e consciente

listen

Se voce quiser rodar a biblioteca como servidor HTTP de verdade, use listen.

const app = createWaiter();

app.get("/health", () => ({ ok: true }));

await app.listen(3000);

Por que isso existe

Porque a biblioteca nao serve apenas para testes ou frameworks externos. Ela tambem consegue iniciar o servidor HTTP diretamente.

Fluxo recomendado para iniciantes

Se voce estiver comecando agora, siga esta ordem:

  1. crie a aplicacao com createWaiter()
  2. registre uma rota get
  3. retorne um objeto simples
  4. use params para rotas dinamicas
  5. adicione middleware
  6. adicione validacao
  7. depois adicione auth

Exemplo completo e simples

import { z } from "zod";
import {
  HttpError,
  createBearerAuth,
  createValidationMiddleware,
  createWaiter
} from "waiter-api";

const app = createWaiter({
  notFoundMessage: "Rota nao existe",
  maxBodySizeBytes: 1024 * 1024
});

app.use(createBearerAuth({ tokens: ["demo-token"] }));
app.use(
  createValidationMiddleware({
    body: z.object({
      name: z.string().min(3)
    })
  })
);

app.post("/users", ({ auth, validation }) => {
  if (!auth.isAuthenticated) {
    throw new HttpError("Unauthorized", {
      status: 401,
      code: "UNAUTHORIZED"
    });
  }

  const body = validation.body as { name: string };

  return {
    created: true,
    name: body.name,
    token: auth.token
  };
});

Boas praticas gerais

  • mantenha as rotas pequenas
  • valide tudo que vier de fora
  • use nomes claros para rotas e variaveis
  • prefira erros tipados quando o problema for esperado
  • separe auth, validacao e regra de negocio
  • trate request como dado nao confiavel
  • use state apenas quando precisar compartilhar informacao entre middlewares e rota

Para quem ja tem experiencia

Se voce ja programa APIs, a ideia da WAITER e te dar uma superficie pequena e previsivel.

Voce nao precisa abrir dez camadas para fazer uma rota simples, mas ainda pode estruturar a aplicacao com:

  • middlewares reutilizaveis
  • validacao padronizada
  • auth centralizada
  • erros consistentes
  • defaults seguros

Scripts do projeto

  • npm run build compila a biblioteca
  • npm run test executa a suite de testes
  • npm run lint valida o estilo e os tipos suportados pelo ESLint
  • npm run typecheck verifica tipos sem gerar saida

O que vem depois

Se quiser evoluir a WAITER, os proximos passos naturais sao:

  • tipagem mais forte para validation.body por rota
  • sistema de roles e permissao
  • rate limit
  • logs estruturados
  • plugins
  • geracao de documentacao automatica

Resumo curto

WAITER existe para ser uma biblioteca de API que voce entende rapido, escreve rapido e consegue levar para producao sem parecer um brinquedo.