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

@rafaelhdsv/keep-alive

v1.0.2

Published

Self-ping periódico para Web Services Node que hibernam por inatividade (Render, Railway, Fly.io e similares)

Readme

@rafaelhdsv/keep-alive

Self-ping periódico para Web Services Node com processo long-lived que hibernam por inatividade (Render Free, Railway, Fly.io e similares). Zero dependências de runtime — usa fetch nativo do Node 18+.


Para quem é (e para quem não é)

| Cenário | Este pacote ajuda? | |---------|------------------| | API Express/Node em PaaS com idle (~15 min sem tráfego) | Sim | | App já no ar e você quer evitar hibernação enquanto o processo roda | Sim | | Static Site, serverless sem processo persistente | Não — use ping externo (UptimeRobot, cron) | | Acordar instância já hibernada sem tráfego externo | Não — self-ping só funciona com processo ativo | | Substituir plano pago / SLA garantido | Não — é workaround para idle, não produto de disponibilidade |


Como funciona (em 30 segundos)

Depois de app.listen, o pacote faz um GET periódico na rota de health do próprio app, usando a URL pública configurada. Isso gera tráfego HTTP interno e mantém a instância dentro da janela de atividade do provedor.

app.listen() → startKeepAlive()
                 ├─ resolve URL pública (env)
                 ├─ sem URL? → no-op (dev local)
                 └─ a cada N min: GET https://seu-app/.../api/health

Log esperado em produção:

[keep-alive] ativo: https://seu-app.onrender.com/api/health a cada 10 min

Qual opção escolher?

Há duas formas de adotar o pacote. A escolha depende do seu projeto, não da “qualidade” da solução — ambas usam a mesma lib por baixo.

Comparativo rápido

| Critério | Opção A — CLI init | Opção B — Manual | |----------|--------------------------|----------------------| | Framework | Express | Qualquer (Express, Fastify, Hono, etc.) | | Tempo de adoção | ~1 comando | ~5 minutos copiando código | | Controle do diff | Automático (edita arquivos) | Total — você decide onde injetar | | Rota health | Cria se não existir no path | Você garante a rota | | .env.example | Atualiza com vars comentadas | Você documenta as vars | | Rodar de novo | Idempotente (não duplica) | N/A | | Quando preferir | Projeto novo ou Express padrão | Framework diferente, health customizada, ou diff mínimo revisado à mão |

Escolha a Opção A (init) se…

  • O servidor é Express com entry em server/index.ts (ou script start apontando para um .ts/.js claro).
  • Você quer adoção rápida sem editar vários arquivos.
  • Ainda não tem rota de health leve — ou aceita que o CLI crie uma mínima em /api/health.
  • Está começando um projeto ou pode revisar o diff do init num PR.

Escolha a Opção B (manual) se…

  • Usa Fastify, Hono ou outro framework (CLI v1 não suporta).
  • Já tem rota /api/health com lógica sensível (ex.: checagem de banco) e quer manter ou refatorar sem o CLI tocar no arquivo.
  • Prefere controle explícito do import e do momento em que startKeepAlive() é chamado.
  • O entry não segue o padrão server/index.ts e você não quer forçar --entry.

Self-ping interno vs ping externo

| Abordagem | Por que escolher | Limitação | |-----------|------------------|-----------| | Este pacote (self-ping) | Sem UptimeRobot, sem cron, sem GitHub Actions; zero serviço extra; mesmo deploy | Só age enquanto o processo Node está no ar | | UptimeRobot / cron externo | Pode acordar instância já dormindo | Conta/config extra, latência, mais um ponto de falha | | GitHub Actions schedule | Gratuito em repos públicos | Workflow redundante se o app já está rodando; não substitui health do painel do provedor | | Combinar self-ping + health do provedor | Redundância razoável em Render | Ainda não é SLA; documente limitações do plano Free |

Recomendação prática: use este pacote como camada principal em Web Services Node; configure também o Health Check Path no painel do provedor (Render, Railway, etc.) quando disponível.


Opção A — CLI init (Express)

1. Executar

Na raiz do projeto Express:

npx @rafaelhdsv/keep-alive init

Com opções explícitas (útil quando o entry não é o default):

npx @rafaelhdsv/keep-alive init --path /api/health --entry server/index.ts
npx @rafaelhdsv/keep-alive init --help

| Flag | Default | Quando mudar | |------|---------|--------------| | --path | /api/health | Sua API já usa /health, /healthz ou path do painel do Render | | --entry | server/index.ts ou script start | Monorepo, src/server.ts, api/index.ts, etc. |

2. O que o comando altera

  1. package.json — adiciona "@rafaelhdsv/keep-alive" em dependencies (se ausente).

  2. Entry do servidor — insere import { startKeepAlive } from '@rafaelhdsv/keep-alive' e chama startKeepAlive() dentro do callback de app.listen (após o servidor estar ouvindo).

  3. Rota health — se não existir app.get('<path>', ...) (ou equivalente) no entry, injeta:

    app.get('/api/health', (_req, res) => {
      res.status(200).json({ ok: true })
    })
  4. .env.example — acrescenta variáveis comentadas (KEEP_ALIVE_*).

3. Exemplo — antes e depois

Antes (server/index.ts):

app.listen(PORT, () => {
  console.log(`Server on ${PORT}`)
})

Depois (init):

import { startKeepAlive } from '@rafaelhdsv/keep-alive'

app.get('/api/health', (_req, res) => {
  res.status(200).json({ ok: true })
})

app.listen(PORT, () => {
  console.log(`Server on ${PORT}`)
  startKeepAlive()
})

4. Segunda execução (idempotência)

npx @rafaelhdsv/keep-alive init
# [keep-alive] Nada a alterar — projeto já configurado.

Pode rodar após merge, em CI de scaffold ou ao atualizar o template — não duplica import nem rota.

5. Depois do init — checklist

yarn install          # ou npm install — instala a nova dependência
yarn dev              # local: sem URL pública → no-op (sem log de ativo)

Em produção:

  • [ ] Deploy com variáveis corretas (ver Variáveis de ambiente)
  • [ ] Painel do provedor: Health Check Path = mesmo path do --path (ex.: /api/health)
  • [ ] Logs com [keep-alive] ativo: ...
  • [ ] GET /api/health responde 200 sem consultar banco na rota do ping

6. Erros comuns do init

| Mensagem / situação | Causa | O que fazer | |---------------------|-------|-------------| | Entry não encontrado | Sem server/index.ts nem start legível | init --entry caminho/para/seu/server.ts | | Estrutura não-Express | Fastify/Hono/outro | Use Opção B — manual | | Rota health “pesada” já existe | CLI não substitui rota existente | Refatore manualmente ou use path dedicado (--path /ping) |


Opção B — Uso manual

1. Instalar

yarn add @rafaelhdsv/keep-alive
# ou: npm install @rafaelhdsv/keep-alive

2. Chamar após listen

O ping só deve começar quando o servidor já está aceitando conexões:

import express from 'express'
import { startKeepAlive } from '@rafaelhdsv/keep-alive'

const app = express()
const PORT = Number(process.env.PORT ?? 3000)

// Rota leve — sem banco, sem filas, sem I/O pesado
app.get('/api/health', (_req, res) => {
  res.status(200).json({ ok: true })
})

app.listen(PORT, () => {
  console.log(`Server on port ${PORT}`)
  startKeepAlive()
})

Por que no callback de listen? O self-ping faz HTTP para a URL pública do app; se o servidor ainda não subiu, o primeiro tick falharia sem necessidade.

3. Opções em código vs env

| Configuração | Onde definir | Quando usar | |--------------|--------------|-------------| | URL base | KEEP_ALIVE_URL (env) | Produção — URL pública HTTPS do app | | Path | KEEP_ALIVE_PATH (env) | Path padrão do time (/health, /api/health) | | Path | startKeepAlive({ path: '...' }) | Teste local, multi-tenant, ou override pontual | | Intervalo | KEEP_ALIVE_INTERVAL_MIN (env) | Ajuste fino por ambiente | | Intervalo | startKeepAlive({ intervalMin: 8 }) | Provedor com idle < 15 min |

Precedência: argumentos de startKeepAlive() sobrescrevem env para url, path e intervalMin.

startKeepAlive({
  path: '/api/health',
  intervalMin: 10,
  // url: 'https://...'  // raro; prefira KEEP_ALIVE_URL em produção
})

4. Fastify

import Fastify from 'fastify'
import { startKeepAlive } from '@rafaelhdsv/keep-alive'

const app = Fastify()

app.get('/api/health', async () => ({ ok: true }))

await app.listen({ port: 3000, host: '0.0.0.0' })
startKeepAlive({ path: '/api/health' })

5. Hono (Node adapter)

import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { startKeepAlive } from '@rafaelhdsv/keep-alive'

const app = new Hono()
app.get('/api/health', (c) => c.json({ ok: true }))

serve({ fetch: app.fetch, port: 3000 }, () => {
  startKeepAlive({ path: '/api/health' })
})

Variáveis de ambiente

Variáveis do pacote (as que você configura)

| Variável | Default | Descrição | |----------|---------|-----------| | KEEP_ALIVE_URL | — | URL base pública HTTPS do app — use em qualquer provedor (Railway, Fly.io, VPS, staging, etc.) | | KEEP_ALIVE_PATH | /api/health | Caminho do ping (sem query string) | | KEEP_ALIVE_INTERVAL_MIN | 10 | Intervalo em minutos entre pings |

Em dev local: não defina nada → sem KEEP_ALIVE_URLno-op silencioso (sem log [keep-alive] ativo).

Qual variável usar?

| Situação | O que fazer | |----------|-------------| | Produção (qualquer provedor) | KEEP_ALIVE_URL=https://seu-dominio-publico | | Dev local | Nada |

Intervalo — como escolher

| Idle do provedor | KEEP_ALIVE_INTERVAL_MIN sugerido | |------------------|-------------------------------------| | ~15 min (Render Free) | 10 (default) — margem segura | | ~5 min (alguns PaaS) | 3 ou 4 | | Muito agressivo (< 5 min) | Avalie plano pago ou ping externo |

Regra: intervalo < janela de idle do provedor.

Exemplo .env (Railway / genérico)

KEEP_ALIVE_URL=https://meu-app.up.railway.app
KEEP_ALIVE_PATH=/api/health
KEEP_ALIVE_INTERVAL_MIN=10

Fluxo completo — deploy (exemplo)

  1. Adotar o pacote

    npx @rafaelhdsv/keep-alive init
    yarn install
  2. Variáveis no painel do provedor

    • KEEP_ALIVE_URL = URL pública HTTPS do app (ex.: https://meu-app.exemplo.com)
    • Health Check Path (se o provedor oferecer) = mesmo path do --path (ex.: /api/health)
  3. Deploy

    • Nos logs: [keep-alive] ativo: https://meu-app.exemplo.com/api/health a cada 10 min
  4. Validar

    curl -s https://meu-app.exemplo.com/api/health
    # {"ok":true}

Boas práticas — rota de health

A rota usada pelo keep-alive deve ser a mais leve possível:

| Faça | Evite | |------|-------| | res.json({ ok: true }) imediato | SELECT 1 ou ping em Mongo/Postgres | | Path dedicado (/api/health) | Reutilizar rota que carrega sessão/auth pesada | | ready em rota separada (/api/ready) se precisar de DB | Bloquear o ping em migração de schema |

Projetos existentes: se /api/health já consulta banco, o init não altera essa rota. Opções:

  1. Refatorar health para ser leve no ping e mover checagens para /api/ready.
  2. Criar path novo: init --path /ping e configurar o keep-alive nesse path.

Limitações (leia antes de depender em produção)

  • Self-ping não acorda instância já hibernada. Processo morto ou instância dormindo só voltam com tráfego externo (usuário, health do provedor, UptimeRobot).
  • Workaround para idle, não substituto de plano pago nem garantia de SLA.
  • Políticas de provedores podem mudar — acompanhe changelogs (ex.: Render Free).
  • CLI init v1: apenas Express. Demais frameworks: lib manual.

API pública

import {
  startKeepAlive,
  resolveBaseUrl,
  resolveKeepAliveConfig,
  type StartKeepAliveOptions,
} from '@rafaelhdsv/keep-alive'

// Uso típico — lê env
startKeepAlive()

// Override explícito (testes ou casos especiais)
startKeepAlive({
  url: 'https://example.com',
  path: '/api/health',
  intervalMin: 10,
})

// Inspeção sem iniciar ping
resolveBaseUrl()           // string | null
resolveKeepAliveConfig()   // { baseUrl, healthUrl, intervalMin } | null

Comportamento em falha de rede: console.warnnão derruba o processo. Timeout do fetch: 90s (tolera cold start pós-deploy).


Solução de problemas

| Sintoma | Provável causa | Ação | |---------|----------------|------| | Sem log [keep-alive] ativo em produção | KEEP_ALIVE_URL ausente ou incorreta | Defina a URL pública HTTPS do app | | Log aparece mas app ainda dorme | Intervalo ≥ idle do provedor | Reduza KEEP_ALIVE_INTERVAL_MIN | | HTTP 4xx/5xx nos warns | Path errado ou health pesada/falhando | Confira KEEP_ALIVE_PATH e resposta da rota | | Funciona local, não em prod | URL interna ou HTTP em vez de HTTPS | Use URL pública HTTPS em KEEP_ALIVE_URL | | init não acha o entry | Estrutura fora do padrão | --entry ou adoção manual |


Desenvolvimento do pacote

yarn
yarn build
yarn test

Requer Node ≥ 18 (recomendado: 22, conforme .nvmrc).

Licença

MIT © 2026 Rafael Vieira — veja LICENSE.